410750 Mongo clustered sessions persisted across stops
This commit is contained in:
parent
e92f44ed73
commit
092c53b335
|
@ -45,7 +45,6 @@ public class NoSqlSession extends AbstractSession
|
|||
{
|
||||
super(manager, request);
|
||||
_manager=manager;
|
||||
save(true);
|
||||
_active.incrementAndGet();
|
||||
}
|
||||
|
||||
|
@ -88,6 +87,14 @@ public class NoSqlSession extends AbstractSession
|
|||
_dirty.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void timeout() throws IllegalStateException
|
||||
{
|
||||
super.timeout();
|
||||
}
|
||||
|
||||
/*
|
||||
* a boolean version of the setAttribute method that lets us manage the _dirty set
|
||||
|
@ -125,7 +132,7 @@ public class NoSqlSession extends AbstractSession
|
|||
@Override
|
||||
protected boolean access(long time)
|
||||
{
|
||||
__log.debug("NoSqlSession:access:active "+_active);
|
||||
__log.debug("NoSqlSession:access:active {} time {}", _active, time);
|
||||
if (_active.incrementAndGet()==1)
|
||||
{
|
||||
long period=_manager.getStalePeriod()*1000L;
|
||||
|
@ -169,6 +176,7 @@ public class NoSqlSession extends AbstractSession
|
|||
protected void doInvalidate() throws IllegalStateException
|
||||
{
|
||||
super.doInvalidate();
|
||||
//jb why save here? if the session is invalidated it should be removed
|
||||
save(false);
|
||||
}
|
||||
|
||||
|
@ -232,7 +240,4 @@ public class NoSqlSession extends AbstractSession
|
|||
{
|
||||
super.setNodeId(nodeId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.nosql;
|
|||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
@ -30,6 +31,12 @@ import org.eclipse.jetty.server.session.AbstractSessionManager;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* NoSqlSessionManager
|
||||
*
|
||||
* Base class for SessionManager implementations using nosql frameworks
|
||||
*
|
||||
*/
|
||||
public abstract class NoSqlSessionManager extends AbstractSessionManager implements SessionManager
|
||||
{
|
||||
private final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session");
|
||||
|
@ -40,11 +47,11 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
|||
private int _savePeriod=0;
|
||||
private int _idlePeriod=-1;
|
||||
private boolean _invalidateOnStop;
|
||||
private boolean _preserveOnStop;
|
||||
private boolean _preserveOnStop = true;
|
||||
private boolean _saveAllAttributes;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* (non-Javadoc)
|
||||
/**
|
||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
|
||||
*/
|
||||
@Override
|
||||
|
@ -59,7 +66,12 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
|||
protected void addSession(AbstractSession session)
|
||||
{
|
||||
if (isRunning())
|
||||
{
|
||||
//add into memory
|
||||
_sessions.put(session.getClusterId(),(NoSqlSession)session);
|
||||
//add into db
|
||||
((NoSqlSession)session).save(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -67,15 +79,16 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
|||
public AbstractSession getSession(String idInCluster)
|
||||
{
|
||||
NoSqlSession session = _sessions.get(idInCluster);
|
||||
|
||||
__log.debug("getSession: " + session );
|
||||
__log.debug("getSession {} ", session );
|
||||
|
||||
if (session==null)
|
||||
{
|
||||
//session not in this node's memory, load it
|
||||
session=loadSession(idInCluster);
|
||||
|
||||
if (session!=null)
|
||||
{
|
||||
//session exists, check another request thread hasn't loaded it too
|
||||
NoSqlSession race=_sessions.putIfAbsent(idInCluster,session);
|
||||
if (race!=null)
|
||||
{
|
||||
|
@ -83,54 +96,83 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
|||
session.clearAttributes();
|
||||
session=race;
|
||||
}
|
||||
else
|
||||
__log.debug("session loaded ", idInCluster);
|
||||
|
||||
//check if the session we just loaded has actually expired, maybe while we weren't running
|
||||
if (getMaxInactiveInterval() > 0 && session.getAccessed() > 0 && ((getMaxInactiveInterval()*1000)+session.getAccessed()) < System.currentTimeMillis())
|
||||
{
|
||||
__log.debug("session expired ", idInCluster);
|
||||
expire(idInCluster);
|
||||
session = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
__log.debug("session does not exist {}", idInCluster);
|
||||
}
|
||||
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void invalidateSessions() throws Exception
|
||||
protected void shutdownSessions() throws Exception
|
||||
{
|
||||
// Invalidate all sessions to cause unbind events
|
||||
//If we are stopping, and we're preserving sessions, then we want to
|
||||
//save all of the sessions (including those that have been added during this method call)
|
||||
//and then just remove them from memory.
|
||||
|
||||
//If we don't wish to preserve sessions and we're stopping, then we should invalidate
|
||||
//the session (which may remove it).
|
||||
long gracefulStopMs = getContextHandler().getServer().getStopTimeout();
|
||||
long stopTime = 0;
|
||||
if (gracefulStopMs > 0)
|
||||
stopTime = System.nanoTime() + (TimeUnit.NANOSECONDS.convert(gracefulStopMs, TimeUnit.MILLISECONDS));
|
||||
|
||||
ArrayList<NoSqlSession> sessions=new ArrayList<NoSqlSession>(_sessions.values());
|
||||
int loop=100;
|
||||
while (sessions.size()>0 && loop-->0)
|
||||
{
|
||||
// If we are called from doStop
|
||||
if (isStopping())
|
||||
{
|
||||
// Then we only save and remove the session - it is not invalidated.
|
||||
for (NoSqlSession session : sessions)
|
||||
{
|
||||
session.save(false);
|
||||
|
||||
if (!_preserveOnStop) {
|
||||
removeSession(session,false);
|
||||
}
|
||||
// loop while there are sessions, and while there is stop time remaining, or if no stop time, just 1 loop
|
||||
while (sessions.size() > 0 && ((stopTime > 0 && (System.nanoTime() < stopTime)) || (stopTime == 0)))
|
||||
{
|
||||
for (NoSqlSession session : sessions)
|
||||
{
|
||||
if (isPreserveOnStop())
|
||||
{
|
||||
//we don't want to delete the session, so save the session
|
||||
//and remove from memory
|
||||
session.save(false);
|
||||
_sessions.remove(session.getClusterId());
|
||||
}
|
||||
else
|
||||
{
|
||||
//invalidate the session so listeners will be called and also removes the session
|
||||
session.invalidate();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (NoSqlSession session : sessions)
|
||||
session.invalidate();
|
||||
}
|
||||
|
||||
// check that no new sessions were created while we were iterating
|
||||
//check if we should terminate our loop if we're not using the stop timer
|
||||
if (stopTime == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// Get any sessions that were added by other requests during processing and go around the loop again
|
||||
sessions=new ArrayList<NoSqlSession>(_sessions.values());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected AbstractSession newSession(HttpServletRequest request)
|
||||
{
|
||||
long created=System.currentTimeMillis();
|
||||
return new NoSqlSession(this,request);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Remove the session from the in-memory list for this context.
|
||||
* Also remove the context sub-document for this session id from the db.
|
||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
protected boolean removeSession(String idInCluster)
|
||||
{
|
||||
|
@ -147,7 +189,7 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
__log.warn("Problem deleting session id=" + idInCluster,e);
|
||||
__log.warn("Problem deleting session {}", idInCluster,e);
|
||||
}
|
||||
|
||||
return session != null;
|
||||
|
@ -155,31 +197,52 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void invalidateSession( String idInCluster )
|
||||
protected void expire( String idInCluster )
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
NoSqlSession session = _sessions.remove(idInCluster);
|
||||
//get the session from memory
|
||||
NoSqlSession session = _sessions.get(idInCluster);
|
||||
|
||||
try
|
||||
{
|
||||
if (session == null)
|
||||
{
|
||||
//we need to expire the session with its listeners, so load it
|
||||
session = loadSession(idInCluster);
|
||||
}
|
||||
|
||||
if (session != null)
|
||||
session.timeout();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
__log.warn("Problem expiring session {}", idInCluster,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void invalidateSession (String idInCluster)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
NoSqlSession session = _sessions.get(idInCluster);
|
||||
try
|
||||
{
|
||||
__log.debug("invalidating session {}", idInCluster);
|
||||
if (session != null)
|
||||
{
|
||||
remove(session);
|
||||
session.invalidate();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
__log.warn("Problem deleting session id=" + idInCluster,e);
|
||||
__log.warn("Problem invalidating session {}", idInCluster,e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ought we not go to cluster and mark it invalid?
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
|
|
@ -48,16 +48,15 @@ import com.mongodb.Mongo;
|
|||
import com.mongodb.MongoException;
|
||||
|
||||
/**
|
||||
* Based partially on the jdbc session id manager...
|
||||
* Based partially on the JDBCSessionIdManager.
|
||||
*
|
||||
* Theory is that we really only need the session id manager for the local
|
||||
* instance so we have something to scavenge on, namely the list of known ids
|
||||
*
|
||||
* this class has a timer that runs at the scavenge delay that runs a query
|
||||
* for all id's known to this node and that have and old accessed value greater
|
||||
* then the scavengeDelay.
|
||||
* This class has a timer that runs a periodic scavenger thread to query
|
||||
* for all id's known to this node whose precalculated expiry time has passed.
|
||||
*
|
||||
* these found sessions are then run through the invalidateAll(id) method that
|
||||
* These found sessions are then run through the invalidateAll(id) method that
|
||||
* is a bit hinky but is supposed to notify all handlers this id is now DOA and
|
||||
* ought to be cleaned up. this ought to result in a save operation on the session
|
||||
* that will change the valid field to false (this conjecture is unvalidated atm)
|
||||
|
@ -150,27 +149,28 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
protected void scavenge()
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
__log.debug("SessionIdManager:scavenge:at " + now);
|
||||
__log.debug("SessionIdManager:scavenge:at {}", now);
|
||||
synchronized (_sessionsIds)
|
||||
{
|
||||
/*
|
||||
* run a query returning results that:
|
||||
* - are in the known list of sessionIds
|
||||
* - have an accessed time less then current time - the scavenger period
|
||||
* - the expiry time has passed
|
||||
*
|
||||
* we limit the query to return just the __ID so we are not sucking back full sessions
|
||||
*/
|
||||
BasicDBObject query = new BasicDBObject();
|
||||
query.put(MongoSessionManager.__ID,new BasicDBObject("$in", _sessionsIds ));
|
||||
|
||||
query.put(MongoSessionManager.__ACCESSED, new BasicDBObject("$lt",now - _scavengePeriod));
|
||||
query.put(MongoSessionManager.__EXPIRY, new BasicDBObject("$gt", 0));
|
||||
query.put(MongoSessionManager.__EXPIRY, new BasicDBObject("$lt", now));
|
||||
|
||||
|
||||
DBCursor checkSessions = _sessions.find(query, new BasicDBObject(MongoSessionManager.__ID, 1));
|
||||
|
||||
for ( DBObject session : checkSessions )
|
||||
{
|
||||
__log.debug("SessionIdManager:scavenge: invalidating " + (String)session.get(MongoSessionManager.__ID));
|
||||
invalidateAll((String)session.get(MongoSessionManager.__ID));
|
||||
__log.debug("SessionIdManager:scavenge: expiring session {}", (String)session.get(MongoSessionManager.__ID));
|
||||
expireAll((String)session.get(MongoSessionManager.__ID));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,12 +178,10 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* ScavengeFully is a process that periodically checks the tracked session
|
||||
* ids of this given instance of the session id manager to see if they
|
||||
* are past the point of expiration.
|
||||
* ScavengeFully will expire all sessions. In most circumstances
|
||||
* you should never need to call this method.
|
||||
*
|
||||
* NOTE: this is potentially devastating and may lead to serious session
|
||||
* coherence issues, not to be used in a running cluster
|
||||
* <b>USE WITH CAUTION</b>
|
||||
*/
|
||||
protected void scavengeFully()
|
||||
{
|
||||
|
@ -193,7 +191,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
|
||||
for (DBObject session : checkSessions)
|
||||
{
|
||||
invalidateAll((String)session.get(MongoSessionManager.__ID));
|
||||
expireAll((String)session.get(MongoSessionManager.__ID));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -205,7 +203,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
*
|
||||
* There are two checks being done here:
|
||||
*
|
||||
* - if the accessed time is older then the current time minus the purge invalid age
|
||||
* - if the accessed time is older than the current time minus the purge invalid age
|
||||
* and it is no longer valid then remove that session
|
||||
* - if the accessed time is older then the current time minus the purge valid age
|
||||
* then we consider this a lost record and remove it
|
||||
|
@ -229,7 +227,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
{
|
||||
String id = (String)session.get("id");
|
||||
|
||||
__log.debug("MongoSessionIdManager:purging invalid " + id);
|
||||
__log.debug("MongoSessionIdManager:purging invalid session {}", id);
|
||||
|
||||
_sessions.remove(session);
|
||||
}
|
||||
|
@ -247,7 +245,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
{
|
||||
String id = (String)session.get(MongoSessionManager.__ID);
|
||||
|
||||
__log.debug("MongoSessionIdManager:purging valid " + id);
|
||||
__log.debug("MongoSessionIdManager:purging valid session {}", id);
|
||||
|
||||
_sessions.remove(session);
|
||||
}
|
||||
|
@ -264,7 +262,6 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
protected void purgeFully()
|
||||
{
|
||||
BasicDBObject invalidQuery = new BasicDBObject();
|
||||
|
||||
invalidQuery.put(MongoSessionManager.__VALID, false);
|
||||
|
||||
DBCursor oldSessions = _sessions.find(invalidQuery, new BasicDBObject(MongoSessionManager.__ID, 1));
|
||||
|
@ -273,7 +270,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
{
|
||||
String id = (String)session.get(MongoSessionManager.__ID);
|
||||
|
||||
__log.debug("MongoSessionIdManager:purging invalid " + id);
|
||||
__log.debug("MongoSessionIdManager:purging invalid session {}", id);
|
||||
|
||||
_sessions.remove(session);
|
||||
}
|
||||
|
@ -302,6 +299,8 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* The delay before the first scavenge operation is performed.
|
||||
*
|
||||
* sets the scavengeDelay
|
||||
*/
|
||||
public void setScavengeDelay(long scavengeDelay)
|
||||
|
@ -314,6 +313,11 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* The period in seconds between scavenge checks.
|
||||
*
|
||||
* @param scavengePeriod
|
||||
*/
|
||||
public void setScavengePeriod(long scavengePeriod)
|
||||
{
|
||||
if (scavengePeriod <= 0)
|
||||
|
@ -448,7 +452,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* is the session id known to mongo, and is it valid
|
||||
* Searches database to find if the session id known to mongo, and is it valid
|
||||
*/
|
||||
@Override
|
||||
public boolean idInUse(String sessionId)
|
||||
|
@ -461,11 +465,10 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
if ( o != null )
|
||||
{
|
||||
Boolean valid = (Boolean)o.get(MongoSessionManager.__VALID);
|
||||
|
||||
if ( valid == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
@ -486,7 +489,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
* already a part of the index in mongo...
|
||||
*/
|
||||
|
||||
__log.debug("MongoSessionIdManager:addSession:" + session.getId());
|
||||
__log.debug("MongoSessionIdManager:addSession {}", session.getId());
|
||||
|
||||
synchronized (_sessionsIds)
|
||||
{
|
||||
|
@ -511,14 +514,18 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Remove the session id from the list of in-use sessions.
|
||||
* Inform all other known contexts that sessions with the same id should be
|
||||
* invalidated.
|
||||
* @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void invalidateAll(String sessionId)
|
||||
{
|
||||
synchronized (_sessionsIds)
|
||||
{
|
||||
_sessionsIds.remove(sessionId);
|
||||
|
||||
|
||||
|
||||
//tell all contexts that may have a session object with this id to
|
||||
//get rid of them
|
||||
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
|
||||
|
@ -537,6 +544,38 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Expire this session for all contexts that are sharing the session
|
||||
* id.
|
||||
* @param sessionId
|
||||
*/
|
||||
public void expireAll (String sessionId)
|
||||
{
|
||||
synchronized (_sessionsIds)
|
||||
{
|
||||
_sessionsIds.remove(sessionId);
|
||||
|
||||
|
||||
//tell all contexts that may have a session object with this id to
|
||||
//get rid of them
|
||||
Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
|
||||
for (int i=0; contexts!=null && i<contexts.length; i++)
|
||||
{
|
||||
SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
|
||||
if (sessionHandler != null)
|
||||
{
|
||||
SessionManager manager = sessionHandler.getSessionManager();
|
||||
|
||||
if (manager != null && manager instanceof MongoSessionManager)
|
||||
{
|
||||
((MongoSessionManager)manager).expire(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
|
|
|
@ -44,6 +44,55 @@ import com.mongodb.DBObject;
|
|||
import com.mongodb.MongoException;
|
||||
|
||||
|
||||
/**
|
||||
* MongoSessionManager
|
||||
*
|
||||
* Clustered session manager using MongoDB as the shared DB instance.
|
||||
* The document model is an outer object that contains the elements:
|
||||
* <ul>
|
||||
* <li>"id" : session_id </li>
|
||||
* <li>"created" : create_time </li>
|
||||
* <li>"accessed": last_access_time </li>
|
||||
* <li>"maxIdle" : max_idle_time setting as session was created </li>
|
||||
* <li>"expiry" : time at which session should expire </li>
|
||||
* <li>"valid" : session_valid </li>
|
||||
* <li>"context" : a nested object containing 1 nested object per context for which the session id is in use
|
||||
* </ul>
|
||||
* Each of the nested objects inside the "context" element contains:
|
||||
* <ul>
|
||||
* <li>unique_context_name : nested object containing name:value pairs of the session attributes for that context</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* One of the name:value attribute pairs will always be the special attribute "__metadata__". The value
|
||||
* is an object representing a version counter which is incremented every time the attributes change.
|
||||
* </p>
|
||||
* <p>
|
||||
* For example:
|
||||
* <code>
|
||||
* { "_id" : ObjectId("52845534a40b66410f228f23"),
|
||||
* "accessed" : NumberLong("1384818548903"),
|
||||
* "maxIdle" : 1,
|
||||
* "context" : { "::/contextA" : { "A" : "A",
|
||||
* "__metadata__" : { "version" : NumberLong(2) }
|
||||
* },
|
||||
* "::/contextB" : { "B" : "B",
|
||||
* "__metadata__" : { "version" : NumberLong(1) }
|
||||
* }
|
||||
* },
|
||||
* "created" : NumberLong("1384818548903"),
|
||||
* "expiry" : NumberLong("1384818549903"),
|
||||
* "id" : "w01ijx2vnalgv1sqrpjwuirprp7",
|
||||
* "valid" : true
|
||||
* }
|
||||
* </code>
|
||||
* </p>
|
||||
* <p>
|
||||
* In MongoDB, the nesting level is indicated by "." separators for the key name. Thus to
|
||||
* interact with a session attribute, the key is composed of:
|
||||
* "context".unique_context_name.attribute_name
|
||||
* Eg "context"."::/contextA"."A"
|
||||
* </p>
|
||||
*/
|
||||
@ManagedObject("Mongo Session Manager")
|
||||
public class MongoSessionManager extends NoSqlSessionManager
|
||||
{
|
||||
|
@ -54,14 +103,56 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
/*
|
||||
* strings used as keys or parts of keys in mongo
|
||||
*/
|
||||
/**
|
||||
* Special attribute for a session that is context-specific
|
||||
*/
|
||||
private final static String __METADATA = "__metadata__";
|
||||
|
||||
|
||||
/**
|
||||
* Session id
|
||||
*/
|
||||
public final static String __ID = "id";
|
||||
|
||||
/**
|
||||
* Time of session creation
|
||||
*/
|
||||
private final static String __CREATED = "created";
|
||||
|
||||
/**
|
||||
* Whether or not session is valid
|
||||
*/
|
||||
public final static String __VALID = "valid";
|
||||
|
||||
/**
|
||||
* Time at which session was invalidated
|
||||
*/
|
||||
public final static String __INVALIDATED = "invalidated";
|
||||
|
||||
/**
|
||||
* Last access time of session
|
||||
*/
|
||||
public final static String __ACCESSED = "accessed";
|
||||
|
||||
/**
|
||||
* Time this session will expire, based on last access time and maxIdle
|
||||
*/
|
||||
public final static String __EXPIRY = "expiry";
|
||||
|
||||
/**
|
||||
* The max idle time of a session (smallest value across all contexts which has a session with the same id)
|
||||
*/
|
||||
public final static String __MAX_IDLE = "maxIdle";
|
||||
|
||||
/**
|
||||
* Name of nested document field containing 1 sub document per context for which the session id is in use
|
||||
*/
|
||||
private final static String __CONTEXT = "context";
|
||||
|
||||
|
||||
/**
|
||||
* Special attribute per session per context, incremented each time attributes are modified
|
||||
*/
|
||||
public final static String __VERSION = __METADATA + ".version";
|
||||
|
||||
/**
|
||||
|
@ -70,8 +161,16 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
private String _contextId = null;
|
||||
|
||||
|
||||
private DBCollection _sessions;
|
||||
private DBObject __version_1;
|
||||
/**
|
||||
* Access to MongoDB
|
||||
*/
|
||||
private DBCollection _dbSessions;
|
||||
|
||||
|
||||
/**
|
||||
* Utility value of 1 for a session version for this context
|
||||
*/
|
||||
private DBObject _version_1;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -88,9 +187,7 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
{
|
||||
super.doStart();
|
||||
String[] hosts = getContextHandler().getVirtualHosts();
|
||||
//TODO: can this be replaced?
|
||||
/*if (hosts == null || hosts.length == 0)
|
||||
hosts = getContextHandler().getConnectorNames();*/
|
||||
|
||||
if (hosts == null || hosts.length == 0)
|
||||
hosts = new String[]
|
||||
{ "::" }; // IPv6 equiv of 0.0.0.0
|
||||
|
@ -102,19 +199,18 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
}
|
||||
|
||||
_contextId = createContextId(hosts,contextPath);
|
||||
|
||||
__version_1 = new BasicDBObject(getContextKey(__VERSION),1);
|
||||
_version_1 = new BasicDBObject(getContextAttributeKey(__VERSION),1);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* (non-Javadoc)
|
||||
/**
|
||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#setSessionIdManager(org.eclipse.jetty.server.SessionIdManager)
|
||||
*/
|
||||
@Override
|
||||
public void setSessionIdManager(SessionIdManager metaManager)
|
||||
{
|
||||
MongoSessionIdManager msim = (MongoSessionIdManager)metaManager;
|
||||
_sessions=msim.getSessions();
|
||||
_dbSessions=msim.getSessions();
|
||||
super.setSessionIdManager(metaManager);
|
||||
|
||||
}
|
||||
|
@ -125,7 +221,7 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
{
|
||||
try
|
||||
{
|
||||
__log.debug("MongoSessionManager:save:" + session);
|
||||
__log.debug("MongoSessionManager:save session {}", session.getClusterId());
|
||||
session.willPassivate();
|
||||
|
||||
// Form query for upsert
|
||||
|
@ -137,9 +233,13 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
BasicDBObject sets = new BasicDBObject();
|
||||
BasicDBObject unsets = new BasicDBObject();
|
||||
|
||||
|
||||
// handle valid or invalid
|
||||
if (session.isValid())
|
||||
{
|
||||
long expiry = (session.getMaxInactiveInterval() > 0?(session.getAccessed()+(1000*getMaxInactiveInterval())):0);
|
||||
__log.debug("MongoSessionManager: calculated expiry {} for session {}", expiry, session.getId());
|
||||
|
||||
// handle new or existing
|
||||
if (version == null)
|
||||
{
|
||||
|
@ -148,12 +248,26 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
version = new Long(1);
|
||||
sets.put(__CREATED,session.getCreationTime());
|
||||
sets.put(__VALID,true);
|
||||
sets.put(getContextKey(__VERSION),version);
|
||||
|
||||
sets.put(getContextAttributeKey(__VERSION),version);
|
||||
sets.put(__MAX_IDLE, getMaxInactiveInterval());
|
||||
sets.put(__EXPIRY, expiry);
|
||||
}
|
||||
else
|
||||
{
|
||||
version = new Long(((Number)version).longValue() + 1);
|
||||
update.put("$inc",__version_1);
|
||||
update.put("$inc",_version_1);
|
||||
//if max idle time and/or expiry is smaller for this context, then choose that for the whole session doc
|
||||
BasicDBObject fields = new BasicDBObject();
|
||||
fields.append(__MAX_IDLE, true);
|
||||
fields.append(__EXPIRY, true);
|
||||
DBObject o = _dbSessions.findOne(new BasicDBObject("id",session.getClusterId()), fields);
|
||||
Integer currentMaxIdle = (Integer)o.get(__MAX_IDLE);
|
||||
Long currentExpiry = (Long)o.get(__EXPIRY);
|
||||
if (currentMaxIdle != null && getMaxInactiveInterval() > 0 && getMaxInactiveInterval() < currentMaxIdle)
|
||||
sets.put(__MAX_IDLE, getMaxInactiveInterval());
|
||||
if (currentExpiry != null && expiry > 0 && expiry < currentExpiry)
|
||||
sets.put(__EXPIRY, currentExpiry);
|
||||
}
|
||||
|
||||
sets.put(__ACCESSED,session.getAccessed());
|
||||
|
@ -185,8 +299,8 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
if (!unsets.isEmpty())
|
||||
update.put("$unset",unsets);
|
||||
|
||||
_sessions.update(key,update,upsert,false);
|
||||
__log.debug("MongoSessionManager:save:db.sessions.update(" + key + "," + update + ",true)");
|
||||
_dbSessions.update(key,update,upsert,false);
|
||||
__log.debug("MongoSessionManager:save:db.sessions.update( {}, {}, true) ", key, update);
|
||||
|
||||
if (activateAfterSave)
|
||||
session.didActivate();
|
||||
|
@ -204,20 +318,20 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
@Override
|
||||
protected Object refresh(NoSqlSession session, Object version)
|
||||
{
|
||||
__log.debug("MongoSessionManager:refresh " + session);
|
||||
__log.debug("MongoSessionManager:refresh session {}", session.getId());
|
||||
|
||||
// check if our in memory version is the same as what is on the disk
|
||||
if (version != null)
|
||||
{
|
||||
DBObject o = _sessions.findOne(new BasicDBObject(__ID,session.getClusterId()),__version_1);
|
||||
DBObject o = _dbSessions.findOne(new BasicDBObject(__ID,session.getClusterId()),_version_1);
|
||||
|
||||
if (o != null)
|
||||
{
|
||||
Object saved = getNestedValue(o, getContextKey(__VERSION));
|
||||
Object saved = getNestedValue(o, getContextAttributeKey(__VERSION));
|
||||
|
||||
if (saved != null && saved.equals(version))
|
||||
{
|
||||
__log.debug("MongoSessionManager:refresh not needed");
|
||||
__log.debug("MongoSessionManager:refresh not needed session {}", session.getId());
|
||||
return version;
|
||||
}
|
||||
version = saved;
|
||||
|
@ -225,12 +339,12 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
}
|
||||
|
||||
// If we are here, we have to load the object
|
||||
DBObject o = _sessions.findOne(new BasicDBObject(__ID,session.getClusterId()));
|
||||
DBObject o = _dbSessions.findOne(new BasicDBObject(__ID,session.getClusterId()));
|
||||
|
||||
// If it doesn't exist, invalidate
|
||||
if (o == null)
|
||||
{
|
||||
__log.debug("MongoSessionManager:refresh:marking invalid, no object");
|
||||
__log.debug("MongoSessionManager:refresh:marking session {} invalid, no object", session.getClusterId());
|
||||
session.invalidate();
|
||||
return null;
|
||||
}
|
||||
|
@ -239,7 +353,7 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
Boolean valid = (Boolean)o.get(__VALID);
|
||||
if (valid == null || !valid)
|
||||
{
|
||||
__log.debug("MongoSessionManager:refresh:marking invalid, valid flag " + valid);
|
||||
__log.debug("MongoSessionManager:refresh:marking session {} invalid, valid flag {}", session.getClusterId(), valid);
|
||||
session.invalidate();
|
||||
return null;
|
||||
}
|
||||
|
@ -251,17 +365,15 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
{
|
||||
session.clearAttributes();
|
||||
|
||||
DBObject attrs = (DBObject)getNestedValue(o,getContextKey());
|
||||
|
||||
DBObject attrs = (DBObject)getNestedValue(o,getContextKey());
|
||||
|
||||
if (attrs != null)
|
||||
{
|
||||
for (String name : attrs.keySet())
|
||||
{
|
||||
//skip special metadata field which is not one of the session attributes
|
||||
if (__METADATA.equals(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String attr = decodeName(name);
|
||||
Object value = decodeValue(attrs.get(name));
|
||||
|
@ -301,7 +413,7 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
update.put("$set",sets);
|
||||
}
|
||||
|
||||
_sessions.update(key,update,false,false);
|
||||
_dbSessions.update(key,update,false,false);
|
||||
|
||||
session.didActivate();
|
||||
|
||||
|
@ -319,51 +431,51 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
@Override
|
||||
protected synchronized NoSqlSession loadSession(String clusterId)
|
||||
{
|
||||
DBObject o = _sessions.findOne(new BasicDBObject(__ID,clusterId));
|
||||
|
||||
__log.debug("MongoSessionManager:loaded " + o);
|
||||
DBObject o = _dbSessions.findOne(new BasicDBObject(__ID,clusterId));
|
||||
|
||||
__log.debug("MongoSessionManager:id={} loaded={}", clusterId, o);
|
||||
if (o == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Boolean valid = (Boolean)o.get(__VALID);
|
||||
__log.debug("MongoSessionManager:id={} valid={}", clusterId, valid);
|
||||
if (valid == null || !valid)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Object version = o.get(getContextKey(__VERSION));
|
||||
Object version = o.get(getContextAttributeKey(__VERSION));
|
||||
Long created = (Long)o.get(__CREATED);
|
||||
Long accessed = (Long)o.get(__ACCESSED);
|
||||
|
||||
NoSqlSession session = new NoSqlSession(this,created,accessed,clusterId,version);
|
||||
NoSqlSession session = null;
|
||||
|
||||
// get the attributes for the context
|
||||
// get the session for the context
|
||||
DBObject attrs = (DBObject)getNestedValue(o,getContextKey());
|
||||
|
||||
__log.debug("MongoSessionManager:attrs: " + attrs);
|
||||
__log.debug("MongoSessionManager:attrs {}", attrs);
|
||||
if (attrs != null)
|
||||
{
|
||||
__log.debug("MongoSessionManager: session {} present for context {}", clusterId, getContextKey());
|
||||
//only load a session if it exists for this context
|
||||
session = new NoSqlSession(this,created,accessed,clusterId,version);
|
||||
|
||||
for (String name : attrs.keySet())
|
||||
{
|
||||
//skip special metadata attribute which is not one of the actual session attributes
|
||||
if ( __METADATA.equals(name) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String attr = decodeName(name);
|
||||
Object value = decodeValue(attrs.get(name));
|
||||
|
||||
session.doPutOrRemove(attr,value);
|
||||
session.bindValue(attr,value);
|
||||
|
||||
}
|
||||
session.didActivate();
|
||||
}
|
||||
session.didActivate();
|
||||
else
|
||||
__log.debug("MongoSessionManager: session {} not present for context {}",clusterId, getContextKey());
|
||||
|
||||
return session;
|
||||
}
|
||||
|
@ -374,11 +486,17 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------ */
|
||||
/**
|
||||
* Remove the per-context sub document for this session id.
|
||||
* @see org.eclipse.jetty.nosql.NoSqlSessionManager#remove(org.eclipse.jetty.nosql.NoSqlSession)
|
||||
*/
|
||||
@Override
|
||||
protected boolean remove(NoSqlSession session)
|
||||
{
|
||||
__log.debug("MongoSessionManager:remove:session " + session.getClusterId());
|
||||
__log.debug("MongoSessionManager:remove:session {} for context {}",session.getClusterId(), getContextKey());
|
||||
|
||||
/*
|
||||
* Check if the session exists and if it does remove the context
|
||||
|
@ -386,7 +504,7 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
*/
|
||||
BasicDBObject key = new BasicDBObject(__ID,session.getClusterId());
|
||||
|
||||
DBObject o = _sessions.findOne(key,__version_1);
|
||||
DBObject o = _dbSessions.findOne(key,_version_1);
|
||||
|
||||
if (o != null)
|
||||
{
|
||||
|
@ -394,7 +512,7 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
BasicDBObject unsets = new BasicDBObject();
|
||||
unsets.put(getContextKey(),1);
|
||||
remove.put("$unset",unsets);
|
||||
_sessions.update(key,remove);
|
||||
_dbSessions.update(key,remove);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -404,20 +522,22 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------ */
|
||||
|
||||
|
||||
/**
|
||||
* @see org.eclipse.jetty.nosql.NoSqlSessionManager#expire(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
protected void invalidateSession(String idInCluster)
|
||||
protected void expire (String idInCluster)
|
||||
{
|
||||
__log.debug("MongoSessionManager:invalidateSession:invalidating " + idInCluster);
|
||||
__log.debug("MongoSessionManager:expire session {} ", idInCluster);
|
||||
|
||||
//Expire the session for this context
|
||||
super.expire(idInCluster);
|
||||
|
||||
super.invalidateSession(idInCluster);
|
||||
|
||||
/*
|
||||
* pull back the 'valid' value, we can check if its false, if is we don't need to
|
||||
* reset it to false
|
||||
*/
|
||||
//If the outer session document has not already been marked invalid, do so.
|
||||
DBObject validKey = new BasicDBObject(__VALID, true);
|
||||
DBObject o = _sessions.findOne(new BasicDBObject(__ID,idInCluster), validKey);
|
||||
DBObject o = _dbSessions.findOne(new BasicDBObject(__ID,idInCluster), validKey);
|
||||
|
||||
if (o != null && (Boolean)o.get(__VALID))
|
||||
{
|
||||
|
@ -428,21 +548,24 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
update.put("$set",sets);
|
||||
|
||||
BasicDBObject key = new BasicDBObject(__ID,idInCluster);
|
||||
|
||||
_sessions.update(key,update);
|
||||
_dbSessions.update(key,update);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------ */
|
||||
/**
|
||||
* Change the session id. Note that this will change the session id for all contexts for which the session id is in use.
|
||||
* @see org.eclipse.jetty.nosql.NoSqlSessionManager#update(org.eclipse.jetty.nosql.NoSqlSession, java.lang.String, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
protected void update(NoSqlSession session, String newClusterId, String newNodeId) throws Exception
|
||||
{
|
||||
// Form query for update - use object's existing session id
|
||||
BasicDBObject key = new BasicDBObject(__ID, session.getClusterId());
|
||||
BasicDBObject sets = new BasicDBObject();
|
||||
BasicDBObject update = new BasicDBObject(__ID, newClusterId);
|
||||
sets.put("$set", update);
|
||||
_sessions.update(key, sets, false, false);
|
||||
_dbSessions.update(key, sets, false, false);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------ */
|
||||
|
@ -527,9 +650,13 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
}
|
||||
|
||||
/*------------------------------------------------------------ */
|
||||
private String getContextKey(String keybit)
|
||||
/** Get a dot separated key for
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
private String getContextAttributeKey(String attr)
|
||||
{
|
||||
return __CONTEXT + "." + _contextId + "." + keybit;
|
||||
return getContextKey()+ "." + attr;
|
||||
}
|
||||
|
||||
@ManagedOperation(value="purge invalid sessions in the session store based on normal criteria", impact="ACTION")
|
||||
|
@ -567,7 +694,7 @@ public class MongoSessionManager extends NoSqlSessionManager
|
|||
@ManagedAttribute("total number of known sessions in the store")
|
||||
public long getSessionStoreCount()
|
||||
{
|
||||
return _sessions.find().count();
|
||||
return _dbSessions.find().count();
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------ */
|
||||
|
|
|
@ -276,7 +276,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
|||
{
|
||||
super.doStop();
|
||||
|
||||
invalidateSessions();
|
||||
shutdownSessions();
|
||||
|
||||
_loader=null;
|
||||
}
|
||||
|
@ -735,7 +735,12 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
|||
*/
|
||||
public abstract AbstractSession getSession(String idInCluster);
|
||||
|
||||
protected abstract void invalidateSessions() throws Exception;
|
||||
/**
|
||||
* Prepare sessions for session manager shutdown
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected abstract void shutdownSessions() throws Exception;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -783,7 +788,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
|||
* @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
|
||||
* {@link SessionIdManager#invalidateAll(String)} should be called.
|
||||
*/
|
||||
public void removeSession(AbstractSession session, boolean invalidate)
|
||||
public boolean removeSession(AbstractSession session, boolean invalidate)
|
||||
{
|
||||
// Remove session from context and global maps
|
||||
boolean removed = removeSession(session.getClusterId());
|
||||
|
@ -807,6 +812,8 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -388,7 +388,7 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void invalidateSessions() throws Exception
|
||||
protected void shutdownSessions() throws Exception
|
||||
{
|
||||
// Invalidate all sessions to cause unbind events
|
||||
ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values());
|
||||
|
@ -398,11 +398,11 @@ public class HashSessionManager extends AbstractSessionManager
|
|||
// If we are called from doStop
|
||||
if (isStopping() && _storeDir != null && _storeDir.exists() && _storeDir.canWrite())
|
||||
{
|
||||
// Then we only save and remove the session - it is not invalidated.
|
||||
// Then we only save and remove the session from memory- it is not invalidated.
|
||||
for (HashedSession session : sessions)
|
||||
{
|
||||
session.save(false);
|
||||
removeSession(session,false);
|
||||
_sessions.remove(session.getClusterId());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -27,11 +27,13 @@ import java.sql.Connection;
|
|||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -72,7 +74,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(JDBCSessionManager.class);
|
||||
|
||||
private ConcurrentHashMap<String, AbstractSession> _sessions;
|
||||
private ConcurrentHashMap<String, Session> _sessions;
|
||||
protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
|
||||
protected long _saveIntervalSec = 60; //only persist changes to session access times every 60 secs
|
||||
|
||||
|
@ -326,9 +328,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
{
|
||||
//The session attributes have changed, write to the db, ensuring
|
||||
//http passivation/activation listeners called
|
||||
willPassivate();
|
||||
updateSession(this);
|
||||
didActivate();
|
||||
save(true);
|
||||
}
|
||||
else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
|
||||
{
|
||||
|
@ -361,6 +361,23 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void save (boolean reactivate) throws Exception
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (_dirty)
|
||||
{
|
||||
//The session attributes have changed, write to the db, ensuring
|
||||
//http passivation/activation listeners called
|
||||
willPassivate();
|
||||
updateSession(this);
|
||||
if (reactivate)
|
||||
didActivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void timeout() throws IllegalStateException
|
||||
|
@ -451,109 +468,110 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
public Session getSession(String idInCluster)
|
||||
{
|
||||
Session session = null;
|
||||
Session memSession = (Session)_sessions.get(idInCluster);
|
||||
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
//check if we need to reload the session -
|
||||
//as an optimization, don't reload on every access
|
||||
//to reduce the load on the database. This introduces a window of
|
||||
//possibility that the node may decide that the session is local to it,
|
||||
//when the session has actually been live on another node, and then
|
||||
//re-migrated to this node. This should be an extremely rare occurrence,
|
||||
//as load-balancers are generally well-behaved and consistently send
|
||||
//sessions to the same node, changing only iff that node fails.
|
||||
//Session data = null;
|
||||
long now = System.currentTimeMillis();
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
if (memSession==null)
|
||||
LOG.debug("getSession("+idInCluster+"): not in session map,"+
|
||||
" now="+now+
|
||||
" lastSaved="+(memSession==null?0:memSession._lastSaved)+
|
||||
" interval="+(_saveIntervalSec * 1000L));
|
||||
else
|
||||
LOG.debug("getSession("+idInCluster+"): in session map, "+
|
||||
" now="+now+
|
||||
" lastSaved="+(memSession==null?0:memSession._lastSaved)+
|
||||
" interval="+(_saveIntervalSec * 1000L)+
|
||||
" lastNode="+memSession._lastNode+
|
||||
" thisNode="+getSessionIdManager().getWorkerName()+
|
||||
" difference="+(now - memSession._lastSaved));
|
||||
}
|
||||
Session memSession = (Session)_sessions.get(idInCluster);
|
||||
|
||||
//check if we need to reload the session -
|
||||
//as an optimization, don't reload on every access
|
||||
//to reduce the load on the database. This introduces a window of
|
||||
//possibility that the node may decide that the session is local to it,
|
||||
//when the session has actually been live on another node, and then
|
||||
//re-migrated to this node. This should be an extremely rare occurrence,
|
||||
//as load-balancers are generally well-behaved and consistently send
|
||||
//sessions to the same node, changing only iff that node fails.
|
||||
//Session data = null;
|
||||
long now = System.currentTimeMillis();
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
if (memSession==null)
|
||||
LOG.debug("getSession("+idInCluster+"): not in session map,"+
|
||||
" now="+now+
|
||||
" lastSaved="+(memSession==null?0:memSession._lastSaved)+
|
||||
" interval="+(_saveIntervalSec * 1000L));
|
||||
else
|
||||
LOG.debug("getSession("+idInCluster+"): in session map, "+
|
||||
" now="+now+
|
||||
" lastSaved="+(memSession==null?0:memSession._lastSaved)+
|
||||
" interval="+(_saveIntervalSec * 1000L)+
|
||||
" lastNode="+memSession._lastNode+
|
||||
" thisNode="+getSessionIdManager().getWorkerName()+
|
||||
" difference="+(now - memSession._lastSaved));
|
||||
}
|
||||
|
||||
try
|
||||
try
|
||||
{
|
||||
if (memSession==null)
|
||||
{
|
||||
if (memSession==null)
|
||||
{
|
||||
LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
|
||||
session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
|
||||
}
|
||||
else if ((now - memSession._lastSaved) >= (_saveIntervalSec * 1000L))
|
||||
{
|
||||
LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
|
||||
session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("getSession("+idInCluster+"): session in session map");
|
||||
session = memSession;
|
||||
}
|
||||
LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
|
||||
session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
|
||||
}
|
||||
catch (Exception e)
|
||||
else if ((now - memSession._lastSaved) >= (_saveIntervalSec * 1000L))
|
||||
{
|
||||
LOG.warn("Unable to load session "+idInCluster, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
//If we have a session
|
||||
if (session != null)
|
||||
{
|
||||
//If the session was last used on a different node, or session doesn't exist on this node
|
||||
if (!session.getLastNode().equals(getSessionIdManager().getWorkerName()) || memSession==null)
|
||||
{
|
||||
//if session doesn't expire, or has not already expired, update it and put it in this nodes' memory
|
||||
if (session._expiryTime <= 0 || session._expiryTime > now)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("getSession("+idInCluster+"): lastNode="+session.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
|
||||
|
||||
session.setLastNode(getSessionIdManager().getWorkerName());
|
||||
_sessions.put(idInCluster, session);
|
||||
|
||||
//update in db
|
||||
try
|
||||
{
|
||||
updateSessionNode(session);
|
||||
session.didActivate();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Unable to update freshly loaded session "+idInCluster, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("getSession ({}): Session has expired", idInCluster);
|
||||
//ensure that the session id for the expired session is deleted so that a new session with the
|
||||
//same id cannot be created (because the idInUse() test would succeed)
|
||||
_jdbcSessionIdMgr.removeSession(idInCluster);
|
||||
session=null;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
|
||||
LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
|
||||
session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
|
||||
}
|
||||
else
|
||||
{
|
||||
//No session in db with matching id and context path.
|
||||
LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
|
||||
LOG.debug("getSession("+idInCluster+"): session in session map");
|
||||
session = memSession;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Unable to load session "+idInCluster, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return session;
|
||||
|
||||
//If we have a session
|
||||
if (session != null)
|
||||
{
|
||||
//If the session was last used on a different node, or session doesn't exist on this node
|
||||
if (!session.getLastNode().equals(getSessionIdManager().getWorkerName()) || memSession==null)
|
||||
{
|
||||
//if session doesn't expire, or has not already expired, update it and put it in this nodes' memory
|
||||
if (session._expiryTime <= 0 || session._expiryTime > now)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("getSession("+idInCluster+"): lastNode="+session.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
|
||||
|
||||
session.setLastNode(getSessionIdManager().getWorkerName());
|
||||
_sessions.put(idInCluster, session);
|
||||
|
||||
//update in db
|
||||
try
|
||||
{
|
||||
updateSessionNode(session);
|
||||
session.didActivate();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Unable to update freshly loaded session "+idInCluster, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("getSession ({}): Session has expired", idInCluster);
|
||||
//ensure that the session id for the expired session is deleted so that a new session with the
|
||||
//same id cannot be created (because the idInUse() test would succeed)
|
||||
_jdbcSessionIdMgr.removeSession(idInCluster);
|
||||
session=null;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
|
||||
}
|
||||
else
|
||||
{
|
||||
//No session in db with matching id and context path.
|
||||
LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -566,12 +584,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
@Override
|
||||
public int getSessions()
|
||||
{
|
||||
int size = 0;
|
||||
synchronized (this)
|
||||
{
|
||||
size = _sessions.size();
|
||||
}
|
||||
return size;
|
||||
return _sessions.size();
|
||||
}
|
||||
|
||||
|
||||
|
@ -588,7 +601,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
|
||||
_jdbcSessionIdMgr = (JDBCSessionIdManager)_sessionIdManager;
|
||||
|
||||
_sessions = new ConcurrentHashMap<String, AbstractSession>();
|
||||
_sessions = new ConcurrentHashMap<String, Session>();
|
||||
|
||||
super.doStart();
|
||||
}
|
||||
|
@ -602,21 +615,46 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
@Override
|
||||
public void doStop() throws Exception
|
||||
{
|
||||
super.doStop();
|
||||
_sessions.clear();
|
||||
_sessions = null;
|
||||
|
||||
super.doStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidateSessions()
|
||||
protected void shutdownSessions()
|
||||
{
|
||||
//Do nothing - we don't want to remove and
|
||||
//invalidate all the sessions because this
|
||||
//method is called from doStop(), and just
|
||||
//because this context is stopping does not
|
||||
//mean that we should remove the session from
|
||||
//any other nodes
|
||||
//Save the current state of all of our sessions,
|
||||
//do NOT delete them (so other nodes can manage them)
|
||||
long gracefulStopMs = getContextHandler().getServer().getStopTimeout();
|
||||
long stopTime = 0;
|
||||
if (gracefulStopMs > 0)
|
||||
stopTime = System.nanoTime() + (TimeUnit.NANOSECONDS.convert(gracefulStopMs, TimeUnit.MILLISECONDS));
|
||||
|
||||
ArrayList<Session> sessions = (_sessions == null? new ArrayList<Session>() :new ArrayList<Session>(_sessions.values()) );
|
||||
|
||||
// loop while there are sessions, and while there is stop time remaining, or if no stop time, just 1 loop
|
||||
while (sessions.size() > 0 && ((stopTime > 0 && (System.nanoTime() < stopTime)) || (stopTime == 0)))
|
||||
{
|
||||
for (Session session : sessions)
|
||||
{
|
||||
try
|
||||
{
|
||||
session.save(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
_sessions.remove(session.getClusterId());
|
||||
}
|
||||
|
||||
//check if we should terminate our loop if we're not using the stop timer
|
||||
if (stopTime == 0)
|
||||
break;
|
||||
|
||||
// Get any sessions that were added by other requests during processing and go around the loop again
|
||||
sessions=new ArrayList<Session>(_sessions.values());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -627,23 +665,23 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
public void renewSessionId (String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
|
||||
{
|
||||
Session session = null;
|
||||
synchronized (this)
|
||||
try
|
||||
{
|
||||
try
|
||||
session = (Session)_sessions.remove(oldClusterId);
|
||||
if (session != null)
|
||||
{
|
||||
session = (Session)_sessions.remove(oldClusterId);
|
||||
if (session != null)
|
||||
synchronized (session)
|
||||
{
|
||||
session.setClusterId(newClusterId); //update ids
|
||||
session.setNodeId(newNodeId);
|
||||
_sessions.put(newClusterId, session); //put it into list in memory
|
||||
session.save(); //update database
|
||||
updateSession(session); //update database
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
|
||||
super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
|
||||
|
@ -658,11 +696,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
*/
|
||||
protected void invalidateSession (String idInCluster)
|
||||
{
|
||||
Session session = null;
|
||||
synchronized (this)
|
||||
{
|
||||
session = (Session)_sessions.get(idInCluster);
|
||||
}
|
||||
Session session = (Session)_sessions.get(idInCluster);
|
||||
|
||||
if (session != null)
|
||||
{
|
||||
|
@ -679,20 +713,17 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
@Override
|
||||
protected boolean removeSession(String idInCluster)
|
||||
{
|
||||
synchronized (this)
|
||||
Session session = (Session)_sessions.remove(idInCluster);
|
||||
try
|
||||
{
|
||||
Session session = (Session)_sessions.remove(idInCluster);
|
||||
try
|
||||
{
|
||||
if (session != null)
|
||||
deleteSession(session);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Problem deleting session id="+idInCluster, e);
|
||||
}
|
||||
return session!=null;
|
||||
if (session != null)
|
||||
deleteSession(session);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Problem deleting session id="+idInCluster, e);
|
||||
}
|
||||
return session!=null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -707,13 +738,8 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
if (session==null)
|
||||
return;
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
_sessions.put(session.getClusterId(), session);
|
||||
}
|
||||
_sessions.put(session.getClusterId(), (Session)session);
|
||||
|
||||
//TODO or delay the store until exit out of session? If we crash before we store it
|
||||
//then session data will be lost.
|
||||
try
|
||||
{
|
||||
synchronized (session)
|
||||
|
@ -748,40 +774,20 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
* {@link SessionIdManager#invalidateAll(String)} should be called.
|
||||
*/
|
||||
@Override
|
||||
public void removeSession(AbstractSession session, boolean invalidate)
|
||||
public boolean removeSession(AbstractSession session, boolean invalidate)
|
||||
{
|
||||
// Remove session from context and global maps
|
||||
boolean removed = false;
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
//take this session out of the map of sessions for this context
|
||||
if (_sessions.containsKey(session.getClusterId()))
|
||||
{
|
||||
removed = true;
|
||||
removeSession(session.getClusterId());
|
||||
}
|
||||
}
|
||||
boolean removed = super.removeSession(session, invalidate);
|
||||
|
||||
if (removed)
|
||||
{
|
||||
// Remove session from all context and global id maps
|
||||
_sessionIdManager.removeSession(session);
|
||||
|
||||
if (invalidate)
|
||||
_sessionIdManager.invalidateAll(session.getClusterId());
|
||||
|
||||
if (invalidate && !_sessionListeners.isEmpty())
|
||||
{
|
||||
HttpSessionEvent event=new HttpSessionEvent(session);
|
||||
for (HttpSessionListener l : _sessionListeners)
|
||||
l.sessionDestroyed(event);
|
||||
}
|
||||
if (!invalidate)
|
||||
{
|
||||
session.willPassivate();
|
||||
}
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
|
||||
|
@ -822,10 +828,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
{
|
||||
//loaded an expired session last managed on this node for this context, add it to the list so we can
|
||||
//treat it like a normal expired session
|
||||
synchronized (this)
|
||||
{
|
||||
_sessions.put(session.getClusterId(), session);
|
||||
}
|
||||
_sessions.put(session.getClusterId(), session);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -834,7 +837,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (session != null)
|
||||
{
|
||||
session.timeout();
|
||||
|
|
|
@ -122,10 +122,10 @@ public class SessionCookieTest
|
|||
}
|
||||
|
||||
/**
|
||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#invalidateSessions()
|
||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#shutdownSessions()
|
||||
*/
|
||||
@Override
|
||||
protected void invalidateSessions() throws Exception
|
||||
protected void shutdownSessions() throws Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -84,7 +84,6 @@ public class JdbcTestServer extends AbstractTestServer
|
|||
idManager.setScavengeInterval(_scavengePeriod);
|
||||
idManager.setWorkerName("w"+(__workers++));
|
||||
idManager.setDriverInfo(DRIVER_CLASS, (config==null?DEFAULT_CONNECTION_URL:config));
|
||||
//System.err.println("new jdbcidmgr inst="+idManager);
|
||||
return idManager;
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +101,7 @@ public class JdbcTestServer extends AbstractTestServer
|
|||
}
|
||||
|
||||
|
||||
public boolean existsInSessionTable(String id)
|
||||
public boolean existsInSessionIdTable(String id)
|
||||
throws Exception
|
||||
{
|
||||
Class.forName(DRIVER_CLASS);
|
||||
|
@ -122,6 +121,39 @@ public class JdbcTestServer extends AbstractTestServer
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean existsInSessionTable(String id, boolean verbose)
|
||||
throws Exception
|
||||
{
|
||||
Class.forName(DRIVER_CLASS);
|
||||
Connection con = null;
|
||||
try
|
||||
{
|
||||
con = DriverManager.getConnection(DEFAULT_CONNECTION_URL);
|
||||
PreparedStatement statement = con.prepareStatement("select * from "+((JDBCSessionIdManager)_sessionIdManager)._sessionTable+" where sessionId = ?");
|
||||
statement.setString(1, id);
|
||||
ResultSet result = statement.executeQuery();
|
||||
if (verbose)
|
||||
{
|
||||
boolean results = false;
|
||||
while (result.next())
|
||||
{
|
||||
results = true;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
else
|
||||
return result.next();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (con != null)
|
||||
con.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Set<String> getSessionIds ()
|
||||
throws Exception
|
||||
{
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
|
||||
package org.eclipse.jetty.server.session;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest
|
||||
{
|
||||
|
||||
@Override
|
||||
public AbstractTestServer createServer(int port, int max, int scavenge)
|
||||
{
|
||||
return new JdbcTestServer(port,max,scavenge);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionScavenge() throws Exception
|
||||
{
|
||||
super.testSessionScavenge();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server.session;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
public class StopSessionManagerPreserveSessionTest extends AbstractStopSessionManagerPreserveSessionTest
|
||||
{
|
||||
JdbcTestServer _server;
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
DriverManager.getConnection( "jdbc:derby:sessions;shutdown=true" );
|
||||
}
|
||||
catch( SQLException expected )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkSessionPersisted(boolean expected)
|
||||
{
|
||||
try
|
||||
{
|
||||
boolean actual = _server.existsInSessionTable(_id, true);
|
||||
System.err.println(expected+":"+actual);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractTestServer createServer(int port)
|
||||
{
|
||||
_server = new JdbcTestServer(0);
|
||||
return _server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureSessionManagement(ServletContextHandler context)
|
||||
{
|
||||
//nothing special
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testStopSessionManagerPreserveSession() throws Exception
|
||||
{
|
||||
super.testStopSessionManagerPreserveSession();
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@
|
|||
<parent>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<artifactId>test-sessions-parent</artifactId>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
<version>9.1.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>test-mongodb-sessions</artifactId>
|
||||
<name>Jetty Tests :: Sessions :: Mongo</name>
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.nosql.mongodb;
|
||||
|
||||
|
||||
import org.eclipse.jetty.server.session.AbstractInvalidationSessionTest;
|
||||
import org.eclipse.jetty.server.session.AbstractTestServer;
|
||||
import org.junit.Test;
|
||||
|
||||
public class InvalidateSessionTest extends AbstractInvalidationSessionTest
|
||||
{
|
||||
|
||||
@Override
|
||||
public AbstractTestServer createServer(int port)
|
||||
{
|
||||
return new MongoTestServer(port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause()
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.currentThread().sleep(2000);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidation() throws Exception
|
||||
{
|
||||
super.testInvalidation();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.nosql.mongodb;
|
||||
|
||||
import org.eclipse.jetty.server.session.AbstractLocalSessionScavengingTest;
|
||||
import org.eclipse.jetty.server.session.AbstractTestServer;
|
||||
|
||||
public class LocalSessionScavengingTest extends AbstractLocalSessionScavengingTest
|
||||
{
|
||||
|
||||
@Override
|
||||
public AbstractTestServer createServer(int port, int max, int scavenge)
|
||||
{
|
||||
return new MongoTestServer(port,max,scavenge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testLocalSessionsScavenging() throws Exception
|
||||
{
|
||||
super.testLocalSessionsScavenging();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -58,11 +58,11 @@ public class MongoTestServer extends AbstractTestServer
|
|||
{
|
||||
try
|
||||
{
|
||||
System.err.println("MongoTestServer:SessionIdManager scavenge: delay:"+ _scavengePeriod + " period:"+_maxInactivePeriod);
|
||||
System.err.println("MongoTestServer:SessionIdManager scavenge: delay:"+ _scavengePeriod + " period:"+_scavengePeriod);
|
||||
MongoSessionIdManager idManager = new MongoSessionIdManager(_server);
|
||||
idManager.setWorkerName("w"+(__workers++));
|
||||
idManager.setScavengeDelay((_scavengePeriod));
|
||||
idManager.setScavengePeriod(_maxInactivePeriod);
|
||||
idManager.setScavengePeriod(_scavengePeriod);
|
||||
|
||||
return idManager;
|
||||
}
|
||||
|
|
|
@ -145,26 +145,31 @@ public class PurgeInvalidSessionTest
|
|||
if ("create".equals(action))
|
||||
{
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute("foo", "bar");
|
||||
assertTrue(session.isNew());
|
||||
}
|
||||
else if ("invalidate".equals(action))
|
||||
{
|
||||
HttpSession existingSession = request.getSession(false);
|
||||
assertNotNull(existingSession);
|
||||
String id = existingSession.getId();
|
||||
id = (id.indexOf(".") > 0?id.substring(0, id.indexOf(".")):id);
|
||||
DBObject dbSession = _sessions.findOne(new BasicDBObject("id",id));
|
||||
assertNotNull(dbSession);
|
||||
|
||||
existingSession.invalidate();
|
||||
String id = request.getRequestedSessionId();
|
||||
assertNotNull(id);
|
||||
id = id.substring(0, id.indexOf("."));
|
||||
|
||||
//still in db, just marked as invalid
|
||||
DBObject dbSession = _sessions.findOne(new BasicDBObject("id", id));
|
||||
assertTrue(dbSession != null);
|
||||
dbSession = _sessions.findOne(new BasicDBObject("id", id));
|
||||
assertNotNull(dbSession);
|
||||
assertTrue(dbSession.containsField(MongoSessionManager.__INVALIDATED));
|
||||
}
|
||||
else if ("test".equals(action))
|
||||
{
|
||||
String id = request.getRequestedSessionId();
|
||||
assertNotNull(id);
|
||||
id = id.substring(0, id.indexOf("."));
|
||||
|
||||
id = (id.indexOf(".") > 0?id.substring(0, id.indexOf(".")):id);
|
||||
|
||||
HttpSession existingSession = request.getSession(false);
|
||||
assertTrue(existingSession == null);
|
||||
|
@ -175,5 +180,4 @@ public class PurgeInvalidSessionTest
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.nosql.mongodb;
|
||||
|
||||
import org.eclipse.jetty.server.session.AbstractSessionExpiryTest;
|
||||
import org.eclipse.jetty.server.session.AbstractTestServer;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SessionExpiryTest extends AbstractSessionExpiryTest
|
||||
{
|
||||
|
||||
@Override
|
||||
public AbstractTestServer createServer(int port, int max, int scavenge)
|
||||
{
|
||||
return new MongoTestServer(port,max,scavenge);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionNotExpired() throws Exception
|
||||
{
|
||||
super.testSessionNotExpired();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionExpiry() throws Exception
|
||||
{
|
||||
super.testSessionExpiry();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.nosql.mongodb;
|
||||
|
||||
import org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest;
|
||||
import org.eclipse.jetty.server.session.AbstractTestServer;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest
|
||||
{
|
||||
|
||||
@Override
|
||||
public AbstractTestServer createServer(int port, int max, int scavenge)
|
||||
{
|
||||
return new MongoTestServer(port,max,scavenge);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSessionScavenge() throws Exception
|
||||
{
|
||||
super.testSessionScavenge();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.nosql.mongodb;
|
||||
|
||||
import org.eclipse.jetty.server.session.AbstractSessionMigrationTest;
|
||||
import org.eclipse.jetty.server.session.AbstractTestServer;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SessionMigrationTest extends AbstractSessionMigrationTest
|
||||
{
|
||||
|
||||
@Override
|
||||
public AbstractTestServer createServer(int port)
|
||||
{
|
||||
return new MongoTestServer(port);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionMigration() throws Exception
|
||||
{
|
||||
super.testSessionMigration();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.nosql.mongodb;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoException;
|
||||
|
||||
public class StopSessionManagerDeleteSessionTest
|
||||
{
|
||||
public MongoTestServer createServer(int port, int max, int scavenge)
|
||||
{
|
||||
MongoTestServer server = new MongoTestServer(port,max,scavenge);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testStopSessionManagerDeleteSession() throws Exception
|
||||
{
|
||||
String contextPath = "";
|
||||
String servletMapping = "/server";
|
||||
|
||||
MongoTestServer server = createServer(0, 1, 0);
|
||||
ServletContextHandler context = server.addContext(contextPath);
|
||||
ServletHolder holder = new ServletHolder();
|
||||
TestServlet servlet = new TestServlet();
|
||||
holder.setServlet(servlet);
|
||||
|
||||
context.addServlet(holder, servletMapping);
|
||||
|
||||
MongoSessionManager sessionManager = (MongoSessionManager)context.getSessionHandler().getSessionManager();
|
||||
sessionManager.setPreserveOnStop(false);
|
||||
MongoSessionIdManager idManager = (MongoSessionIdManager)server.getServer().getSessionIdManager();
|
||||
idManager.setPurge(true);
|
||||
|
||||
|
||||
server.start();
|
||||
int port=server.getPort();
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.start();
|
||||
try
|
||||
{
|
||||
//Create a session
|
||||
ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
|
||||
assertTrue(sessionCookie != null);
|
||||
// Mangle the cookie, replacing Path with $Path, etc.
|
||||
sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
|
||||
|
||||
//stop the session manager
|
||||
sessionManager.stop();
|
||||
|
||||
//check the database to see that the session has been marked invalid
|
||||
servlet.checkSessionInDB(false);
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class TestServlet extends HttpServlet
|
||||
{
|
||||
DBCollection _sessions;
|
||||
String _id;
|
||||
|
||||
public TestServlet() throws UnknownHostException, MongoException
|
||||
{
|
||||
super();
|
||||
_sessions = new Mongo().getDB("HttpSessions").getCollection("sessions");
|
||||
}
|
||||
|
||||
public void checkSessionInDB (boolean expectedValid)
|
||||
{
|
||||
DBObject dbSession = _sessions.findOne(new BasicDBObject("id", _id));
|
||||
assertTrue(dbSession != null);
|
||||
assertEquals(expectedValid, dbSession.get("valid"));
|
||||
if (!expectedValid)
|
||||
assertNotNull(dbSession.get(MongoSessionManager.__INVALIDATED));
|
||||
}
|
||||
|
||||
public String getId()
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
String action = request.getParameter("action");
|
||||
if ("create".equals(action))
|
||||
{
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute("foo", "bar");
|
||||
assertTrue(session.isNew());
|
||||
_id = session.getId();
|
||||
}
|
||||
else if ("test".equals(action))
|
||||
{
|
||||
String id = request.getRequestedSessionId();
|
||||
assertNotNull(id);
|
||||
id = id.substring(0, id.indexOf("."));
|
||||
|
||||
HttpSession existingSession = request.getSession(false);
|
||||
assertTrue(existingSession == null);
|
||||
|
||||
//not in db any more
|
||||
DBObject dbSession = _sessions.findOne(new BasicDBObject("id", id));
|
||||
assertTrue(dbSession == null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
|
||||
package org.eclipse.jetty.nosql.mongodb;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoException;
|
||||
|
||||
/**
|
||||
* StopSessionManagerPreserveSessionTest
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class StopSessionManagerPreserveSessionTest extends AbstractStopSessionManagerPreserveSessionTest
|
||||
{
|
||||
DBCollection _sessions;
|
||||
|
||||
@Before
|
||||
public void setUp() throws UnknownHostException, MongoException
|
||||
{
|
||||
_sessions = new Mongo().getDB("HttpSessions").getCollection("sessions");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public MongoTestServer createServer(int port)
|
||||
{
|
||||
MongoTestServer server = new MongoTestServer(port);
|
||||
server.getServer().setStopTimeout(0);
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void checkSessionPersisted(boolean expected)
|
||||
{
|
||||
DBObject dbSession = _sessions.findOne(new BasicDBObject("id", _id));
|
||||
|
||||
if (expected)
|
||||
{
|
||||
assertTrue(dbSession != null);
|
||||
assertEquals(expected, dbSession.get("valid"));
|
||||
}
|
||||
else
|
||||
{
|
||||
assertTrue(dbSession==null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void configureSessionManagement(ServletContextHandler context)
|
||||
{
|
||||
((MongoSessionManager)context.getSessionHandler().getSessionManager()).setPreserveOnStop(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testStopSessionManagerPreserveSession() throws Exception
|
||||
{
|
||||
super.testStopSessionManagerPreserveSession();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -20,8 +20,10 @@ package org.eclipse.jetty.server.session;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -40,6 +42,7 @@ import org.eclipse.jetty.client.api.ContentResponse;
|
|||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
@ -78,7 +81,7 @@ public abstract class AbstractSessionInvalidateAndCreateTest
|
|||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(scavengePeriod * 2500L);
|
||||
Thread.sleep(scavengePeriod * 3000L);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
|
@ -128,7 +131,7 @@ public abstract class AbstractSessionInvalidateAndCreateTest
|
|||
ContentResponse response2 = request2.send();
|
||||
assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
|
||||
|
||||
// Wait for the scavenger to run, waiting 2.5 times the scavenger period
|
||||
// Wait for the scavenger to run, waiting 3 times the scavenger period
|
||||
pause(scavengePeriod);
|
||||
|
||||
//test that the session created in the last test is scavenged:
|
||||
|
@ -152,6 +155,20 @@ public abstract class AbstractSessionInvalidateAndCreateTest
|
|||
public static class TestServlet extends HttpServlet
|
||||
{
|
||||
private boolean unbound = false;
|
||||
|
||||
public class MySessionBindingListener implements HttpSessionBindingListener, Serializable
|
||||
{
|
||||
|
||||
public void valueUnbound(HttpSessionBindingEvent event)
|
||||
{
|
||||
unbound = true;
|
||||
}
|
||||
|
||||
public void valueBound(HttpSessionBindingEvent event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
|
||||
|
@ -167,25 +184,16 @@ public abstract class AbstractSessionInvalidateAndCreateTest
|
|||
HttpSession session = request.getSession(false);
|
||||
if (session != null)
|
||||
{
|
||||
//invalidate existing session
|
||||
session.invalidate();
|
||||
|
||||
//now make a new session
|
||||
session = request.getSession(true);
|
||||
session.setAttribute("identity", "session2");
|
||||
session.setAttribute("listener", new HttpSessionBindingListener()
|
||||
{
|
||||
|
||||
public void valueUnbound(HttpSessionBindingEvent event)
|
||||
{
|
||||
unbound = true;
|
||||
}
|
||||
|
||||
public void valueBound(HttpSessionBindingEvent event)
|
||||
{
|
||||
|
||||
}
|
||||
});
|
||||
session.setAttribute("listener", new MySessionBindingListener());
|
||||
}
|
||||
else
|
||||
fail("Session already missing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server.session;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.Test;
|
||||
|
||||
public abstract class AbstractStopSessionManagerPreserveSessionTest
|
||||
{
|
||||
public String _id;
|
||||
|
||||
|
||||
public abstract void checkSessionPersisted (boolean expected);
|
||||
|
||||
public abstract AbstractTestServer createServer (int port);
|
||||
|
||||
public abstract void configureSessionManagement(ServletContextHandler context);
|
||||
|
||||
@Test
|
||||
public void testStopSessionManagerPreserveSession() throws Exception
|
||||
{
|
||||
String contextPath = "";
|
||||
String servletMapping = "/server";
|
||||
|
||||
AbstractTestServer server = createServer(0);
|
||||
ServletContextHandler context = server.addContext(contextPath);
|
||||
ServletHolder holder = new ServletHolder();
|
||||
TestServlet servlet = new TestServlet();
|
||||
holder.setServlet(servlet);
|
||||
|
||||
context.addServlet(holder, servletMapping);
|
||||
|
||||
configureSessionManagement(context);
|
||||
|
||||
server.start();
|
||||
int port=server.getPort();
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.start();
|
||||
try
|
||||
{
|
||||
//Create a session
|
||||
ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
String sessionCookie = response.getHeaders().getStringField("Set-Cookie");
|
||||
assertTrue(sessionCookie != null);
|
||||
// Mangle the cookie, replacing Path with $Path, etc.
|
||||
sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
|
||||
|
||||
//stop the session manager
|
||||
context.getSessionHandler().getSessionManager().stop();
|
||||
|
||||
//check the database to see that the session is still valid
|
||||
checkSessionPersisted(true);
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestServlet extends HttpServlet
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
String action = request.getParameter("action");
|
||||
if ("create".equals(action))
|
||||
{
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute("foo", "bar");
|
||||
assertTrue(session.isNew());
|
||||
_id = session.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue