}
if (potential == null) {
- throw new DataNormalizationException(String.format("Supplied QName %s is not valid according to schema %s", child, schema));
+ throw new DataNormalizationException(String.format("Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema,schema.getChildNodes()));
}
if ((schema instanceof DataSchemaNode) && !((DataSchemaNode) schema).isAugmenting() && potential.isAugmenting()) {
}
+ @Override
+ public String toString() {
+ return "ChangeListenerNotifyTask [listeners=" + listeners + ", event=" + event + "]";
+ }
+
}
checkState(success, "Store snapshot and transaction snapshot differ. This should never happen.");
for (ChangeListenerNotifyTask task : listenerResolver.call()) {
+ LOG.trace("Scheduling invocation of listeners: {}",task);
executor.submit(task);
}
}
@Override
public void write(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
checkNotReady();
- mutableTree.write(path, data);
+ try {
+ LOG.trace("Tx: {} Write: {}:{}",getIdentifier(),path,data);
+ mutableTree.write(path, data);
+ // FIXME: Add checked exception
+ } catch (Exception e) {
+ LOG.error("Tx: {}, failed to write {}:{} in {}",getIdentifier(),path,data,mutableTree,e);
+ }
}
@Override
public void delete(final InstanceIdentifier path) {
checkNotReady();
- mutableTree.delete(path);
+ try {
+ LOG.trace("Tx: {} Delete: {}",getIdentifier(),path);
+ mutableTree.delete(path);
+ // FIXME: Add checked exception
+ } catch (Exception e) {
+ LOG.error("Tx: {}, failed to delete {} in {}",getIdentifier(),path,mutableTree,e);
+ }
}
protected final boolean isReady() {
@Override
public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
- return Futures.immediateFuture(getMutatedView().read(path));
+ LOG.trace("Tx: {} Read: {}",getIdentifier(),path);
+ try {
+ return Futures.immediateFuture(getMutatedView().read(path));
+ } catch (Exception e) {
+ LOG.error("Tx: {} Failed Read of {}",getIdentifier(),path,e);
+ throw e;
+ }
}
}
return resolveModificationStrategy(path).apply(modification, modification.getOriginal(),
StoreUtils.increase(snapshot.getMetadataTree().getSubtreeVersion()));
} catch (Exception e) {
- LOG.error("Could not create snapshot for {}", path,e);
+ LOG.error("Could not create snapshot for {}:{}", path,modification,e);
throw e;
}
}
import com.google.common.base.Function;
import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
case DELETE:
return modification.storeSnapshot(Optional.<StoreMetadataNode>absent());
case SUBTREE_MODIFIED:
+ Preconditions.checkArgument(currentMeta.isPresent(),"Metadata not available for modification",modification);
return modification.storeSnapshot(Optional.of(applySubtreeChange(modification, currentMeta.get(), subtreeVersion)));
case WRITE:
return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, subtreeVersion)));
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustersession</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>javax.servlet</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.catalina</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.catalina.ha</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.coyote</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.juli.extras</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.tomcat.api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.tomcat.util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.5.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-api-mockito</artifactId>
+ <version>1.5.4</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-module-junit4</artifactId>
+ <version>1.5.4</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.4.0</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Fragment-Host>org.eclipse.gemini.web.tomcat</Fragment-Host>
+ <Export-Package>org.opendaylight.controller.clustersession</Export-Package>
+ <Import-Package>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</Import-Package>
+ </instructions>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+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<SessionListener>();
+ }
+ if (notes == null){
+ notes = new ConcurrentHashMap<String, Object>();
+ }
+ 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);
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+/*
+ * 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
+ * <code><description>/<version></code>.
+ */
+ @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<String, String> 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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<String, List<String>>) ois.readObject());
+ setLocales(savedRequest, (List<Locale>) 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<Locale> locales) {
+ if(locales != null && !locales.isEmpty()) {
+ for (final Locale locale : locales) {
+ savedRequest.addLocale(locale);
+ }
+ }
+ }
+
+ private static <T> List<T> newArrayList(final Iterator<T> iter) {
+ if(!iter.hasNext()) {
+ return Collections.emptyList();
+ }
+ final List<T> result = new ArrayList<T>();
+ while (iter.hasNext()) {
+ result.add(iter.next());
+ }
+ return result;
+ }
+
+ private static Map<String, List<String>> getHeaders(final SavedRequest obj) {
+ final Map<String, List<String>> result = new HashMap<String, List<String>>();
+ final Iterator<String> namesIter = obj.getHeaderNames();
+ while (namesIter.hasNext()) {
+ final String name = namesIter.next();
+ final List<String> values = new ArrayList<String>();
+ result.put(name, values);
+ final Iterator<String> 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<String, List<String>> headers) {
+ if(headers != null) {
+ for (final Entry<String, List<String>> entry : headers.entrySet()) {
+ final List<String> 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());
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+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<String, String> getSession(String sessionId);
+
+ /**
+ * update the session object in persistence
+ * @param session
+ */
+ void updateSession(final ClusterSession session);
+
+}
--- /dev/null
+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<String, ClusterSessionData> sessions = new ConcurrentHashMap<String, ClusterSessionData>();
+ 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<IClusterGlobalServices> 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<String, ClusterSessionData>)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<String, String> 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
--- /dev/null
+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<String, ClusterSessionData> sessions = new ConcurrentHashMap<String, ClusterSessionData>();
+ 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<IClusterGlobalServices> 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<String, ClusterSessionData>)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<String, String> 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<String,String> 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
--- /dev/null
+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<String> roles = new ArrayList<String>();
+ 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;
+ }
+}