From: Harman Singh Date: Thu, 17 Apr 2014 23:43:22 +0000 (-0700) Subject: tomcat Session manager to support clustering using infinispan X-Git-Tag: autorelease-tag-v20140601202136_82eb3f9~191^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=bb8e483729cee2950c8394751e21f87812aec48d tomcat Session manager to support clustering using infinispan Change-Id: I65faff3a62f5c2cf3098e87b52649bf58a6e873a Signed-off-by: Harman Singh --- diff --git a/opendaylight/samples/clustersession/pom.xml b/opendaylight/samples/clustersession/pom.xml new file mode 100644 index 0000000000..f72e47b283 --- /dev/null +++ b/opendaylight/samples/clustersession/pom.xml @@ -0,0 +1,109 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.2-SNAPSHOT + ../../commons/opendaylight + + org.opendaylight.controller + clustersession + 1.0.0-SNAPSHOT + bundle + + + equinoxSDK381 + javax.servlet + + + orbit + org.apache.catalina + + + orbit + org.apache.catalina.ha + + + orbit + org.apache.coyote + + + orbit + org.apache.juli.extras + + + orbit + org.apache.tomcat.api + + + orbit + org.apache.tomcat.util + + + org.opendaylight.controller + clustering.services + 0.5.1-SNAPSHOT + + + org.slf4j + jcl-over-slf4j + + + org.osgi + org.osgi.core + provided + + + junit + junit + test + + + org.mockito + mockito-all + 1.9.5 + test + + + org.powermock + powermock-api-mockito + 1.5.4 + test + + + org.powermock + powermock-module-junit4 + 1.5.4 + test + + + + + + + org.apache.felix + maven-bundle-plugin + 2.4.0 + true + + + org.eclipse.gemini.web.tomcat + org.opendaylight.controller.clustersession + org.apache.catalina, + org.apache.catalina.session, + org.apache.catalina.util, + org.apache.catalina.ha.session, + javax.servlet, + javax.servlet.http, + org.slf4j, + org.osgi.framework, + org.eclipse.osgi.framework.console, + org.opendaylight.controller.clustering.services + + ${project.basedir}/META-INF + + + + + diff --git a/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSession.java b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSession.java new file mode 100644 index 0000000000..dc93700c0d --- /dev/null +++ b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSession.java @@ -0,0 +1,162 @@ +package org.opendaylight.controller.clustersession; + +import java.beans.PropertyChangeSupport; +import java.io.Serializable; +import java.security.Principal; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.catalina.Manager; +import org.apache.catalina.SessionListener; +import org.apache.catalina.session.StandardSession; +import org.opendaylight.controller.clustersession.service.ClusterSessionService; + +public class ClusterSession extends StandardSession implements Serializable { + + private static final long serialVersionUID = 1L; + + private transient ClusterSessionService sessionService; + + public ClusterSession(Manager manager, ClusterSessionService sessionService) { + super(manager); + this.sessionService = sessionService; + } + + public void setSessionService(ClusterSessionService sessionService){ + this.sessionService = sessionService; + } + + @Override + public void setAuthType(String authType) { + super.setAuthType(authType); + sessionService.updateSession(this); + } + + @Override + public void setCreationTime(long time) { + super.setCreationTime(time); + sessionService.updateSession(this); + } + + @Override + public void setMaxInactiveInterval(int interval) { + super.setMaxInactiveInterval(interval); + sessionService.updateSession(this); + } + + @Override + public void setNew(boolean isNew) { + super.setNew(isNew); + sessionService.updateSession(this); + } + + @Override + public void setPrincipal(Principal principal) { + super.setPrincipal(principal); + sessionService.updateSession(this); + } + + @Override + public void setValid(boolean isValid) { + super.setValid(isValid); + sessionService.updateSession(this); + } + + @Override + public void access() { + super.access(); + sessionService.updateSession(this); + } + + @Override + public void endAccess() { + super.endAccess(); + sessionService.updateSession(this); + } + + @Override + public void removeAttribute(String name, boolean notify) { + super.removeAttribute(name, notify); + sessionService.updateSession(this); + } + + @Override + public void setAttribute(String name, Object value, boolean notify) { + super.setAttribute(name, value, notify); + sessionService.updateSession(this); + } + + @Override + public void recycle() { + super.recycle(); + sessionService.updateSession(this); + } + + @Override + public void removeNote(String name) { + super.removeNote(name); + sessionService.updateSession(this); + } + + @Override + public void addSessionListener(SessionListener listener) { + super.addSessionListener(listener); + sessionService.updateSession(this); + } + + @Override + public void removeSessionListener(SessionListener listener) { + super.removeSessionListener(listener); + sessionService.updateSession(this); + } + + @Override + public void setNote(String name, Object value) { + super.setNote(name, value); + sessionService.updateSession(this); + } + + /* + * Certain fields inside Standard session are not serialized, We need to process them here + */ + public void afterDeserialization(){ + if (listeners == null){ + listeners = new ArrayList(); + } + if (notes == null){ + notes = new ConcurrentHashMap(); + } + if(support == null){ + support = new PropertyChangeSupport(this); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("ClusterSession["); + sb.append(id); + sb.append(", isNew : "); + sb.append(isNew); + sb.append(", isValid : "); + sb.append(isValid); + sb.append("]"); + return sb.toString(); + } + + /* + * These methods are added for deserialization purpose + */ + + public void setAuthTypeInternal(String authType){ + this.authType = authType; + } + + public void setPrincipalInternal(Principal principal){ + this.principal = principal; + } + + public void setNoteInternal(String name, Object value) { + notes.put(name, value); + } +} diff --git a/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSessionData.java b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSessionData.java new file mode 100644 index 0000000000..b5796d2a6f --- /dev/null +++ b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSessionData.java @@ -0,0 +1,79 @@ +package org.opendaylight.controller.clustersession; + +import java.io.Serializable; +import java.util.Arrays; + +public class ClusterSessionData implements Serializable{ + + private static final long serialVersionUID = 1L; + + private ClusterSession session; + + private byte[] principalData; + + private byte[] savedRequestData; + + private byte[] savedPrincipalData; + + private String authType; + + private String userName; + + private String password; + + public ClusterSession getSession() { + return session; + } + + public void setSession(final ClusterSession session) { + this.session = session; + } + + public byte[] getPrincipalData() { + return principalData; + } + + public void setPrincipalData(final byte[] principalData) { + this.principalData = Arrays.copyOf(principalData, principalData.length); + } + + public String getAuthType() { + return authType; + } + + public void setAuthType(String authType) { + this.authType = authType; + } + + public byte[] getSavedRequestData() { + return savedRequestData; + } + + public void setSavedRequestData(byte[] savedRequestData) { + this.savedRequestData = Arrays.copyOf(savedRequestData, savedRequestData.length); + } + + public byte[] getSavedPrincipalData() { + return savedPrincipalData; + } + + public void setSavedPrincipalData(byte[] savedPrincipalData) { + this.savedPrincipalData = Arrays.copyOf(savedPrincipalData, savedPrincipalData.length); + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSessionManager.java b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSessionManager.java new file mode 100644 index 0000000000..e285bb4f15 --- /dev/null +++ b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSessionManager.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + */ +package org.opendaylight.controller.clustersession; + +import java.io.IOException; +import java.util.HashMap; + +import org.apache.catalina.LifecycleException; +import org.apache.catalina.LifecycleState; +import org.apache.catalina.Session; +import org.apache.catalina.session.ManagerBase; +import org.apache.catalina.util.SessionIdGenerator; +import org.opendaylight.controller.clustersession.impl.ClusterSessionServiceImpl; +import org.opendaylight.controller.clustersession.service.ClusterSessionService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +/** + * ClusterSession Manager is a custom session manager, that is used to persist session data + * across cluster of a storage such as infinispan or memcache + * @author harman singh + * + */ +public class ClusterSessionManager extends ManagerBase{ + /** + * Has this component been _started yet? + */ + protected boolean started = false; + + protected ClusterSessionService sessionService; + + private static final Logger LOGGER = LoggerFactory.getLogger(ClusterSessionManager.class); + /** + * The descriptive information about this implementation. + */ + protected static final String INFO = "ClusterSessionManager/1.0"; + + /** + * The descriptive name of this Manager implementation (for logging). + */ + protected static final String NAME = "ClusterSessionManager"; + + public ClusterSessionManager(){ + sessionService = new ClusterSessionServiceImpl(this); + } + + /** + * Return descriptive information about this Manager implementation and + * the corresponding version number, in the format + * <description>/<version>. + */ + @Override + public String getInfo(){ + return INFO; + } + + /** + * Return the descriptive short name of this Manager implementation. + */ + @Override + public String getName(){ + return NAME; + } + + /** + * {@inheritDoc} + */ + @Override + public void load() throws ClassNotFoundException, IOException { + // We are not persisting any session in database, infinispan does not persist data. + // loading of persisted session is not required. + } + + /** + * {@inheritDoc} + */ + @Override + public void unload() throws IOException { + // We are not persisting any session in database, infinispan does not persist data. + // unloading of session to persistence layer is not required. + } + + /** + * Start this component and implement the requirements + * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. + * + * @exception LifecycleException if this component detects a fatal error + * that prevents this component from being used + */ + @Override + protected synchronized void startInternal() throws LifecycleException { + sessionIdGenerator = new SessionIdGenerator(); + sessionIdGenerator.setJvmRoute(getJvmRoute()); + sessionIdGenerator.setSecureRandomAlgorithm(getSecureRandomAlgorithm()); + sessionIdGenerator.setSecureRandomClass(getSecureRandomClass()); + sessionIdGenerator.setSecureRandomProvider(getSecureRandomProvider()); + sessionIdGenerator.setSessionIdLength(getSessionIdLength()); + sessionService.startInternal(sessionIdGenerator); + setState(LifecycleState.STARTING); + } + + /** + * Stop this component and implement the requirements + * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. + * + * @exception LifecycleException if this component detects a fatal error + * that prevents this component from being used + */ + @Override + protected synchronized void stopInternal() throws LifecycleException { + setState(LifecycleState.STOPPING); + + // Expire all active sessions + Session sessions[] = findSessions(); + for (int i = 0; i < sessions.length; i++) { + Session session = sessions[i]; + try { + if (session.isValid()) { + session.expire(); + } + } catch (Exception e) { + LOGGER.warn(e.toString()); + } finally { + // Measure against memory leaking if references to the session + // object are kept in a shared field somewhere + session.recycle(); + } + } + // Require a new random number generator if we are restarted + super.stopInternal(); + sessionService.stopInternal(); + } + + /** + * {@inheritDoc} + */ + @Override + public void expireSession(final String sessionId){ + LOGGER.debug("SESSION EXPIRE : ", sessionId); + sessionService.expireSession(sessionId); + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(final Session session){ + LOGGER.debug("SESSION REMOVE : ", session.getId()); + sessionService.removeSession(session.getId()); + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(Session session, boolean update) { + sessionService.removeSession(session.getId()); + } + + /** + * {@inheritDoc} + */ + @Override + public Session findSession(final String id) throws IOException{ + return sessionService.findSession(id); + } + + /** + * {@inheritDoc} + */ + @Override + public Session createSession(final String sessionId){ + LOGGER.debug("SESSION CREATE : ", sessionId); + if(sessionId != null){ + Session session = sessionService.findSession(sessionId); + if(session != null){ + return session; + } + } + return sessionService.createSession(sessionId); + } + + /** + * {@inheritDoc} + */ + @Override + public Session createEmptySession(){ + return sessionService.createEmptySession(); + } + /** + * {@inheritDoc} + */ + @Override + public void add(Session session){ + LOGGER.debug("SESSION ADD : ", session.getId()); + sessionService.addSession((ClusterSession)session); + } + /** + * {@inheritDoc} + */ + @Override + public HashMap getSession(String sessionId){ + return sessionService.getSession(sessionId); + } + /** + * {@inheritDoc} + */ + @Override + public Session[] findSessions() { + return sessionService.findSessions(); + } + + public ClusterSessionService getSessionService() { + return sessionService; + } + + public void setSessionService(ClusterSessionService sessionService) { + this.sessionService = sessionService; + } + +} diff --git a/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSessionUtil.java b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSessionUtil.java new file mode 100644 index 0000000000..01fad1aabd --- /dev/null +++ b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/ClusterSessionUtil.java @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + */ +package org.opendaylight.controller.clustersession; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.catalina.authenticator.Constants; +import org.apache.catalina.authenticator.SavedRequest; +import org.apache.catalina.ha.session.SerializablePrincipal; +import org.apache.catalina.realm.GenericPrincipal; +import org.opendaylight.controller.clustersession.service.ClusterSessionService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ClusterSessionUtil will be used to convert ClusterSession object into ClusterSessionData object, + * which is serializable and can be passed for storage. This class also perform deserialization to + * create ClusterSession object + * @author harman singh + * + */ + +public class ClusterSessionUtil { + + private static final Logger LOGGER = LoggerFactory.getLogger(ClusterSessionUtil.class); + + private ClusterSessionUtil() { + + } + + /** + * Serialize the ClusterSession object to provide ClusterSessionData object, + * that will be used for storage like in inifinispan or memcache etc. + * @param session an instance of ClusterSession + * @return an instance of ClusterSessionData + */ + public static ClusterSessionData getSerializableSession(ClusterSession session) { + if(session == null){ + return null; + } + ClusterSessionData sessionData = new ClusterSessionData(); + sessionData.setSession(session); + sessionData.setAuthType(session.getAuthType()); + sessionData.setPrincipalData(serializePrincipal(session.getPrincipal())); + sessionData.setSavedRequestData(serializeSavedRequest(session.getNote(Constants.FORM_REQUEST_NOTE))); + Principal notePrincipal = (Principal) session.getNote(Constants.FORM_PRINCIPAL_NOTE); + byte[] principalBytes = serializePrincipal(notePrincipal); + sessionData.setSavedPrincipalData(principalBytes); + if(session.getPrincipal() == null && notePrincipal != null){ + sessionData.setPrincipalData(principalBytes); + } + sessionData.setUserName((String) session.getNote(Constants.FORM_USERNAME)); + sessionData.setPassword((String) session.getNote(Constants.FORM_PASSWORD)); + return sessionData; + } + + /** + * Deserialize the ClusterSessionData object that usually comes from storage + * to provide ClusterSession object, + * that will be used by Session Manager + * @param sessionData an instance of ClusterSessionData + * @param sessionService an instance of ClusterSessionService + * @param manager an instance of ClusterSessionManager + * @return an instance of ClusterSession + */ + + public static ClusterSession getDeserializedSession(ClusterSessionData sessionData, ClusterSessionService sessionService, + ClusterSessionManager manager) { + if(sessionData == null){ + return null; + } + ClusterSession session = sessionData.getSession(); + session.afterDeserialization(); + session.setManager(manager); + session.setSessionService(sessionService); + if(sessionData.getAuthType() != null) { + session.setAuthTypeInternal(sessionData.getAuthType()); + } + if(sessionData.getPrincipalData() != null && sessionData.getPrincipalData().length > 0){ + session.setPrincipalInternal(deserializePrincipal(sessionData.getPrincipalData())); + } + if(sessionData.getSavedPrincipalData() != null && sessionData.getSavedPrincipalData().length > 0){ + session.setNoteInternal(Constants.FORM_PRINCIPAL_NOTE, deserializePrincipal(sessionData.getSavedPrincipalData())); + } + if(sessionData.getSavedRequestData() != null && sessionData.getSavedRequestData().length > 0){ + session.setNoteInternal(Constants.FORM_REQUEST_NOTE, deserializeSavedRequest(sessionData.getSavedRequestData())); + } + if(sessionData.getUserName() != null){ + session.setNoteInternal(Constants.FORM_USERNAME, sessionData.getUserName()); + } + if(sessionData.getPassword() != null){ + session.setNoteInternal(Constants.FORM_PASSWORD, sessionData.getPassword()); + } + return session; + } + + private static byte[] serializePrincipal(final Principal principal){ + if(principal == null) { + return new byte[0]; + } + ByteArrayOutputStream bos = null; + ObjectOutputStream oos = null; + try { + bos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(bos); + SerializablePrincipal.writePrincipal((GenericPrincipal) principal, oos ); + oos.flush(); + return bos.toByteArray(); + } catch (IOException e) { + throw new IllegalArgumentException( "Non-serializable object", e); + } finally { + closeSilently(bos); + closeSilently(oos); + } + } + + private static byte[] serializeSavedRequest(final Object obj) { + if(obj == null) { + return new byte[0]; + } + final SavedRequest savedRequest = (SavedRequest) obj; + ByteArrayOutputStream bos = null; + ObjectOutputStream oos = null; + try { + bos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(bos); + oos.writeObject(savedRequest.getContentType()); + oos.writeObject(getHeaders(savedRequest)); + oos.writeObject(newArrayList(savedRequest.getLocales())); + oos.writeObject(savedRequest.getMethod()); + oos.writeObject(savedRequest.getQueryString()); + oos.writeObject(savedRequest.getRequestURI()); + oos.writeObject(savedRequest.getDecodedRequestURI()); + oos.flush(); + return bos.toByteArray(); + } catch (IOException e) { + throw new IllegalArgumentException( "Non-serializable object", e); + } finally { + closeSilently(bos); + closeSilently(oos); + } + } + + private static Principal deserializePrincipal(final byte[] data) { + ByteArrayInputStream bis = null; + ObjectInputStream ois = null; + try { + bis = new ByteArrayInputStream(data); + ois = new ObjectInputStream(bis); + return SerializablePrincipal.readPrincipal(ois); + } catch (IOException e) { + throw new IllegalArgumentException( "Could not deserialize principal", e); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException( "Could not deserialize principal", e); + } finally { + closeSilently(bis); + closeSilently(ois); + } + } + + @SuppressWarnings("unchecked") + private static SavedRequest deserializeSavedRequest(final byte[] data) { + ByteArrayInputStream bis = null; + ObjectInputStream ois = null; + try { + bis = new ByteArrayInputStream(data); + ois = new ObjectInputStream(bis); + final SavedRequest savedRequest = new SavedRequest(); + savedRequest.setContentType((String) ois.readObject()); + setHeaders(savedRequest, (Map>) ois.readObject()); + setLocales(savedRequest, (List) ois.readObject()); + savedRequest.setMethod((String) ois.readObject()); + savedRequest.setQueryString((String) ois.readObject()); + savedRequest.setRequestURI((String) ois.readObject()); + savedRequest.setDecodedRequestURI((String) ois.readObject()); + return savedRequest; + } catch (final IOException e) { + throw new IllegalArgumentException( "Could not deserialize SavedRequest", e ); + } catch (final ClassNotFoundException e) { + throw new IllegalArgumentException( "Could not deserialize SavedRequest", e ); + } finally { + closeSilently(bis); + closeSilently(ois); + } + } + + private static void setLocales(final SavedRequest savedRequest, final List locales) { + if(locales != null && !locales.isEmpty()) { + for (final Locale locale : locales) { + savedRequest.addLocale(locale); + } + } + } + + private static List newArrayList(final Iterator iter) { + if(!iter.hasNext()) { + return Collections.emptyList(); + } + final List result = new ArrayList(); + while (iter.hasNext()) { + result.add(iter.next()); + } + return result; + } + + private static Map> getHeaders(final SavedRequest obj) { + final Map> result = new HashMap>(); + final Iterator namesIter = obj.getHeaderNames(); + while (namesIter.hasNext()) { + final String name = namesIter.next(); + final List values = new ArrayList(); + result.put(name, values); + final Iterator valuesIter = obj.getHeaderValues(name); + while (valuesIter.hasNext()) { + final String value = valuesIter.next(); + values.add(value); + } + } + return result; + } + + private static void setHeaders(final SavedRequest obj, final Map> headers) { + if(headers != null) { + for (final Entry> entry : headers.entrySet()) { + final List values = entry.getValue(); + for (final String value : values) { + obj.addHeader(entry.getKey(), value); + } + } + } + } + + private static void closeSilently(final OutputStream os) { + if (os != null) { + try { + os.close(); + } catch (final IOException f) { + LOGGER.debug("Exception occurred while closing output stream", f.toString()); + } + } + } + + private static void closeSilently(final InputStream is) { + if (is != null) { + try { + is.close(); + } catch (final IOException f) { + LOGGER.debug("Exception occurred while closing input stream", f.toString()); + } + } + } +} 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 index 0000000000..e1c7dfd65d --- /dev/null +++ b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/impl/ClusterSessionServiceImpl.java @@ -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{ + + private IClusterGlobalServices clusterGlobalServices = null; + private static final Logger LOGGER = LoggerFactory.getLogger(ClusterSessionServiceImpl.class); + private ConcurrentMap sessions = null; + private static final String SESSION_CACHE = "customSessionManager.sessionData"; + private ClusterSessionManager manager = null; + private SessionIdGenerator sessionIdGenerator = null; + private BundleContext context = null; + private ServiceTracker 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 sessionDataList = sessions.values(); + ArrayList sessionList = new ArrayList(); + 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. + * + *

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 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 ee = s.getAttributeNames(); + if (ee == null || !ee.hasMoreElements()) { + return null; + } + HashMap map = new HashMap(); + 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 reference) { + if (clusterGlobalServices == null) { + this.clusterGlobalServices = context.getService(reference); + createCache(); + return clusterGlobalServices; + } + return null; + } + + @Override + public void modifiedService(ServiceReference reference, IClusterGlobalServices service) { + // This method is added from ServiceTracker interface, We don't have to modify service. + } + + @Override + public void removedService(ServiceReference 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)clusterGlobalServices.getCache(SESSION_CACHE); + if(sessions == null){ + LOGGER.warn("Failed to get session cache"); + } + } +} \ No newline at end of file diff --git a/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/service/ClusterSessionService.java b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/service/ClusterSessionService.java new file mode 100644 index 0000000000..9991b33f80 --- /dev/null +++ b/opendaylight/samples/clustersession/src/main/java/org/opendaylight/controller/clustersession/service/ClusterSessionService.java @@ -0,0 +1,84 @@ +package org.opendaylight.controller.clustersession.service; + +import java.util.HashMap; + +import org.apache.catalina.Session; +import org.apache.catalina.util.SessionIdGenerator; +import org.opendaylight.controller.clustersession.ClusterSession; + +/** + * A service to handle session persistence and retrieval in any data store + * + * @author harman singh + * + */ +public interface ClusterSessionService { + + /** + * This method performs all startup operations + */ + void startInternal(SessionIdGenerator sessionIdGenerator); + + /** + * Method to perform all clean up operations + */ + void stopInternal(); + + /** + * Find Session object based on provided session id from persistance + * @param id + * @return an instance of Session + */ + Session findSession(final String id); + + /** + * Get an array of session objects available in storage + */ + Session[] findSessions(); + + /** + * Remove a session object from persistence + * @param id of session object need to be removed + */ + void removeSession(final String id); + + /** + * Expire and remove a session object from persistence + * @param id of session object need to be expired + */ + void expireSession(final String id); + + /** + * Create a session object based on session id, if session is not present + * use random session id + * @param sessionId + * @return an instance of Session + */ + Session createSession(final String sessionId); + + /** + * Add a session object in persistence + * @param session an instance of ClusterSession + */ + void addSession(final ClusterSession session); + + /** + * Create an empty Session object + * @return session object + */ + Session createEmptySession(); + + /** + * Fetch attributes of Session object fetched by supplied session id + * @param sessionId + * @return + */ + HashMap getSession(String sessionId); + + /** + * update the session object in persistence + * @param session + */ + void updateSession(final ClusterSession session); + +} diff --git a/opendaylight/samples/clustersession/src/test/java/org/opendaylight/controller/clustersession/ClusterSessionManagerTest.java b/opendaylight/samples/clustersession/src/test/java/org/opendaylight/controller/clustersession/ClusterSessionManagerTest.java new file mode 100644 index 0000000000..9aae2bb3b7 --- /dev/null +++ b/opendaylight/samples/clustersession/src/test/java/org/opendaylight/controller/clustersession/ClusterSessionManagerTest.java @@ -0,0 +1,157 @@ +package org.opendaylight.controller.clustersession; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.Session; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.opendaylight.controller.clustering.services.IClusterGlobalServices; +import org.opendaylight.controller.clustersession.impl.ClusterSessionServiceImpl; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({FrameworkUtil.class}) +public class ClusterSessionManagerTest { + static ClusterSessionManager manager = null; + static ClusterSessionServiceImpl sessionService = null; + private static final String SESSION_CACHE = "customSessionManager.sessionData"; + static ConcurrentMap sessions = new ConcurrentHashMap(); + private String sessionId = "1234567"; + final String AUTH_TYPE = "FORM"; + final String ATTRIBUTE_NAME = "AuthType"; + final int SESSION_ID_LENGTH = 7; + @SuppressWarnings("unchecked") + @BeforeClass + public static void init(){ + Bundle bundle = mock(Bundle.class); + BundleContext context = mock(BundleContext.class); + IClusterGlobalServices clusterGlobalService = mock(IClusterGlobalServices.class); + ServiceReference serviceReference = mock(ServiceReference.class); + PowerMockito.mockStatic(FrameworkUtil.class); + when(FrameworkUtil.getBundle(ClusterSessionManager.class)).thenReturn(bundle); + when(bundle.getBundleContext()).thenReturn(context); + when(context.getService(serviceReference)).thenReturn(clusterGlobalService); + when((ConcurrentMap)clusterGlobalService.getCache(SESSION_CACHE)).thenReturn(sessions); + Context containerContext = mock(Context.class); + manager = new ClusterSessionManager(); + manager.setContainer(containerContext); + try { + manager.startInternal(); + } catch (LifecycleException e) { + } + sessionService = (ClusterSessionServiceImpl) manager.getSessionService(); + sessionService.addingService(serviceReference); + } + + @Test + public void checkSessionManagerCreated(){ + assertEquals("session manager info does not match", "ClusterSessionManager/1.0", manager.getInfo()); + assertEquals("session manager name does not match", "ClusterSessionManager", manager.getName()); + } + + @Test + public void testCreateEmptySession(){ + Session session = manager.createEmptySession(); + assertEquals("session manager does not match", manager, session.getManager()); + } + + @Test + public void testCreateRandomSessionId(){ + Session session = manager.createSession(null); + assertEquals("Session should be valid", true, session.isValid()); + manager.remove(session); + } + + @Test + public void testCreateSession(){ + Session session = manager.createSession(sessionId); + assertEquals("Session should be valid", true, session.isValid()); + assertEquals("Session id does not match", sessionId, session.getId()); + manager.remove(session); + } + + @Test + public void testReCreateSession(){ + Session session = manager.createSession(sessionId); + assertEquals("Session should be valid", true, session.isValid()); + assertEquals("Session id does not match", sessionId, session.getId()); + manager.createSession(sessionId); + manager.remove(session); + } + + @Test + public void testSessionCRUD() throws IOException{ + Session foundSession = manager.findSession(sessionId); + assertNull("Session should not exist here", foundSession); + Session session = manager.createSession(sessionId); + manager.add(session); + foundSession = manager.findSession(sessionId); + assertEquals("Session was not found, id does not match", sessionId, foundSession.getId()); + manager.remove(session); + foundSession = manager.findSession(sessionId); + assertEquals("Session was not removed", null, foundSession); + } + + @Test + public void testExpireSession() throws IOException{ + Session session = manager.createSession(sessionId); + session.setAuthType(AUTH_TYPE); + manager.add(session); + Session foundSession = manager.findSession(sessionId); + assertEquals("Session was not found", sessionId, foundSession.getId()); + manager.expireSession(sessionId); + foundSession = manager.findSession(sessionId); + assertEquals("Session was not expired", null, foundSession); + } + + @Test + public void testFindSessions(){ + Session session = manager.createSession(sessionId); + session.setAuthType(AUTH_TYPE); + manager.add(session); + Session[] sessions = manager.findSessions(); + assertEquals("Session array size does not match", 1, sessions.length); + assertEquals("Session array size does not match", sessionId, sessions[0].getId()); + manager.remove(session); + } + + @Test + public void testGetSession(){ + ClusterSession session = (ClusterSession) manager.createSession(sessionId); + session.setAttribute(ATTRIBUTE_NAME, AUTH_TYPE); + manager.add(session); + HashMap sessionAttributes = manager.getSession(sessionId); + assertNotNull("Session attribute should not be null", sessionAttributes); + assertEquals("Session attribute size does not match", 1, sessionAttributes.size()); + assertEquals("Session attribute size does not match", AUTH_TYPE, sessionAttributes.get(ATTRIBUTE_NAME)); + manager.remove(session); + } + + @AfterClass + public static void cleanup(){ + try { + manager.stopInternal(); + } catch (LifecycleException e) { + } + } + +} \ No newline at end of file diff --git a/opendaylight/samples/clustersession/src/test/java/org/opendaylight/controller/clustersession/ClusterSessionServiceImplTest.java b/opendaylight/samples/clustersession/src/test/java/org/opendaylight/controller/clustersession/ClusterSessionServiceImplTest.java new file mode 100644 index 0000000000..1835db5976 --- /dev/null +++ b/opendaylight/samples/clustersession/src/test/java/org/opendaylight/controller/clustersession/ClusterSessionServiceImplTest.java @@ -0,0 +1,162 @@ +package org.opendaylight.controller.clustersession; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.catalina.Context; +import org.apache.catalina.Session; +import org.apache.catalina.util.SessionIdGenerator; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.opendaylight.controller.clustering.services.IClusterGlobalServices; +import org.opendaylight.controller.clustersession.impl.ClusterSessionServiceImpl; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({FrameworkUtil.class}) +public class ClusterSessionServiceImplTest { + static ClusterSessionManager manager = null; + static ClusterSessionServiceImpl sessionService = null; + private static final String SESSION_CACHE = "customSessionManager.sessionData"; + static ConcurrentMap sessions = new ConcurrentHashMap(); + private String sessionId = "1234567"; + final String AUTH_TYPE = "FORM"; + final String ATTRIBUTE_NAME = "AuthType"; + + @SuppressWarnings("unchecked") + @BeforeClass + public static void init(){ + Bundle bundle = mock(Bundle.class); + BundleContext context = mock(BundleContext.class); + IClusterGlobalServices clusterGlobalService = mock(IClusterGlobalServices.class); + ServiceReference serviceReference = mock(ServiceReference.class); + PowerMockito.mockStatic(FrameworkUtil.class); + when(FrameworkUtil.getBundle(ClusterSessionManager.class)).thenReturn(bundle); + when(bundle.getBundleContext()).thenReturn(context); + when(context.getService(serviceReference)).thenReturn(clusterGlobalService); + when((ConcurrentMap)clusterGlobalService.getCache(SESSION_CACHE)).thenReturn(sessions); + Context containerContext = mock(Context.class); + manager = new ClusterSessionManager(); + sessionService = (ClusterSessionServiceImpl) manager.getSessionService(); + manager.setContainer(containerContext); + sessionService.startInternal(new SessionIdGenerator()); + sessionService.addingService(serviceReference); + } + + @Test + public void testCreateEmptySession(){ + Session session = sessionService.createEmptySession(); + assertEquals("session manager does not match", manager, session.getManager()); + } + + @Test + public void testCreateSessionwithRandomId(){ + Session session = sessionService.createSession(null); + assertEquals("Session should be valid", true, session.isValid()); + sessionService.removeSession(session.getId()); + } + + @Test + public void testCreateSession(){ + Session session = sessionService.createSession(sessionId); + assertEquals("Session should be valid", true, session.isValid()); + assertEquals("Session id does not match", sessionId, session.getId()); + sessionService.removeSession(sessionId); + } + + @Test + public void testNullfindSession() { + Session session = sessionService.findSession(null); + assertNull("Session should be null", session); + } + + @Test + public void testSessionCRUD(){ + Session foundSession = sessionService.findSession(sessionId); + assertNull("Session should not exist here", foundSession); + Session session = sessionService.createSession(sessionId); + foundSession = sessionService.findSession(sessionId); + assertEquals("Session was not added", sessionId, foundSession.getId()); + session.setAuthType(AUTH_TYPE); + sessionService.updateSession((ClusterSession)session); + foundSession = sessionService.findSession(sessionId); + assertEquals("Session was not found, id does not match", sessionId, foundSession.getId()); + assertEquals("Session was not found, auth type does match", AUTH_TYPE, foundSession.getAuthType()); + sessionService.removeSession(sessionId); + foundSession = sessionService.findSession(sessionId); + assertEquals("Session was not removed", null, foundSession); + } + + @Test + public void testExpireSession(){ + Session session = sessionService.createSession(sessionId); + session.setAuthType(AUTH_TYPE); + sessionService.addSession((ClusterSession)session); + Session foundSession = sessionService.findSession(sessionId); + assertEquals("Session was not found", sessionId, foundSession.getId()); + sessionService.expireSession(sessionId); + foundSession = sessionService.findSession(sessionId); + assertEquals("Session was not expired", null, foundSession); + } + + @Test + public void testFindSessions(){ + Session session = sessionService.createSession(sessionId); + session.setAuthType(AUTH_TYPE); + sessionService.addSession((ClusterSession)session); + Session[] sessions = sessionService.findSessions(); + assertEquals("Session array size does not match", 1, sessions.length); + assertEquals("Session array size does not match", sessionId, sessions[0].getId()); + sessionService.removeSession(sessionId); + } + + @Test + public void testGetSession(){ + ClusterSession session = (ClusterSession) sessionService.createSession(sessionId); + session.setAttribute(ATTRIBUTE_NAME, AUTH_TYPE); + HashMap sessionAttributes = sessionService.getSession(sessionId); + assertNotNull("Session attribute should not be null", sessionAttributes); + assertEquals("Session attribute size does not match", 1, sessionAttributes.size()); + assertEquals("Session attribute size does not match", AUTH_TYPE, sessionAttributes.get(ATTRIBUTE_NAME)); + sessionService.removeSession(sessionId); + } + + @Test + public void testNullSessionCache(){ + ClusterSessionManager clustermanager = new ClusterSessionManager(); + ClusterSessionServiceImpl service = new ClusterSessionServiceImpl(clustermanager); + Session session = service.findSession(sessionId); + assertNull("Session should be null, as cache is null", session); + Session[] sessions = service.findSessions(); + assertEquals("Session array should be empty", 0, sessions.length); + service.removeSession(sessionId); + service.expireSession(sessionId); + session = service.createSession(sessionId); + assertNull("Session should be null, as cache is null", session); + service.addSession(null); + Map attributes = service.getSession(sessionId); + assertNull("Attributes should be null, as cache is null", attributes); + service.updateSession(null); + } + + @AfterClass + public static void cleanup(){ + sessionService.stopInternal(); + } +} \ No newline at end of file diff --git a/opendaylight/samples/clustersession/src/test/java/org/opendaylight/controller/clustersession/ClusterSessionUtilTest.java b/opendaylight/samples/clustersession/src/test/java/org/opendaylight/controller/clustersession/ClusterSessionUtilTest.java new file mode 100644 index 0000000000..563b2be63c --- /dev/null +++ b/opendaylight/samples/clustersession/src/test/java/org/opendaylight/controller/clustersession/ClusterSessionUtilTest.java @@ -0,0 +1,156 @@ +package org.opendaylight.controller.clustersession; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.apache.catalina.authenticator.Constants; +import org.apache.catalina.authenticator.SavedRequest; +import org.apache.catalina.realm.GenericPrincipal; +import org.junit.Test; +import org.opendaylight.controller.clustersession.impl.ClusterSessionServiceImpl; +import org.opendaylight.controller.clustersession.service.ClusterSessionService; + + +public class ClusterSessionUtilTest { + + final String AUTH_TYPE = "FORM"; + final String ADMIN = "admin"; + final String REQUEST_URI = "/test"; + final String BLANK = ""; + final String HEADER_NAME = "ContentType"; + final String HEADER_VALUE = "JSON"; + final long creationTime = 54545454L; + final int interval = 0; + ClusterSessionManager manager = new ClusterSessionManager(); + ClusterSessionService sessionService = new ClusterSessionServiceImpl(manager); + + @Test + public void testNullSerializableClusterSession() { + ClusterSessionData sessionData = ClusterSessionUtil.getSerializableSession(null); + assertEquals("Session data should be null for null session", null, sessionData); + } + + @Test + public void testSerializableClusterSession() { + ClusterSession customSession = createClusterSesion(); + ClusterSessionData sessionData = ClusterSessionUtil.getSerializableSession(customSession); + assertEquals("Session authentication type not valid", AUTH_TYPE, sessionData.getAuthType()); + assertEquals("Session username does not match", ADMIN, sessionData.getUserName()); + assertEquals("Session password does not match", ADMIN, sessionData.getPassword()); + assertEquals("Session prinicpal does not match", ADMIN, sessionData.getSession().getPrincipal().getName()); + } + + @Test + public void testNullDeserialzableclusterSession() { + ClusterSession session = ClusterSessionUtil.getDeserializedSession(null, sessionService, manager); + assertEquals("Session should be null for null session data", null, session); + } + + @Test + public void testDeserializableClusterSesion() { + ClusterSession customSession = createClusterSesion(); + ClusterSessionData sessionData = ClusterSessionUtil.getSerializableSession(customSession); + customSession = sessionData.getSession(); + customSession.setAuthType(AUTH_TYPE); + customSession.setNote(Constants.FORM_PRINCIPAL_NOTE, BLANK); + customSession.setNote(Constants.FORM_REQUEST_NOTE, BLANK); + ClusterSession session = ClusterSessionUtil.getDeserializedSession(sessionData, sessionService, manager); + assertEquals("Session authentication type not valid", AUTH_TYPE, session.getAuthType()); + assertEquals("prinicpal name is not valid", ADMIN, session.getPrincipal().getName()); + SavedRequest savedRequest = (SavedRequest)session.getNote(Constants.FORM_REQUEST_NOTE); + assertEquals("saved request uri does not match", REQUEST_URI, savedRequest.getRequestURI()); + assertEquals("saved request header does not match", HEADER_VALUE, savedRequest.getHeaderValues(HEADER_NAME).next()); + assertEquals("saved request header does not match", Locale.ENGLISH, savedRequest.getLocales().next()); + String username = (String)session.getNote(Constants.FORM_USERNAME); + assertEquals("username does not match", ADMIN, username); + String password = (String)session.getNote(Constants.FORM_PASSWORD); + assertEquals("password does not match", ADMIN, password); + assertEquals("session manager does not match", manager, session.getManager()); + assertEquals("session creation time does not match", creationTime, session.getCreationTime()); + assertEquals("session man inactive interval does not match", interval, session.getMaxInactiveInterval()); + assertEquals("is session new does not match", true, session.isNew()); + assertEquals("is session valid does not match", true, session.isValid()); + } + + @Test + public void testSerializationtoFile(){ + ClusterSession customSession = createClusterSesion(); + ClusterSessionData sessionData = ClusterSessionUtil.getSerializableSession(customSession); + try( + OutputStream file = new FileOutputStream("sessionData.ser"); + OutputStream buffer = new BufferedOutputStream(file); + ObjectOutput output = new ObjectOutputStream(buffer); + ){ + output.writeObject(sessionData); + } + catch(IOException ex){ + fail("IO exception while serializing object to a file."); + } + try( + InputStream file = new FileInputStream("sessionData.ser"); + InputStream buffer = new BufferedInputStream(file); + ObjectInput input = new ObjectInputStream (buffer); + ){ + //deserialize the session + ClusterSessionData recovedSession = (ClusterSessionData)input.readObject(); + //display its data + ClusterSession session = ClusterSessionUtil.getDeserializedSession(recovedSession, sessionService, manager); + assertEquals("Session authentication type not valid", AUTH_TYPE, session.getAuthType()); + assertEquals("prinicpal name is not valid", ADMIN, session.getPrincipal().getName()); + SavedRequest savedRequest = (SavedRequest)session.getNote(Constants.FORM_REQUEST_NOTE); + assertEquals("saved request uri is not valid", REQUEST_URI, savedRequest.getRequestURI()); + assertEquals("saved request header does not match", HEADER_VALUE, savedRequest.getHeaderValues(HEADER_NAME).next()); + assertEquals("saved request header does not match", Locale.ENGLISH, savedRequest.getLocales().next()); + String username = (String)session.getNote(Constants.FORM_USERNAME); + assertEquals("username does not match", ADMIN, username); + String password = (String)session.getNote(Constants.FORM_PASSWORD); + assertEquals("password does not match", ADMIN, password); + } + catch(ClassNotFoundException ex){ + fail("Exception in object deserialization from file"); + } + catch(IOException ex){ + fail("Exception in object deserialization from file"); + } + File serializedFile = new File("sessionData.ser"); + serializedFile.delete(); + } + + private ClusterSession createClusterSesion(){ + ClusterSession clusterSession = new ClusterSession(manager, sessionService); + clusterSession.setAuthType(AUTH_TYPE); + clusterSession.setCreationTime(creationTime); + clusterSession.setMaxInactiveInterval(interval); + clusterSession.setNew(true); + clusterSession.setValid(true); + List roles = new ArrayList(); + roles.add(ADMIN); + GenericPrincipal principal = new GenericPrincipal(ADMIN, ADMIN, roles); + clusterSession.setPrincipal(principal); + clusterSession.setNote(Constants.FORM_PRINCIPAL_NOTE, principal); + SavedRequest savedRequest = new SavedRequest(); + savedRequest.setRequestURI(REQUEST_URI); + savedRequest.addHeader(HEADER_NAME, HEADER_VALUE); + savedRequest.addLocale(Locale.ENGLISH); + clusterSession.setNote(Constants.FORM_REQUEST_NOTE, savedRequest); + clusterSession.setNote(Constants.FORM_USERNAME, ADMIN); + clusterSession.setNote(Constants.FORM_PASSWORD, ADMIN); + return clusterSession; + } +}