package org.ops4j.pax.web.service.jetty.wadi;
import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;
import org.codehaus.wadi.aop.replication.AOPStackContext;
import org.codehaus.wadi.core.assembler.StackContext;
import org.codehaus.wadi.core.manager.Manager;
import org.codehaus.wadi.core.manager.SessionAlreadyExistException;
import org.codehaus.wadi.core.manager.SessionMonitor;
import org.codehaus.wadi.core.store.Store;
import org.codehaus.wadi.replication.strategy.BackingStrategyFactory;
import org.codehaus.wadi.replication.strategy.RoundRobinBackingStrategyFactory;
import org.codehaus.wadi.servicespace.ServiceSpace;
import org.codehaus.wadi.servicespace.ServiceSpaceName;
import org.mortbay.jetty.servlet.AbstractSessionManager;
import org.mortbay.jetty.servlet.HashSessionIdManager;
import org.mortbay.jetty.servlet.wadi.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* @author <a href="mailto:sgonzalez@atricore.org">Sebastian Gonzalez Oyuela</a>
* @version $Id$
*/
public class OsgiWadiSessionManager extends AbstractSessionManager
{
private final Map<String, ClusteredSession> _idToSession = new HashMap<String, ClusteredSession>();
private BackingStrategyFactory _backingStrategyFactory;
private WadiCluster _wadiCluster;
private CopyOnWriteArrayList _listeners;
private Manager _manager;
private SessionMonitor _sessionMonitor;
private ServiceSpace _serviceSpace;
private int _nbReplica;
private int _numPartitions;
private int _sweepInterval;
private final boolean _enableReplication;
private final boolean _deltaReplication;
private Store _sharedStore;
private ClassLoader _classLoader;
public OsgiWadiSessionManager(ClassLoader cl, WadiCluster wadiCluster, int numPartitions, int sweepInterval)
{
this(cl, wadiCluster, 0, numPartitions, sweepInterval, false, false);
}
public OsgiWadiSessionManager(ClassLoader cl, WadiCluster wadiCluster, int nbReplica, int numPartitions, int sweepInterval)
{
this(cl, wadiCluster, nbReplica, numPartitions, sweepInterval, true, false);
}
/**
* Constructs a session manager attached to the provided cluster.
*
* @param wadiCluster cluster this session manager is attached to.
* @param nbReplica number of replicas to be maintained for each sessions managed by this session manager. When a
* session is created or updated, it is replicated to a configurable number of other nodes.
* @param numPartitions WADI maintains a distributed and indexed look-up map tracking HttpSession locations.
* numPartitions is the number of indexes of this look-up map. A standard number of partitions is 24.
* @param sweepInterval number of seconds between the execution of HttpSession eviction policies. In other words,
* it is how often HttpSessions are scanned to identify and invalidate timed out sessions
* @param enableReplication if true, then sessions are replicated to other nodes. The number of replicas is
* configured by nbReplica.
*/
public OsgiWadiSessionManager(ClassLoader cl, WadiCluster wadiCluster,
int nbReplica,
int numPartitions,
int sweepInterval,
boolean enableReplication,
boolean deltaReplication)
{
_classLoader = cl;
_wadiCluster = wadiCluster;
_nbReplica = nbReplica;
_numPartitions = numPartitions;
_sweepInterval = sweepInterval;
_enableReplication = enableReplication;
_deltaReplication = deltaReplication;
_listeners = new CopyOnWriteArrayList();
HashSessionIdManager sessionIdManager = new HashSessionIdManager();
sessionIdManager.setWorkerName(wadiCluster.getNodeName());
setIdManager(sessionIdManager);
registerListener(new MigrationListener());
}
public Store getSharedStore()
{
return _sharedStore;
}
public void setSharedStore(Store store)
{
_sharedStore = store;
}
public void doStart() throws Exception
{
super.doStart();
_backingStrategyFactory = new RoundRobinBackingStrategyFactory(_nbReplica);
ServiceSpaceName serviceSpaceName = new ServiceSpaceName(new URI(_context.getContextPath() + "/"));
StackContext stackContext;
if (_deltaReplication) {
stackContext = new AOPStackContext(_classLoader != null ? _classLoader : Thread.currentThread().getContextClassLoader(),
serviceSpaceName,
_wadiCluster.getDispatcher(),
_dftMaxIdleSecs,
_numPartitions,
_sweepInterval,
_backingStrategyFactory);
} else {
stackContext = new StackContext(_classLoader != null ? _classLoader : Thread.currentThread().getContextClassLoader(),
serviceSpaceName,
_wadiCluster.getDispatcher(),
_dftMaxIdleSecs,
_numPartitions,
_sweepInterval,
_backingStrategyFactory);
}
stackContext.setSharedStore(_sharedStore);
stackContext.setDisableReplication(!_enableReplication);
stackContext.build();
_serviceSpace = stackContext.getServiceSpace();
_manager = stackContext.getManager();
_sessionMonitor = stackContext.getSessionMonitor();
_sessionMonitor.addSessionListener(new SessionListenerAdapter());
_serviceSpace.start();
}
public void doStop() throws Exception
{
_serviceSpace.stop();
super.doStop();
}
public Manager getClusteredManager()
{
return _manager;
}
public WadiSession createSession(String sessionId) throws SessionAlreadyExistsException
{
org.codehaus.wadi.core.session.Session session;
try
{
session = _manager.createWithName(sessionId);
}
catch (SessionAlreadyExistException e)
{
throw new SessionAlreadyExistsException(sessionId);
}
return new WadiSessionAdaptor(session);
}
public void registerListener(SessionListener listener)
{
_listeners.add(listener);
}
public void unregisterListener(SessionListener listener)
{
_listeners.remove(listener);
}
private void notifyInboundSessionMigration(org.codehaus.wadi.core.session.Session session)
{
for (Iterator iter = _listeners.iterator(); iter.hasNext();)
{
SessionListener listener = (SessionListener) iter.next();
listener.notifyInboundSessionMigration(new WadiSessionAdaptor(session));
}
}
private void notifyOutboundSessionMigration(org.codehaus.wadi.core.session.Session session)
{
for (Iterator iter = _listeners.iterator(); iter.hasNext();)
{
SessionListener listener = (SessionListener) iter.next();
listener.notifyOutboundSessionMigration(new WadiSessionAdaptor(session));
}
}
private void notifySessionDestruction(org.codehaus.wadi.core.session.Session session)
{
for (Iterator iter = _listeners.iterator(); iter.hasNext();)
{
SessionListener listener = (SessionListener) iter.next();
listener.notifySessionDestruction(new WadiSessionAdaptor(session));
}
}
private class SessionListenerAdapter implements org.codehaus.wadi.core.manager.SessionListener
{
public void onSessionCreation(org.codehaus.wadi.core.session.Session session) {
}
public void onSessionDestruction(org.codehaus.wadi.core.session.Session session) {
notifySessionDestruction(session);
}
public void onInboundSessionMigration(org.codehaus.wadi.core.session.Session session) {
notifyInboundSessionMigration(session);
}
public void onOutbountSessionMigration(org.codehaus.wadi.core.session.Session session) {
notifyOutboundSessionMigration(session);
}
}
protected Session newSession(HttpServletRequest request)
{
return new ClusteredSession(request);
}
public void complete(HttpSession session)
{
ClusteredSession clusteredSession = (ClusteredSession) session;
clusteredSession.session.onEndAccess();
}
protected void addSession(Session session)
{
ClusteredSession clusteredSession = (ClusteredSession) session;
synchronized (_idToSession)
{
_idToSession.put(clusteredSession.getClusterId(), clusteredSession);
}
}
protected void removeSession(String idInCluster)
{
// Let MigrationListener handle session removal
}
public Session getSession(String idInCluster)
{
synchronized (_idToSession)
{
return _idToSession.get(idInCluster);
}
}
public int getSessions()
{
synchronized (_idToSession)
{
return _idToSession.size();
}
}
public Map getSessionMap()
{
throw new AssertionError("getSessionMap is never used.");
}
protected void invalidateSessions()
{
}
private class MigrationListener implements SessionListener
{
public void notifyInboundSessionMigration(WadiSession session)
{
addSession(new ClusteredSession(session), false);
}
public void notifyOutboundSessionMigration(WadiSession session)
{
ClusteredSession clusteredSession = getClusteredSession(session);
removeSession(clusteredSession, false);
}
public void notifySessionDestruction(WadiSession session)
{
ClusteredSession clusteredSession = getClusteredSession(session);
removeSession(clusteredSession, true);
}
private ClusteredSession getClusteredSession(WadiSession session) throws AssertionError
{
ClusteredSession clusteredSession;
synchronized (_idToSession)
{
clusteredSession = _idToSession.remove(session.getSessionId());
}
if (null == clusteredSession)
{
throw new AssertionError("Session [" + session + "] is undefined");
}
return clusteredSession;
}
}
public class ClusteredSession extends Session
{
private final WadiSession session;
protected ClusteredSession(HttpServletRequest request)
{
super(request);
try
{
this.session = createSession(getClusterId());
}
catch (SessionAlreadyExistsException e)
{
throw (IllegalStateException) new IllegalStateException().initCause(e);
}
initValues();
}
protected ClusteredSession(WadiSession session)
{
super(System.currentTimeMillis(), session.getSessionId());
this.session = session;
initValues();
}
protected Map newAttributeMap()
{
return session.getState();
}
protected String getClusterId()
{
return super.getClusterId();
}
public void invalidate() throws IllegalStateException
{
super.doInvalidate();
session.release();
}
}
}