tomcat Session manager to support clustering using infinispan
[controller.git] / opendaylight / samples / clustersession / src / main / java / org / opendaylight / controller / clustersession / impl / ClusterSessionServiceImpl.java
diff --git a/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/impl/ClusterSessionServiceImpl.java b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/impl/ClusterSessionServiceImpl.java
new file mode 100644 (file)
index 0000000..e1c7dfd
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ */
+package org.opendaylight.controller.clustersession.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.util.SessionIdGenerator;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.clustersession.ClusterSession;
+import org.opendaylight.controller.clustersession.ClusterSessionData;
+import org.opendaylight.controller.clustersession.ClusterSessionManager;
+import org.opendaylight.controller.clustersession.ClusterSessionUtil;
+import org.opendaylight.controller.clustersession.service.ClusterSessionService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation to persist and retrieve session data from infinispan cache
+ * @author harman singh
+ *
+ */
+public class ClusterSessionServiceImpl implements ClusterSessionService,
+  ServiceTrackerCustomizer<IClusterGlobalServices, IClusterGlobalServices>{
+
+  private IClusterGlobalServices clusterGlobalServices = null;
+  private static final Logger LOGGER = LoggerFactory.getLogger(ClusterSessionServiceImpl.class);
+  private ConcurrentMap<String, ClusterSessionData> sessions = null;
+  private static final String SESSION_CACHE = "customSessionManager.sessionData";
+  private ClusterSessionManager manager = null;
+  private SessionIdGenerator sessionIdGenerator = null;
+  private BundleContext context = null;
+  private ServiceTracker<IClusterGlobalServices, IClusterGlobalServices> clusterTracker;
+  public ClusterSessionServiceImpl(ClusterSessionManager manager) {
+    this.manager = manager;
+  }
+  /**
+   * This method initialize the cluster service of opendaylight and
+   * create a cache map in infinispan
+   */
+
+  @Override
+  public void startInternal(SessionIdGenerator sessionIdGenerator){
+    this.sessionIdGenerator = sessionIdGenerator;
+    context = FrameworkUtil.getBundle(ClusterSessionManager.class).getBundleContext();
+    getClusterService();
+    createCache();
+  }
+
+  /**
+   * Removes the cluster service tracker while shut down
+   */
+  @Override
+  public void stopInternal(){
+    if(clusterTracker != null){
+      clusterTracker.close();
+    }
+  }
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Session findSession(final String id){
+    if(id == null) {
+      return null;
+    }
+    if(sessions == null) {
+      LOGGER.debug("Session cache not present, try to create.");
+      createCache();
+      return null;
+    }
+    ClusterSessionData sessionData = sessions.get(id);
+    if(sessionData != null) {
+      LOGGER.debug("SESSION FOUND : ", id);
+    } else {
+      LOGGER.debug("SESSION NOTFOUND : ", id);
+    }
+    return ClusterSessionUtil.getDeserializedSession(sessionData, this, this.manager);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Session[] findSessions() {
+    if(sessions == null) {
+      LOGGER.debug("Session cache not present, try to create.");
+      createCache();
+      return new Session[0];
+    }
+    Collection<ClusterSessionData> sessionDataList = sessions.values();
+    ArrayList<ClusterSession> sessionList = new ArrayList<ClusterSession>();
+    for(ClusterSessionData sessionData : sessionDataList){
+      sessionList.add(ClusterSessionUtil.getDeserializedSession(sessionData, this, this.manager));
+    }
+    return sessionList.toArray(new Session[0]);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void removeSession(final String id){
+    if(sessions == null) {
+      LOGGER.debug("Session cache not present, try to create.");
+      createCache();
+      return;
+    }
+    sessions.remove(id);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void expireSession(final String id){
+    if(sessions == null) {
+      LOGGER.debug("Session cache not present, try to create.");
+      createCache();
+      return;
+    }
+    ClusterSessionData sessionData = sessions.get(id);
+    if(sessionData != null) {
+      sessionData.getSession().expire();
+      removeSession(id);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Session createSession(final String sessionId){
+    if(sessions == null) {
+      LOGGER.debug("Session cache not present, try to create.");
+      createCache();
+      return null;
+    }
+    Session session = createEmptySession();
+    session.setNew(true);
+    session.setValid(true);
+    session.setCreationTime(System.currentTimeMillis());
+    String id = sessionId;
+    if (id == null) {
+      id = generateSessionId();
+    }
+    session.setId(id);
+    return session;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void addSession(final ClusterSession session){
+    if(sessions == null) {
+      LOGGER.debug("Session cache not present, try to create.");
+      createCache();
+      return;
+    }
+    ClusterSessionData sessionData = ClusterSessionUtil.getSerializableSession(session);
+    sessions.put(session.getId(), sessionData);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Session createEmptySession(){
+    return getNewSession();
+  }
+
+  /**
+   * Returns information about the session with the given session id.
+   *
+   * <p>The session information is organized as a HashMap, mapping
+   * session attribute names to the String representation of their values.
+   *
+   * @param sessionId Session id
+   *
+   * @return HashMap mapping session attribute names to the String
+   * representation of their values, or null if no session with the
+   * specified id exists, or if the session does not have any attributes
+   */
+  public HashMap<String, String> getSession(String sessionId) {
+    if(sessions == null) {
+      LOGGER.debug("Session cache not present, try to create.");
+      createCache();
+      return null;
+    }
+    ClusterSessionData sessionData = sessions.get(sessionId);
+    if (sessionData == null) {
+      return null;
+    }
+    ClusterSession s = ClusterSessionUtil.getDeserializedSession(sessionData, this, this.manager);
+    Enumeration<String> ee = s.getAttributeNames();
+    if (ee == null || !ee.hasMoreElements()) {
+      return null;
+    }
+    HashMap<String, String> map = new HashMap<String, String>();
+    while (ee.hasMoreElements()) {
+      String attrName = ee.nextElement();
+      map.put(attrName, s.getAttribute(attrName).toString());
+    }
+    return map;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void updateSession(ClusterSession session) {
+    if(sessions == null) {
+      LOGGER.debug("Session cache not present, try to create.");
+      createCache();
+      return;
+    }
+    if(session.getId() != null && sessions.get(session.getId()) != null){
+      ClusterSessionData sessionData = ClusterSessionUtil.getSerializableSession(session);
+      sessions.put(session.getId(), sessionData);
+    }
+  }
+
+  @Override
+  public IClusterGlobalServices addingService(ServiceReference<IClusterGlobalServices> reference) {
+      if (clusterGlobalServices == null) {
+        this.clusterGlobalServices = context.getService(reference);
+        createCache();
+        return clusterGlobalServices;
+      }
+      return null;
+  }
+
+  @Override
+  public void modifiedService(ServiceReference<IClusterGlobalServices> reference, IClusterGlobalServices service) {
+    // This method is added from ServiceTracker interface, We don't have to modify service.
+  }
+
+  @Override
+  public void removedService(ServiceReference<IClusterGlobalServices> reference, IClusterGlobalServices service) {
+      if (clusterGlobalServices == service) {
+          clusterGlobalServices = null;
+      }
+  }
+
+  /*
+   * Return an instance of Standard Session object with current session manager
+   */
+  private ClusterSession getNewSession() {
+    return new ClusterSession(this.manager, this);
+  }
+
+  /*
+   * Generate and return a new session identifier.
+   */
+  private String generateSessionId() {
+    String result = null;
+    do {
+      result = sessionIdGenerator.generateSessionId();
+    } while (sessions.containsKey(result));
+    return result;
+  }
+
+  private void createCache() {
+    allocateCache();
+    retrieveCache();
+  }
+
+  /*
+   * This is a fragment bundle, so We can't use Activator to set Service.
+   * This is the alternative to get registered clustered service
+   */
+  private void getClusterService(){
+    if (context != null) {
+      clusterTracker = new ServiceTracker<>(context, IClusterGlobalServices.class, this);
+      clusterTracker.open();
+    }
+  }
+
+  /*
+   * Allocate space in infinispan to persist session data
+   */
+  private void allocateCache() {
+    if (clusterGlobalServices == null) {
+      LOGGER.trace("un-initialized clusterGlobalService, can't create cache");
+      return;
+    }
+    try {
+      clusterGlobalServices.createCache(SESSION_CACHE,
+          EnumSet.of(IClusterServices.cacheMode.SYNC , IClusterServices.cacheMode.TRANSACTIONAL));
+
+    } catch (CacheConfigException cce) {
+      LOGGER.error("Cache configuration invalid - check cache mode", cce.toString());
+    } catch (CacheExistException ce) {
+      LOGGER.debug("Skipping cache creation as already present", ce.toString());
+    }
+  }
+
+  /*
+   * Fetch cached session data map object from infinispan
+   */
+  @SuppressWarnings("unchecked")
+  private void retrieveCache(){
+    if (clusterGlobalServices == null) {
+      LOGGER.trace("un-initialized clusterGlobalService, can't retrieve cache");
+      return;
+    }
+    sessions = (ConcurrentMap<String, ClusterSessionData>)clusterGlobalServices.getCache(SESSION_CACHE);
+    if(sessions == null){
+      LOGGER.warn("Failed to get session cache");
+    }
+  }
+}
\ No newline at end of file