package org.opennaas.core.protocols.sessionmanager; 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 ListResponse getAllProtocolSessionIdsWS() { ListResponse resp = new ListResponse(); resp.setList(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); } } }