org.opennaas.core.protocols.sessionmanager.ProtocolSessionManager.java Source code

Java tutorial

Introduction

Here is the source code for org.opennaas.core.protocols.sessionmanager.ProtocolSessionManager.java

Source

package org.opennaas.core.protocols.sessionmanager;

/*
 * #%L
 * OpenNaaS :: Core :: Resources
 * %%
 * Copyright (C) 2007 - 2014 Fundaci Privada i2CAT, Internet i Innovaci a Catalunya
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
 * #L%
 */

import java.io.IOException;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opennaas.core.events.EventFilter;
import org.opennaas.core.events.IEventManager;
import org.opennaas.core.resources.Activator;
import org.opennaas.core.resources.ActivatorException;
import org.opennaas.core.resources.IResource;
import org.opennaas.core.resources.ResourceException;
import org.opennaas.core.resources.alarms.CapabilityAlarm;
import org.opennaas.core.resources.alarms.SessionAlarm;
import org.opennaas.core.resources.configurationadmin.ConfigurationAdminUtil;
import org.opennaas.core.resources.protocol.IProtocolMessageFilter;
import org.opennaas.core.resources.protocol.IProtocolSession;
import org.opennaas.core.resources.protocol.IProtocolSession.Status;
import org.opennaas.core.resources.protocol.IProtocolSessionFactory;
import org.opennaas.core.resources.protocol.IProtocolSessionListener;
import org.opennaas.core.resources.protocol.IProtocolSessionManager;
import org.opennaas.core.resources.protocol.ProtocolException;
import org.opennaas.core.resources.protocol.ProtocolSessionContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;

public class ProtocolSessionManager
        implements IProtocolSessionManager, IProtocolSessionListener, IProtocolMessageFilter, EventHandler {

    private String resourceID = null;
    /* key: sessionId, value: pooled session */
    private Map<String, ProtocolPooled> liveSessions = null;
    /* key: sessionId, value: context */
    private Map<String, ProtocolSessionContext> liveSessionContexts = null;
    /* key: protocol, value: context */
    private Map<String, ProtocolSessionContext> registeredContexts = null;
    private List<String> lockedProtocolSessions = null;
    private ProtocolManager protocolManager = null;
    private IEventManager eventManager;

    Log log = LogFactory.getLog(ProtocolSessionManager.class);

    /**
     * key: sessionId value: registration number
     */
    private Map<String, Integer> sessionEventsListenerRegistrationNumbers = new HashMap<String, Integer>();

    private ServiceRegistration registration;

    // TODO get this from the configuration
    private static long expirationTime = 3000 * 1000; // milis

    class ProtocolPooled {
        private long lastUsed;
        private IProtocolSession session;

        private Lock lock;

        public ProtocolPooled(IProtocolSession protocolSession, long time) {
            this.lastUsed = time;
            this.session = protocolSession;

            lock = new java.util.concurrent.locks.ReentrantLock();
        }

        public void setProtocolSession(IProtocolSession session) {
            this.session = session;
        }

        public IProtocolSession getProtocolSession() {
            return session;
        }

        public void setLastUsed(long time) {
            this.lastUsed = time;
        }

        public long getLastUsed() {
            return lastUsed;
        }

        public Lock getLock() {
            return lock;
        }

    }

    public ProtocolSessionManager(String deviceID) {
        this.resourceID = deviceID;
        this.liveSessions = new HashMap<String, ProtocolPooled>();
        this.liveSessionContexts = new HashMap<String, ProtocolSessionContext>();
        this.registeredContexts = new HashMap<String, ProtocolSessionContext>();
        this.lockedProtocolSessions = new ArrayList<String>();
    }

    public void setProtocolManager(ProtocolManager protocolManager) {
        this.protocolManager = protocolManager;
    }

    @Override
    public String getResourceID() {
        return resourceID;
    }

    @Override
    public SessionIdList getAllProtocolSessionIdsWS() {
        SessionIdList resp = new SessionIdList();
        resp.setSessionsIds(new ArrayList<String>(getAllProtocolSessionIds()));
        return resp;
    }

    @Override
    public Set<String> getAllProtocolSessionIds() {
        return liveSessions.keySet();
    }

    @Override
    public List<ProtocolSessionContext> getRegisteredContexts() {
        List<ProtocolSessionContext> contexts = new ArrayList<ProtocolSessionContext>();
        for (ProtocolSessionContext context : getRegisteredProtocolSessionContexts().values()) {
            contexts.add(context);
        }
        return contexts;
    }

    public Map<String, ProtocolSessionContext> getRegisteredProtocolSessionContexts() {
        return registeredContexts;
    }

    private synchronized IProtocolSession createProtocolSession(ProtocolSessionContext protocolSessionContext)
            throws ProtocolException {

        long now = System.currentTimeMillis();

        String protocol = (String) protocolSessionContext.getSessionParameters()
                .get(ProtocolSessionContext.PROTOCOL);
        IProtocolSessionFactory protocolFactory = protocolManager.getSessionFactory(protocol);
        String sessionID = UUID.randomUUID().toString();
        IProtocolSession protocolSession = protocolFactory.createProtocolSession(sessionID, protocolSessionContext);

        // Don't trust Factory implementations to do this.
        protocolSession.setSessionId(sessionID);
        protocolSession.setSessionContext(protocolSessionContext);

        /* Activate listener */
        protocolSession.registerProtocolSessionListener(this, this, sessionID);
        try {
            registerAsSessionAlarmListener(this, sessionID);
        } catch (ProtocolException e) {
            protocolSession.unregisterProtocolSessionListener(this, sessionID);
            throw new ProtocolException("Failed to register as session alarms listener for session " + sessionID,
                    e);
        }

        liveSessions.put(sessionID, new ProtocolPooled(protocolSession, now));
        liveSessionContexts.put(sessionID, protocolSessionContext);
        protocolSession.connect();

        return protocolSession;
    }

    @Override
    public synchronized void destroyProtocolSession(String sessionID) throws ProtocolException {

        IProtocolSession protocolSession = getSessionById(sessionID);
        if (protocolSession == null) {
            throw new ProtocolException("There is no existing session with this ID: " + sessionID);
        }

        lockProtocolSession(sessionID);

        /* disconnect the session */
        if (protocolSession.getStatus().equals(Status.CONNECTED)) {
            protocolSession.disconnect();
        }

        liveSessions.remove(sessionID);
        liveSessionContexts.remove(sessionID);
        protocolSession.unregisterProtocolSessionListener(this, sessionID);
        try {
            unregisterAsSessionAlarmListener(this, sessionID);
        } catch (ProtocolException e) {
            // ignored (even if unregistration fails, no events can be received as session is destroyed)
            log.warn("Failed to unregister as session alarms listener for session " + sessionID
                    + " No events can be received as session is destroyed.");
        }

        lockedProtocolSessions.remove(sessionID);
    }

    @Override
    public synchronized IProtocolSession obtainSessionByProtocol(String protocol, boolean lock)
            throws ProtocolException {

        if (protocol == null)
            throw new ProtocolException("Requested protocol is null.");

        ProtocolSessionContext context = registeredContexts.get(protocol);
        if (context == null)
            throw new ProtocolException("No such registered context for protocol: " + protocol);

        return obtainSession(context, lock);
    }

    @Override
    public synchronized IProtocolSession obtainSession(ProtocolSessionContext context, boolean lock)
            throws ProtocolException {

        IProtocolSession session = getSessionByContext(context);

        if (session == null) {
            session = createProtocolSession(context);
        }

        if (lock)
            lockProtocolSession(session.getSessionId());

        return session;
    }

    @Override
    public synchronized IProtocolSession getSessionById(String sessionId, boolean lock) throws ProtocolException {

        IProtocolSession session = getSessionById(sessionId);
        if (session == null)
            throw new ProtocolException("There is no existing session with ID: " + sessionId);

        if (lock)
            lockProtocolSession(sessionId);

        return session;
    }

    /**
     * 
     * @param sessionId
     * @return IProtocolSession with given sessionId, if there is one, or null otherwise
     */
    private synchronized IProtocolSession getSessionById(String sessionId) {
        if (sessionId == null)
            return null;

        ProtocolPooled pooled = liveSessions.get(sessionId);
        if (pooled == null)
            return null;

        return pooled.getProtocolSession();
    }

    /**
     * 
     * @param context
     * @return IProtocolSession with given context, if there is one, or null otherwise
     */
    private IProtocolSession getSessionByContext(ProtocolSessionContext context) {
        for (ProtocolPooled pooledSession : liveSessions.values()) {
            if (pooledSession.getProtocolSession().getSessionContext().equals(context)) {
                return pooledSession.getProtocolSession();
            }
        }
        return null;
    }

    @Override
    public void registerContext(ProtocolSessionContext context) throws ProtocolException {

        String protocol = (String) context.getSessionParameters().get(ProtocolSessionContext.PROTOCOL);

        checkIsSupportedProtocol(protocol);

        // unregister the old context (if any)
        unregisterContext(protocol);

        registeredContexts.put(protocol, context);
    }

    @Override
    public void unregisterContext(String protocol) throws ProtocolException {
        ProtocolSessionContext removedContext = registeredContexts.remove(protocol);
        if (removedContext != null) {
            IProtocolSession toDestroy = getSessionByContext(removedContext);
            if (toDestroy != null) {
                destroyProtocolSession(toDestroy.getSessionId());
            }
        }
    }

    @Override
    public void unregisterContext(ProtocolSessionContext context) throws ProtocolException {
        String protocol = (String) context.getSessionParameters().get(ProtocolSessionContext.PROTOCOL);
        unregisterContext(protocol);
    }

    private void checkIsSupportedProtocol(String protocol) throws ProtocolException {
        if (!protocolManager.isSupportedProtocol(protocol)) {
            throw new ProtocolException("Unsupported protocol " + protocol);
        }
    }

    private synchronized void lockProtocolSession(String sessionID) throws ProtocolException {
        if (sessionID == null) {
            throw new ProtocolException("The session ID provided is null");
        }

        if (!liveSessions.containsKey(sessionID)) {
            throw new ProtocolException("There is no existing session with ID: " + sessionID);
        }

        liveSessions.get(sessionID).getLock().lock();

        lockedProtocolSessions.add(sessionID);
    }

    private synchronized void unlockProtocolSession(String sessionID) throws ProtocolException {
        if (sessionID == null) {
            throw new ProtocolException("The session ID provided is null");
        }

        if (!liveSessions.containsKey(sessionID)) {
            throw new ProtocolException("There is no existing session with ID: " + sessionID);
        }

        if (!lockedProtocolSessions.contains(sessionID)) {
            log.warn("The session identified by this sessionID is not currently locked. Ignoring unlock.");
        }

        lockedProtocolSessions.remove(sessionID);
        liveSessions.get(sessionID).getLock().unlock();
    }

    @Override
    public synchronized boolean isLocked(String sessionId) throws ProtocolException {

        if (sessionId == null) {
            throw new ProtocolException("The session ID provided is null");
        }

        if (!liveSessions.containsKey(sessionId)) {
            throw new ProtocolException("There is no existing session with ID: " + sessionId);
        }

        ProtocolPooled pooled = liveSessions.get(sessionId);

        return ((ReentrantLock) pooled.getLock()).isLocked();
    }

    /**
     * Get the miliseconds since this session was last released.
     * 
     * @param sessionIds
     * @return System.currentTimeMillis() snapshot taken when released.
     */
    public long getSessionlastUsed(String sessionIds) {
        // This function is a bit bad placed, but saves us from having a lastUsed field in the IProtocolSession implementations, which are potentially
        // done by third parties.
        return liveSessions.get(sessionIds).getLastUsed();
    }

    public synchronized void purgeOldSessions() throws ProtocolException {
        purgeOldSessions(expirationTime);
    }

    public synchronized void purgeOldSessions(long milis) throws ProtocolException {

        long now = System.currentTimeMillis();

        List<ProtocolPooled> toRemove = new ArrayList<ProtocolSessionManager.ProtocolPooled>();

        for (ProtocolPooled pooledSession : liveSessions.values()) {
            if ((now - pooledSession.lastUsed) > milis) {
                toRemove.add(pooledSession);
            }
        }

        for (int i = toRemove.size() - 1; i >= 0; i--) {
            ProtocolPooled pooledSession = toRemove.get(i);
            log.debug("Destroying session: " + pooledSession.getProtocolSession().getSessionId());
            destroyProtocolSession(pooledSession.getProtocolSession().getSessionId());
        }
    }

    @Override
    public synchronized void releaseSession(String sessionId) throws ProtocolException {
        touchSession(sessionId);
        unlockProtocolSession(sessionId);
    }

    @Override
    public synchronized void releaseSession(IProtocolSession session) throws ProtocolException {
        releaseSession(session.getSessionId());
    }

    private void touchSession(String sessionId) {
        if (sessionId != null) {
            ProtocolPooled pooled = liveSessions.get(sessionId);
            if (pooled != null) {
                pooled.lastUsed = System.currentTimeMillis();
            }
        }
    }

    /* EVENTS METHODS */

    /**
     * If you receive a message its a CONNECTION_LOST so destroy the session.
     */
    @Override
    public void messageReceived(Object message) {
        if (message instanceof String) {
            try {
                destroyProtocolSession((String) message);
            } catch (ProtocolException e) {
                e.printStackTrace();
                log.error(e.getMessage());
            }
        }
    }

    /**
     * Specify the type of message which we want to listen
     */
    @Override
    public boolean notify(Object message) {
        if (message instanceof Status) {
            Status status = (Status) message;
            return status.equals(Status.CONNECTION_LOST);
        }

        return false;
    }

    private void registerAsSessionAlarmListener(EventHandler handler, String sessionId) throws ProtocolException {

        Properties filterProperties = new Properties();
        filterProperties.put(SessionAlarm.SESSION_ID_PROPERTY, sessionId);
        EventFilter filter = new EventFilter(new String[] { SessionAlarm.TOPIC }, filterProperties);

        int registrationNum = getEventManager().registerEventHandler(this, filter);
        sessionEventsListenerRegistrationNumbers.put(sessionId, registrationNum);

    }

    private void unregisterAsSessionAlarmListener(EventHandler handler, String sessionId) throws ProtocolException {
        getEventManager().unregisterHandler(sessionEventsListenerRegistrationNumbers.get(sessionId));
    }

    /**
     * Callback called when events are received
     */
    @Override
    public void handleEvent(Event event) {
        log.debug("ProtocolSessionManager received an event");
        if (event instanceof SessionAlarm) {
            Properties prop = new Properties();
            prop.put(CapabilityAlarm.RESOURCE_ID_PROPERTY, getResourceID());
            prop.put(CapabilityAlarm.CAUSE_PROPERTY, event);

            CapabilityAlarm alarm = new CapabilityAlarm(prop);
            try {
                publish(alarm);
            } catch (ProtocolException e) {
                log.error("Failed to publish alarm for resource: " + getResourceID(), e);
            }
        }
    }

    private void publish(Event event) throws ProtocolException {
        getEventManager().publishEvent(event);
    }

    /**
     * @param eventManager
     */
    public void setEventManager(IEventManager eventManager) {
        this.eventManager = eventManager;
    }

    private IEventManager getEventManager() throws ProtocolException {
        if (this.eventManager == null)
            throw new ProtocolException("No eventManager found!");

        return this.eventManager;
    }

    public void registerAsOSGiService() throws ProtocolException {
        registration = null;
        if (Activator.getBundleContext() != null) {
            Dictionary<String, String> props = new Hashtable<String, String>();
            props.put("resourceId", resourceID);
            props = addWSRegistrationProperties(props);

            registration = Activator.getBundleContext().registerService(IProtocolSessionManager.class.getName(),
                    this, props);
        }
    }

    public void unregisterAsOSGiService() {
        if (registration != null) {
            registration.unregister();
            registration = null;
        }
    }

    private Dictionary<String, String> addWSRegistrationProperties(Dictionary<String, String> props)
            throws ProtocolException {
        IResource resource;
        try {
            resource = getResource(resourceID);
            String resourceType = resource.getResourceDescriptor().getInformation().getType();
            String resourceName = resource.getResourceDescriptor().getInformation().getName();

            ConfigurationAdminUtil configurationAdmin = new ConfigurationAdminUtil(Activator.getBundleContext());
            String url = configurationAdmin.getProperty("org.opennaas", "ws.rest.url");

            if (props != null) {
                props.put("service.exported.interfaces", "*");
                props.put("service.exported.configs", "org.apache.cxf.rs");
                props.put("service.exported.intents", "HTTP");
                props.put("org.apache.cxf.rs.httpservice.context",
                        url + "/" + resourceType + "/" + resourceName + "/protocolSessionManager");
                props.put("org.apache.cxf.rs.address", "/");
                props.put("org.apache.cxf.httpservice.requirefilter", "true");
            }
            log.info("Registering ws in url: " + props.get("org.apache.cxf.rs.address"));
        } catch (ResourceException e) {
            throw new ProtocolException(e);
        } catch (IOException e) {
            throw new ProtocolException(e);
        }
        return props;
    }

    private IResource getResource(String resourceId) throws ResourceException {
        try {
            IResource resource = Activator.getResourceManagerService().getResourceById(resourceID);
            if (resource == null) {
                throw new ResourceException("Given resource does not exist in ResourceManager");
            }
            return resource;
        } catch (ResourceException e) {
            throw new ResourceException("Given resource does not exist in ResourceManager", e);
        } catch (ActivatorException e) {
            throw new ResourceException("Fail to check existence of given resource", e);
        }
    }

}