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);
|
super(manager, request);
|
||||||
_manager=manager;
|
_manager=manager;
|
||||||
save(true);
|
|
||||||
_active.incrementAndGet();
|
_active.incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +88,14 @@ public class NoSqlSession extends AbstractSession
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void timeout() throws IllegalStateException
|
||||||
|
{
|
||||||
|
super.timeout();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* a boolean version of the setAttribute method that lets us manage the _dirty set
|
* a boolean version of the setAttribute method that lets us manage the _dirty set
|
||||||
*/
|
*/
|
||||||
|
@ -125,7 +132,7 @@ public class NoSqlSession extends AbstractSession
|
||||||
@Override
|
@Override
|
||||||
protected boolean access(long time)
|
protected boolean access(long time)
|
||||||
{
|
{
|
||||||
__log.debug("NoSqlSession:access:active "+_active);
|
__log.debug("NoSqlSession:access:active {} time {}", _active, time);
|
||||||
if (_active.incrementAndGet()==1)
|
if (_active.incrementAndGet()==1)
|
||||||
{
|
{
|
||||||
long period=_manager.getStalePeriod()*1000L;
|
long period=_manager.getStalePeriod()*1000L;
|
||||||
|
@ -169,6 +176,7 @@ public class NoSqlSession extends AbstractSession
|
||||||
protected void doInvalidate() throws IllegalStateException
|
protected void doInvalidate() throws IllegalStateException
|
||||||
{
|
{
|
||||||
super.doInvalidate();
|
super.doInvalidate();
|
||||||
|
//jb why save here? if the session is invalidated it should be removed
|
||||||
save(false);
|
save(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +240,4 @@ public class NoSqlSession extends AbstractSession
|
||||||
{
|
{
|
||||||
super.setNodeId(nodeId);
|
super.setNodeId(nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.nosql;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
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.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NoSqlSessionManager
|
||||||
|
*
|
||||||
|
* Base class for SessionManager implementations using nosql frameworks
|
||||||
|
*
|
||||||
|
*/
|
||||||
public abstract class NoSqlSessionManager extends AbstractSessionManager implements SessionManager
|
public abstract class NoSqlSessionManager extends AbstractSessionManager implements SessionManager
|
||||||
{
|
{
|
||||||
private final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session");
|
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 _savePeriod=0;
|
||||||
private int _idlePeriod=-1;
|
private int _idlePeriod=-1;
|
||||||
private boolean _invalidateOnStop;
|
private boolean _invalidateOnStop;
|
||||||
private boolean _preserveOnStop;
|
private boolean _preserveOnStop = true;
|
||||||
private boolean _saveAllAttributes;
|
private boolean _saveAllAttributes;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
|
* @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -59,7 +66,12 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
||||||
protected void addSession(AbstractSession session)
|
protected void addSession(AbstractSession session)
|
||||||
{
|
{
|
||||||
if (isRunning())
|
if (isRunning())
|
||||||
|
{
|
||||||
|
//add into memory
|
||||||
_sessions.put(session.getClusterId(),(NoSqlSession)session);
|
_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)
|
public AbstractSession getSession(String idInCluster)
|
||||||
{
|
{
|
||||||
NoSqlSession session = _sessions.get(idInCluster);
|
NoSqlSession session = _sessions.get(idInCluster);
|
||||||
|
__log.debug("getSession {} ", session );
|
||||||
__log.debug("getSession: " + session );
|
|
||||||
|
|
||||||
if (session==null)
|
if (session==null)
|
||||||
{
|
{
|
||||||
|
//session not in this node's memory, load it
|
||||||
session=loadSession(idInCluster);
|
session=loadSession(idInCluster);
|
||||||
|
|
||||||
if (session!=null)
|
if (session!=null)
|
||||||
{
|
{
|
||||||
|
//session exists, check another request thread hasn't loaded it too
|
||||||
NoSqlSession race=_sessions.putIfAbsent(idInCluster,session);
|
NoSqlSession race=_sessions.putIfAbsent(idInCluster,session);
|
||||||
if (race!=null)
|
if (race!=null)
|
||||||
{
|
{
|
||||||
|
@ -83,7 +96,19 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
||||||
session.clearAttributes();
|
session.clearAttributes();
|
||||||
session=race;
|
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;
|
return session;
|
||||||
|
@ -91,46 +116,63 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@Override
|
@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
|
||||||
ArrayList<NoSqlSession> sessions=new ArrayList<NoSqlSession>(_sessions.values());
|
//save all of the sessions (including those that have been added during this method call)
|
||||||
int loop=100;
|
//and then just remove them from memory.
|
||||||
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) {
|
//If we don't wish to preserve sessions and we're stopping, then we should invalidate
|
||||||
removeSession(session,false);
|
//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());
|
||||||
|
|
||||||
|
// 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());
|
sessions=new ArrayList<NoSqlSession>(_sessions.values());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@Override
|
@Override
|
||||||
protected AbstractSession newSession(HttpServletRequest request)
|
protected AbstractSession newSession(HttpServletRequest request)
|
||||||
{
|
{
|
||||||
long created=System.currentTimeMillis();
|
|
||||||
return new NoSqlSession(this,request);
|
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
|
@Override
|
||||||
protected boolean removeSession(String idInCluster)
|
protected boolean removeSession(String idInCluster)
|
||||||
{
|
{
|
||||||
|
@ -147,7 +189,7 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
__log.warn("Problem deleting session id=" + idInCluster,e);
|
__log.warn("Problem deleting session {}", idInCluster,e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return session != null;
|
return session != null;
|
||||||
|
@ -155,29 +197,50 @@ public abstract class NoSqlSessionManager extends AbstractSessionManager impleme
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
protected void invalidateSession( String idInCluster )
|
protected void expire( String idInCluster )
|
||||||
{
|
{
|
||||||
synchronized (this)
|
synchronized (this)
|
||||||
{
|
{
|
||||||
NoSqlSession session = _sessions.remove(idInCluster);
|
//get the session from memory
|
||||||
|
NoSqlSession session = _sessions.get(idInCluster);
|
||||||
|
|
||||||
try
|
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)
|
if (session != null)
|
||||||
{
|
{
|
||||||
remove(session);
|
session.invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
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;
|
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
|
* 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
|
* 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
|
* This class has a timer that runs a periodic scavenger thread to query
|
||||||
* for all id's known to this node and that have and old accessed value greater
|
* for all id's known to this node whose precalculated expiry time has passed.
|
||||||
* then the scavengeDelay.
|
|
||||||
*
|
*
|
||||||
* 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
|
* 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
|
* 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)
|
* 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()
|
protected void scavenge()
|
||||||
{
|
{
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
__log.debug("SessionIdManager:scavenge:at " + now);
|
__log.debug("SessionIdManager:scavenge:at {}", now);
|
||||||
synchronized (_sessionsIds)
|
synchronized (_sessionsIds)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* run a query returning results that:
|
* run a query returning results that:
|
||||||
* - are in the known list of sessionIds
|
* - 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
|
* we limit the query to return just the __ID so we are not sucking back full sessions
|
||||||
*/
|
*/
|
||||||
BasicDBObject query = new BasicDBObject();
|
BasicDBObject query = new BasicDBObject();
|
||||||
query.put(MongoSessionManager.__ID,new BasicDBObject("$in", _sessionsIds ));
|
query.put(MongoSessionManager.__ID,new BasicDBObject("$in", _sessionsIds ));
|
||||||
|
query.put(MongoSessionManager.__EXPIRY, new BasicDBObject("$gt", 0));
|
||||||
|
query.put(MongoSessionManager.__EXPIRY, new BasicDBObject("$lt", now));
|
||||||
|
|
||||||
query.put(MongoSessionManager.__ACCESSED, new BasicDBObject("$lt",now - _scavengePeriod));
|
|
||||||
|
|
||||||
DBCursor checkSessions = _sessions.find(query, new BasicDBObject(MongoSessionManager.__ID, 1));
|
DBCursor checkSessions = _sessions.find(query, new BasicDBObject(MongoSessionManager.__ID, 1));
|
||||||
|
|
||||||
for ( DBObject session : checkSessions )
|
for ( DBObject session : checkSessions )
|
||||||
{
|
{
|
||||||
__log.debug("SessionIdManager:scavenge: invalidating " + (String)session.get(MongoSessionManager.__ID));
|
__log.debug("SessionIdManager:scavenge: expiring session {}", (String)session.get(MongoSessionManager.__ID));
|
||||||
invalidateAll((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
|
* ScavengeFully will expire all sessions. In most circumstances
|
||||||
* ids of this given instance of the session id manager to see if they
|
* you should never need to call this method.
|
||||||
* are past the point of expiration.
|
|
||||||
*
|
*
|
||||||
* NOTE: this is potentially devastating and may lead to serious session
|
* <b>USE WITH CAUTION</b>
|
||||||
* coherence issues, not to be used in a running cluster
|
|
||||||
*/
|
*/
|
||||||
protected void scavengeFully()
|
protected void scavengeFully()
|
||||||
{
|
{
|
||||||
|
@ -193,7 +191,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
||||||
|
|
||||||
for (DBObject session : checkSessions)
|
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:
|
* 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
|
* 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
|
* - 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
|
* 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");
|
String id = (String)session.get("id");
|
||||||
|
|
||||||
__log.debug("MongoSessionIdManager:purging invalid " + id);
|
__log.debug("MongoSessionIdManager:purging invalid session {}", id);
|
||||||
|
|
||||||
_sessions.remove(session);
|
_sessions.remove(session);
|
||||||
}
|
}
|
||||||
|
@ -247,7 +245,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
||||||
{
|
{
|
||||||
String id = (String)session.get(MongoSessionManager.__ID);
|
String id = (String)session.get(MongoSessionManager.__ID);
|
||||||
|
|
||||||
__log.debug("MongoSessionIdManager:purging valid " + id);
|
__log.debug("MongoSessionIdManager:purging valid session {}", id);
|
||||||
|
|
||||||
_sessions.remove(session);
|
_sessions.remove(session);
|
||||||
}
|
}
|
||||||
|
@ -264,7 +262,6 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
||||||
protected void purgeFully()
|
protected void purgeFully()
|
||||||
{
|
{
|
||||||
BasicDBObject invalidQuery = new BasicDBObject();
|
BasicDBObject invalidQuery = new BasicDBObject();
|
||||||
|
|
||||||
invalidQuery.put(MongoSessionManager.__VALID, false);
|
invalidQuery.put(MongoSessionManager.__VALID, false);
|
||||||
|
|
||||||
DBCursor oldSessions = _sessions.find(invalidQuery, new BasicDBObject(MongoSessionManager.__ID, 1));
|
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);
|
String id = (String)session.get(MongoSessionManager.__ID);
|
||||||
|
|
||||||
__log.debug("MongoSessionIdManager:purging invalid " + id);
|
__log.debug("MongoSessionIdManager:purging invalid session {}", id);
|
||||||
|
|
||||||
_sessions.remove(session);
|
_sessions.remove(session);
|
||||||
}
|
}
|
||||||
|
@ -302,6 +299,8 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
|
* The delay before the first scavenge operation is performed.
|
||||||
|
*
|
||||||
* sets the scavengeDelay
|
* sets the scavengeDelay
|
||||||
*/
|
*/
|
||||||
public void setScavengeDelay(long 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)
|
public void setScavengePeriod(long scavengePeriod)
|
||||||
{
|
{
|
||||||
if (scavengePeriod <= 0)
|
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
|
@Override
|
||||||
public boolean idInUse(String sessionId)
|
public boolean idInUse(String sessionId)
|
||||||
|
@ -461,7 +465,6 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
||||||
if ( o != null )
|
if ( o != null )
|
||||||
{
|
{
|
||||||
Boolean valid = (Boolean)o.get(MongoSessionManager.__VALID);
|
Boolean valid = (Boolean)o.get(MongoSessionManager.__VALID);
|
||||||
|
|
||||||
if ( valid == null )
|
if ( valid == null )
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -486,7 +489,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
||||||
* already a part of the index in mongo...
|
* already a part of the index in mongo...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
__log.debug("MongoSessionIdManager:addSession:" + session.getId());
|
__log.debug("MongoSessionIdManager:addSession {}", session.getId());
|
||||||
|
|
||||||
synchronized (_sessionsIds)
|
synchronized (_sessionsIds)
|
||||||
{
|
{
|
||||||
|
@ -511,6 +514,11 @@ 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
|
@Override
|
||||||
public void invalidateAll(String sessionId)
|
public void invalidateAll(String sessionId)
|
||||||
{
|
{
|
||||||
|
@ -518,6 +526,37 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
||||||
{
|
{
|
||||||
_sessionsIds.remove(sessionId);
|
_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).invalidateSession(sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* 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
|
//tell all contexts that may have a session object with this id to
|
||||||
//get rid of them
|
//get rid of them
|
||||||
|
@ -531,7 +570,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
|
||||||
|
|
||||||
if (manager != null && manager instanceof MongoSessionManager)
|
if (manager != null && manager instanceof MongoSessionManager)
|
||||||
{
|
{
|
||||||
((MongoSessionManager)manager).invalidateSession(sessionId);
|
((MongoSessionManager)manager).expire(sessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,55 @@ import com.mongodb.DBObject;
|
||||||
import com.mongodb.MongoException;
|
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")
|
@ManagedObject("Mongo Session Manager")
|
||||||
public class MongoSessionManager extends NoSqlSessionManager
|
public class MongoSessionManager extends NoSqlSessionManager
|
||||||
{
|
{
|
||||||
|
@ -54,14 +103,56 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
/*
|
/*
|
||||||
* strings used as keys or parts of keys in mongo
|
* 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__";
|
private final static String __METADATA = "__metadata__";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session id
|
||||||
|
*/
|
||||||
public final static String __ID = "id";
|
public final static String __ID = "id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time of session creation
|
||||||
|
*/
|
||||||
private final static String __CREATED = "created";
|
private final static String __CREATED = "created";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not session is valid
|
||||||
|
*/
|
||||||
public final static String __VALID = "valid";
|
public final static String __VALID = "valid";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time at which session was invalidated
|
||||||
|
*/
|
||||||
public final static String __INVALIDATED = "invalidated";
|
public final static String __INVALIDATED = "invalidated";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last access time of session
|
||||||
|
*/
|
||||||
public final static String __ACCESSED = "accessed";
|
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";
|
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";
|
public final static String __VERSION = __METADATA + ".version";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,8 +161,16 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
private String _contextId = null;
|
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();
|
super.doStart();
|
||||||
String[] hosts = getContextHandler().getVirtualHosts();
|
String[] hosts = getContextHandler().getVirtualHosts();
|
||||||
//TODO: can this be replaced?
|
|
||||||
/*if (hosts == null || hosts.length == 0)
|
|
||||||
hosts = getContextHandler().getConnectorNames();*/
|
|
||||||
if (hosts == null || hosts.length == 0)
|
if (hosts == null || hosts.length == 0)
|
||||||
hosts = new String[]
|
hosts = new String[]
|
||||||
{ "::" }; // IPv6 equiv of 0.0.0.0
|
{ "::" }; // IPv6 equiv of 0.0.0.0
|
||||||
|
@ -102,19 +199,18 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
}
|
}
|
||||||
|
|
||||||
_contextId = createContextId(hosts,contextPath);
|
_contextId = createContextId(hosts,contextPath);
|
||||||
|
_version_1 = new BasicDBObject(getContextAttributeKey(__VERSION),1);
|
||||||
__version_1 = new BasicDBObject(getContextKey(__VERSION),1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/* (non-Javadoc)
|
/**
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#setSessionIdManager(org.eclipse.jetty.server.SessionIdManager)
|
* @see org.eclipse.jetty.server.session.AbstractSessionManager#setSessionIdManager(org.eclipse.jetty.server.SessionIdManager)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setSessionIdManager(SessionIdManager metaManager)
|
public void setSessionIdManager(SessionIdManager metaManager)
|
||||||
{
|
{
|
||||||
MongoSessionIdManager msim = (MongoSessionIdManager)metaManager;
|
MongoSessionIdManager msim = (MongoSessionIdManager)metaManager;
|
||||||
_sessions=msim.getSessions();
|
_dbSessions=msim.getSessions();
|
||||||
super.setSessionIdManager(metaManager);
|
super.setSessionIdManager(metaManager);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -125,7 +221,7 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
__log.debug("MongoSessionManager:save:" + session);
|
__log.debug("MongoSessionManager:save session {}", session.getClusterId());
|
||||||
session.willPassivate();
|
session.willPassivate();
|
||||||
|
|
||||||
// Form query for upsert
|
// Form query for upsert
|
||||||
|
@ -137,9 +233,13 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
BasicDBObject sets = new BasicDBObject();
|
BasicDBObject sets = new BasicDBObject();
|
||||||
BasicDBObject unsets = new BasicDBObject();
|
BasicDBObject unsets = new BasicDBObject();
|
||||||
|
|
||||||
|
|
||||||
// handle valid or invalid
|
// handle valid or invalid
|
||||||
if (session.isValid())
|
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
|
// handle new or existing
|
||||||
if (version == null)
|
if (version == null)
|
||||||
{
|
{
|
||||||
|
@ -148,12 +248,26 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
version = new Long(1);
|
version = new Long(1);
|
||||||
sets.put(__CREATED,session.getCreationTime());
|
sets.put(__CREATED,session.getCreationTime());
|
||||||
sets.put(__VALID,true);
|
sets.put(__VALID,true);
|
||||||
sets.put(getContextKey(__VERSION),version);
|
|
||||||
|
sets.put(getContextAttributeKey(__VERSION),version);
|
||||||
|
sets.put(__MAX_IDLE, getMaxInactiveInterval());
|
||||||
|
sets.put(__EXPIRY, expiry);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
version = new Long(((Number)version).longValue() + 1);
|
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());
|
sets.put(__ACCESSED,session.getAccessed());
|
||||||
|
@ -185,8 +299,8 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
if (!unsets.isEmpty())
|
if (!unsets.isEmpty())
|
||||||
update.put("$unset",unsets);
|
update.put("$unset",unsets);
|
||||||
|
|
||||||
_sessions.update(key,update,upsert,false);
|
_dbSessions.update(key,update,upsert,false);
|
||||||
__log.debug("MongoSessionManager:save:db.sessions.update(" + key + "," + update + ",true)");
|
__log.debug("MongoSessionManager:save:db.sessions.update( {}, {}, true) ", key, update);
|
||||||
|
|
||||||
if (activateAfterSave)
|
if (activateAfterSave)
|
||||||
session.didActivate();
|
session.didActivate();
|
||||||
|
@ -204,20 +318,20 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
@Override
|
@Override
|
||||||
protected Object refresh(NoSqlSession session, Object version)
|
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
|
// check if our in memory version is the same as what is on the disk
|
||||||
if (version != null)
|
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)
|
if (o != null)
|
||||||
{
|
{
|
||||||
Object saved = getNestedValue(o, getContextKey(__VERSION));
|
Object saved = getNestedValue(o, getContextAttributeKey(__VERSION));
|
||||||
|
|
||||||
if (saved != null && saved.equals(version))
|
if (saved != null && saved.equals(version))
|
||||||
{
|
{
|
||||||
__log.debug("MongoSessionManager:refresh not needed");
|
__log.debug("MongoSessionManager:refresh not needed session {}", session.getId());
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
version = saved;
|
version = saved;
|
||||||
|
@ -225,12 +339,12 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are here, we have to load the object
|
// 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 it doesn't exist, invalidate
|
||||||
if (o == null)
|
if (o == null)
|
||||||
{
|
{
|
||||||
__log.debug("MongoSessionManager:refresh:marking invalid, no object");
|
__log.debug("MongoSessionManager:refresh:marking session {} invalid, no object", session.getClusterId());
|
||||||
session.invalidate();
|
session.invalidate();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -239,7 +353,7 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
Boolean valid = (Boolean)o.get(__VALID);
|
Boolean valid = (Boolean)o.get(__VALID);
|
||||||
if (valid == null || !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();
|
session.invalidate();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -253,15 +367,13 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
|
|
||||||
DBObject attrs = (DBObject)getNestedValue(o,getContextKey());
|
DBObject attrs = (DBObject)getNestedValue(o,getContextKey());
|
||||||
|
|
||||||
|
|
||||||
if (attrs != null)
|
if (attrs != null)
|
||||||
{
|
{
|
||||||
for (String name : attrs.keySet())
|
for (String name : attrs.keySet())
|
||||||
{
|
{
|
||||||
|
//skip special metadata field which is not one of the session attributes
|
||||||
if (__METADATA.equals(name))
|
if (__METADATA.equals(name))
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
String attr = decodeName(name);
|
String attr = decodeName(name);
|
||||||
Object value = decodeValue(attrs.get(name));
|
Object value = decodeValue(attrs.get(name));
|
||||||
|
@ -301,7 +413,7 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
update.put("$set",sets);
|
update.put("$set",sets);
|
||||||
}
|
}
|
||||||
|
|
||||||
_sessions.update(key,update,false,false);
|
_dbSessions.update(key,update,false,false);
|
||||||
|
|
||||||
session.didActivate();
|
session.didActivate();
|
||||||
|
|
||||||
|
@ -319,51 +431,51 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
@Override
|
@Override
|
||||||
protected synchronized NoSqlSession loadSession(String clusterId)
|
protected synchronized NoSqlSession loadSession(String clusterId)
|
||||||
{
|
{
|
||||||
DBObject o = _sessions.findOne(new BasicDBObject(__ID,clusterId));
|
DBObject o = _dbSessions.findOne(new BasicDBObject(__ID,clusterId));
|
||||||
|
|
||||||
__log.debug("MongoSessionManager:loaded " + o);
|
|
||||||
|
|
||||||
|
__log.debug("MongoSessionManager:id={} loaded={}", clusterId, o);
|
||||||
if (o == null)
|
if (o == null)
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
Boolean valid = (Boolean)o.get(__VALID);
|
Boolean valid = (Boolean)o.get(__VALID);
|
||||||
|
__log.debug("MongoSessionManager:id={} valid={}", clusterId, valid);
|
||||||
if (valid == null || !valid)
|
if (valid == null || !valid)
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Object version = o.get(getContextKey(__VERSION));
|
Object version = o.get(getContextAttributeKey(__VERSION));
|
||||||
Long created = (Long)o.get(__CREATED);
|
Long created = (Long)o.get(__CREATED);
|
||||||
Long accessed = (Long)o.get(__ACCESSED);
|
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());
|
DBObject attrs = (DBObject)getNestedValue(o,getContextKey());
|
||||||
|
|
||||||
__log.debug("MongoSessionManager:attrs: " + attrs);
|
__log.debug("MongoSessionManager:attrs {}", attrs);
|
||||||
if (attrs != null)
|
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())
|
for (String name : attrs.keySet())
|
||||||
{
|
{
|
||||||
|
//skip special metadata attribute which is not one of the actual session attributes
|
||||||
if ( __METADATA.equals(name) )
|
if ( __METADATA.equals(name) )
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
String attr = decodeName(name);
|
String attr = decodeName(name);
|
||||||
Object value = decodeValue(attrs.get(name));
|
Object value = decodeValue(attrs.get(name));
|
||||||
|
|
||||||
session.doPutOrRemove(attr,value);
|
session.doPutOrRemove(attr,value);
|
||||||
session.bindValue(attr,value);
|
session.bindValue(attr,value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
session.didActivate();
|
||||||
}
|
}
|
||||||
session.didActivate();
|
else
|
||||||
|
__log.debug("MongoSessionManager: session {} not present for context {}",clusterId, getContextKey());
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
@ -374,11 +486,17 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
return null;
|
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
|
@Override
|
||||||
protected boolean remove(NoSqlSession session)
|
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
|
* 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());
|
BasicDBObject key = new BasicDBObject(__ID,session.getClusterId());
|
||||||
|
|
||||||
DBObject o = _sessions.findOne(key,__version_1);
|
DBObject o = _dbSessions.findOne(key,_version_1);
|
||||||
|
|
||||||
if (o != null)
|
if (o != null)
|
||||||
{
|
{
|
||||||
|
@ -394,7 +512,7 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
BasicDBObject unsets = new BasicDBObject();
|
BasicDBObject unsets = new BasicDBObject();
|
||||||
unsets.put(getContextKey(),1);
|
unsets.put(getContextKey(),1);
|
||||||
remove.put("$unset",unsets);
|
remove.put("$unset",unsets);
|
||||||
_sessions.update(key,remove);
|
_dbSessions.update(key,remove);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -404,20 +522,22 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------ */
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see org.eclipse.jetty.nosql.NoSqlSessionManager#expire(java.lang.String)
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void invalidateSession(String idInCluster)
|
protected void expire (String idInCluster)
|
||||||
{
|
{
|
||||||
__log.debug("MongoSessionManager:invalidateSession:invalidating " + idInCluster);
|
__log.debug("MongoSessionManager:expire session {} ", idInCluster);
|
||||||
|
|
||||||
super.invalidateSession(idInCluster);
|
//Expire the session for this context
|
||||||
|
super.expire(idInCluster);
|
||||||
|
|
||||||
/*
|
//If the outer session document has not already been marked invalid, do so.
|
||||||
* pull back the 'valid' value, we can check if its false, if is we don't need to
|
|
||||||
* reset it to false
|
|
||||||
*/
|
|
||||||
DBObject validKey = new BasicDBObject(__VALID, true);
|
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))
|
if (o != null && (Boolean)o.get(__VALID))
|
||||||
{
|
{
|
||||||
|
@ -428,21 +548,24 @@ public class MongoSessionManager extends NoSqlSessionManager
|
||||||
update.put("$set",sets);
|
update.put("$set",sets);
|
||||||
|
|
||||||
BasicDBObject key = new BasicDBObject(__ID,idInCluster);
|
BasicDBObject key = new BasicDBObject(__ID,idInCluster);
|
||||||
|
_dbSessions.update(key,update);
|
||||||
_sessions.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
|
@Override
|
||||||
protected void update(NoSqlSession session, String newClusterId, String newNodeId) throws Exception
|
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 key = new BasicDBObject(__ID, session.getClusterId());
|
||||||
BasicDBObject sets = new BasicDBObject();
|
BasicDBObject sets = new BasicDBObject();
|
||||||
BasicDBObject update = new BasicDBObject(__ID, newClusterId);
|
BasicDBObject update = new BasicDBObject(__ID, newClusterId);
|
||||||
sets.put("$set", update);
|
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")
|
@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")
|
@ManagedAttribute("total number of known sessions in the store")
|
||||||
public long getSessionStoreCount()
|
public long getSessionStoreCount()
|
||||||
{
|
{
|
||||||
return _sessions.find().count();
|
return _dbSessions.find().count();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------ */
|
/*------------------------------------------------------------ */
|
||||||
|
|
|
@ -276,7 +276,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
||||||
{
|
{
|
||||||
super.doStop();
|
super.doStop();
|
||||||
|
|
||||||
invalidateSessions();
|
shutdownSessions();
|
||||||
|
|
||||||
_loader=null;
|
_loader=null;
|
||||||
}
|
}
|
||||||
|
@ -735,7 +735,12 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
||||||
*/
|
*/
|
||||||
public abstract AbstractSession getSession(String idInCluster);
|
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
|
* @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
|
||||||
* {@link SessionIdManager#invalidateAll(String)} should be called.
|
* {@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
|
// Remove session from context and global maps
|
||||||
boolean removed = removeSession(session.getClusterId());
|
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
|
@Override
|
||||||
protected void invalidateSessions() throws Exception
|
protected void shutdownSessions() throws Exception
|
||||||
{
|
{
|
||||||
// Invalidate all sessions to cause unbind events
|
// Invalidate all sessions to cause unbind events
|
||||||
ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values());
|
ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values());
|
||||||
|
@ -398,11 +398,11 @@ public class HashSessionManager extends AbstractSessionManager
|
||||||
// If we are called from doStop
|
// If we are called from doStop
|
||||||
if (isStopping() && _storeDir != null && _storeDir.exists() && _storeDir.canWrite())
|
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)
|
for (HashedSession session : sessions)
|
||||||
{
|
{
|
||||||
session.save(false);
|
session.save(false);
|
||||||
removeSession(session,false);
|
_sessions.remove(session.getClusterId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -27,11 +27,13 @@ import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -72,7 +74,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(JDBCSessionManager.class);
|
private static final Logger LOG = Log.getLogger(JDBCSessionManager.class);
|
||||||
|
|
||||||
private ConcurrentHashMap<String, AbstractSession> _sessions;
|
private ConcurrentHashMap<String, Session> _sessions;
|
||||||
protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
|
protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
|
||||||
protected long _saveIntervalSec = 60; //only persist changes to session access times every 60 secs
|
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
|
//The session attributes have changed, write to the db, ensuring
|
||||||
//http passivation/activation listeners called
|
//http passivation/activation listeners called
|
||||||
willPassivate();
|
save(true);
|
||||||
updateSession(this);
|
|
||||||
didActivate();
|
|
||||||
}
|
}
|
||||||
else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
|
else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
|
||||||
{
|
{
|
||||||
|
@ -362,6 +362,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
|
@Override
|
||||||
protected void timeout() throws IllegalStateException
|
protected void timeout() throws IllegalStateException
|
||||||
{
|
{
|
||||||
|
@ -451,109 +468,110 @@ public class JDBCSessionManager extends AbstractSessionManager
|
||||||
public Session getSession(String idInCluster)
|
public Session getSession(String idInCluster)
|
||||||
{
|
{
|
||||||
Session session = null;
|
Session session = null;
|
||||||
Session memSession = (Session)_sessions.get(idInCluster);
|
|
||||||
|
|
||||||
synchronized (this)
|
synchronized (this)
|
||||||
{
|
{
|
||||||
//check if we need to reload the session -
|
Session memSession = (Session)_sessions.get(idInCluster);
|
||||||
//as an optimization, don't reload on every access
|
|
||||||
//to reduce the load on the database. This introduces a window of
|
//check if we need to reload the session -
|
||||||
//possibility that the node may decide that the session is local to it,
|
//as an optimization, don't reload on every access
|
||||||
//when the session has actually been live on another node, and then
|
//to reduce the load on the database. This introduces a window of
|
||||||
//re-migrated to this node. This should be an extremely rare occurrence,
|
//possibility that the node may decide that the session is local to it,
|
||||||
//as load-balancers are generally well-behaved and consistently send
|
//when the session has actually been live on another node, and then
|
||||||
//sessions to the same node, changing only iff that node fails.
|
//re-migrated to this node. This should be an extremely rare occurrence,
|
||||||
//Session data = null;
|
//as load-balancers are generally well-behaved and consistently send
|
||||||
long now = System.currentTimeMillis();
|
//sessions to the same node, changing only iff that node fails.
|
||||||
if (LOG.isDebugEnabled())
|
//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
|
||||||
|
{
|
||||||
|
if (memSession==null)
|
||||||
{
|
{
|
||||||
if (memSession==null)
|
LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
|
||||||
LOG.debug("getSession("+idInCluster+"): not in session map,"+
|
session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
|
||||||
" 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));
|
|
||||||
}
|
}
|
||||||
|
else if ((now - memSession._lastSaved) >= (_saveIntervalSec * 1000L))
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if (memSession==null)
|
LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
|
||||||
{
|
session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//No session in db with matching id and context path.
|
LOG.debug("getSession("+idInCluster+"): session in session map");
|
||||||
LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
|
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
|
@Override
|
||||||
public int getSessions()
|
public int getSessions()
|
||||||
{
|
{
|
||||||
int size = 0;
|
return _sessions.size();
|
||||||
synchronized (this)
|
|
||||||
{
|
|
||||||
size = _sessions.size();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -588,7 +601,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
||||||
|
|
||||||
_jdbcSessionIdMgr = (JDBCSessionIdManager)_sessionIdManager;
|
_jdbcSessionIdMgr = (JDBCSessionIdManager)_sessionIdManager;
|
||||||
|
|
||||||
_sessions = new ConcurrentHashMap<String, AbstractSession>();
|
_sessions = new ConcurrentHashMap<String, Session>();
|
||||||
|
|
||||||
super.doStart();
|
super.doStart();
|
||||||
}
|
}
|
||||||
|
@ -602,21 +615,46 @@ public class JDBCSessionManager extends AbstractSessionManager
|
||||||
@Override
|
@Override
|
||||||
public void doStop() throws Exception
|
public void doStop() throws Exception
|
||||||
{
|
{
|
||||||
|
super.doStop();
|
||||||
_sessions.clear();
|
_sessions.clear();
|
||||||
_sessions = null;
|
_sessions = null;
|
||||||
|
|
||||||
super.doStop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void invalidateSessions()
|
protected void shutdownSessions()
|
||||||
{
|
{
|
||||||
//Do nothing - we don't want to remove and
|
//Save the current state of all of our sessions,
|
||||||
//invalidate all the sessions because this
|
//do NOT delete them (so other nodes can manage them)
|
||||||
//method is called from doStop(), and just
|
long gracefulStopMs = getContextHandler().getServer().getStopTimeout();
|
||||||
//because this context is stopping does not
|
long stopTime = 0;
|
||||||
//mean that we should remove the session from
|
if (gracefulStopMs > 0)
|
||||||
//any other nodes
|
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)
|
public void renewSessionId (String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
|
||||||
{
|
{
|
||||||
Session session = null;
|
Session session = null;
|
||||||
synchronized (this)
|
try
|
||||||
{
|
{
|
||||||
try
|
session = (Session)_sessions.remove(oldClusterId);
|
||||||
|
if (session != null)
|
||||||
{
|
{
|
||||||
session = (Session)_sessions.remove(oldClusterId);
|
synchronized (session)
|
||||||
if (session != null)
|
|
||||||
{
|
{
|
||||||
session.setClusterId(newClusterId); //update ids
|
session.setClusterId(newClusterId); //update ids
|
||||||
session.setNodeId(newNodeId);
|
session.setNodeId(newNodeId);
|
||||||
_sessions.put(newClusterId, session); //put it into list in memory
|
_sessions.put(newClusterId, session); //put it into list in memory
|
||||||
session.save(); //update database
|
updateSession(session); //update database
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
}
|
||||||
{
|
catch (Exception e)
|
||||||
LOG.warn(e);
|
{
|
||||||
}
|
LOG.warn(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
|
super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
|
||||||
|
@ -658,11 +696,7 @@ public class JDBCSessionManager extends AbstractSessionManager
|
||||||
*/
|
*/
|
||||||
protected void invalidateSession (String idInCluster)
|
protected void invalidateSession (String idInCluster)
|
||||||
{
|
{
|
||||||
Session session = null;
|
Session session = (Session)_sessions.get(idInCluster);
|
||||||
synchronized (this)
|
|
||||||
{
|
|
||||||
session = (Session)_sessions.get(idInCluster);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (session != null)
|
if (session != null)
|
||||||
{
|
{
|
||||||
|
@ -679,20 +713,17 @@ public class JDBCSessionManager extends AbstractSessionManager
|
||||||
@Override
|
@Override
|
||||||
protected boolean removeSession(String idInCluster)
|
protected boolean removeSession(String idInCluster)
|
||||||
{
|
{
|
||||||
synchronized (this)
|
Session session = (Session)_sessions.remove(idInCluster);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Session session = (Session)_sessions.remove(idInCluster);
|
if (session != null)
|
||||||
try
|
deleteSession(session);
|
||||||
{
|
|
||||||
if (session != null)
|
|
||||||
deleteSession(session);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOG.warn("Problem deleting session id="+idInCluster, e);
|
|
||||||
}
|
|
||||||
return session!=null;
|
|
||||||
}
|
}
|
||||||
|
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)
|
if (session==null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
synchronized (this)
|
_sessions.put(session.getClusterId(), (Session)session);
|
||||||
{
|
|
||||||
_sessions.put(session.getClusterId(), 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
|
try
|
||||||
{
|
{
|
||||||
synchronized (session)
|
synchronized (session)
|
||||||
|
@ -748,40 +774,20 @@ public class JDBCSessionManager extends AbstractSessionManager
|
||||||
* {@link SessionIdManager#invalidateAll(String)} should be called.
|
* {@link SessionIdManager#invalidateAll(String)} should be called.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void removeSession(AbstractSession session, boolean invalidate)
|
public boolean removeSession(AbstractSession session, boolean invalidate)
|
||||||
{
|
{
|
||||||
// Remove session from context and global maps
|
// Remove session from context and global maps
|
||||||
boolean removed = false;
|
boolean removed = super.removeSession(session, invalidate);
|
||||||
|
|
||||||
synchronized (this)
|
|
||||||
{
|
|
||||||
//take this session out of the map of sessions for this context
|
|
||||||
if (_sessions.containsKey(session.getClusterId()))
|
|
||||||
{
|
|
||||||
removed = true;
|
|
||||||
removeSession(session.getClusterId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (removed)
|
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)
|
if (!invalidate)
|
||||||
{
|
{
|
||||||
session.willPassivate();
|
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
|
//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
|
//treat it like a normal expired session
|
||||||
synchronized (this)
|
_sessions.put(session.getClusterId(), session);
|
||||||
{
|
|
||||||
_sessions.put(session.getClusterId(), session);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -122,10 +122,10 @@ public class SessionCookieTest
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see org.eclipse.jetty.server.session.AbstractSessionManager#invalidateSessions()
|
* @see org.eclipse.jetty.server.session.AbstractSessionManager#shutdownSessions()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void invalidateSessions() throws Exception
|
protected void shutdownSessions() throws Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,6 @@ public class JdbcTestServer extends AbstractTestServer
|
||||||
idManager.setScavengeInterval(_scavengePeriod);
|
idManager.setScavengeInterval(_scavengePeriod);
|
||||||
idManager.setWorkerName("w"+(__workers++));
|
idManager.setWorkerName("w"+(__workers++));
|
||||||
idManager.setDriverInfo(DRIVER_CLASS, (config==null?DEFAULT_CONNECTION_URL:config));
|
idManager.setDriverInfo(DRIVER_CLASS, (config==null?DEFAULT_CONNECTION_URL:config));
|
||||||
//System.err.println("new jdbcidmgr inst="+idManager);
|
|
||||||
return idManager;
|
return idManager;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +101,7 @@ public class JdbcTestServer extends AbstractTestServer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean existsInSessionTable(String id)
|
public boolean existsInSessionIdTable(String id)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
Class.forName(DRIVER_CLASS);
|
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 ()
|
public Set<String> getSessionIds ()
|
||||||
throws Exception
|
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>
|
<parent>
|
||||||
<groupId>org.eclipse.jetty.tests</groupId>
|
<groupId>org.eclipse.jetty.tests</groupId>
|
||||||
<artifactId>test-sessions-parent</artifactId>
|
<artifactId>test-sessions-parent</artifactId>
|
||||||
<version>9.1.0-SNAPSHOT</version>
|
<version>9.1.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>test-mongodb-sessions</artifactId>
|
<artifactId>test-mongodb-sessions</artifactId>
|
||||||
<name>Jetty Tests :: Sessions :: Mongo</name>
|
<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
|
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);
|
MongoSessionIdManager idManager = new MongoSessionIdManager(_server);
|
||||||
idManager.setWorkerName("w"+(__workers++));
|
idManager.setWorkerName("w"+(__workers++));
|
||||||
idManager.setScavengeDelay((_scavengePeriod));
|
idManager.setScavengeDelay((_scavengePeriod));
|
||||||
idManager.setScavengePeriod(_maxInactivePeriod);
|
idManager.setScavengePeriod(_scavengePeriod);
|
||||||
|
|
||||||
return idManager;
|
return idManager;
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,26 +145,31 @@ public class PurgeInvalidSessionTest
|
||||||
if ("create".equals(action))
|
if ("create".equals(action))
|
||||||
{
|
{
|
||||||
HttpSession session = request.getSession(true);
|
HttpSession session = request.getSession(true);
|
||||||
|
session.setAttribute("foo", "bar");
|
||||||
assertTrue(session.isNew());
|
assertTrue(session.isNew());
|
||||||
}
|
}
|
||||||
else if ("invalidate".equals(action))
|
else if ("invalidate".equals(action))
|
||||||
{
|
{
|
||||||
HttpSession existingSession = request.getSession(false);
|
HttpSession existingSession = request.getSession(false);
|
||||||
assertNotNull(existingSession);
|
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();
|
existingSession.invalidate();
|
||||||
String id = request.getRequestedSessionId();
|
|
||||||
assertNotNull(id);
|
|
||||||
id = id.substring(0, id.indexOf("."));
|
|
||||||
|
|
||||||
//still in db, just marked as invalid
|
//still in db, just marked as invalid
|
||||||
DBObject dbSession = _sessions.findOne(new BasicDBObject("id", id));
|
dbSession = _sessions.findOne(new BasicDBObject("id", id));
|
||||||
assertTrue(dbSession != null);
|
assertNotNull(dbSession);
|
||||||
|
assertTrue(dbSession.containsField(MongoSessionManager.__INVALIDATED));
|
||||||
}
|
}
|
||||||
else if ("test".equals(action))
|
else if ("test".equals(action))
|
||||||
{
|
{
|
||||||
String id = request.getRequestedSessionId();
|
String id = request.getRequestedSessionId();
|
||||||
assertNotNull(id);
|
assertNotNull(id);
|
||||||
id = id.substring(0, id.indexOf("."));
|
|
||||||
|
id = (id.indexOf(".") > 0?id.substring(0, id.indexOf(".")):id);
|
||||||
|
|
||||||
HttpSession existingSession = request.getSession(false);
|
HttpSession existingSession = request.getSession(false);
|
||||||
assertTrue(existingSession == null);
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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.client.api.Request;
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,7 +81,7 @@ public abstract class AbstractSessionInvalidateAndCreateTest
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Thread.sleep(scavengePeriod * 2500L);
|
Thread.sleep(scavengePeriod * 3000L);
|
||||||
}
|
}
|
||||||
catch (InterruptedException e)
|
catch (InterruptedException e)
|
||||||
{
|
{
|
||||||
|
@ -128,7 +131,7 @@ public abstract class AbstractSessionInvalidateAndCreateTest
|
||||||
ContentResponse response2 = request2.send();
|
ContentResponse response2 = request2.send();
|
||||||
assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
|
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);
|
pause(scavengePeriod);
|
||||||
|
|
||||||
//test that the session created in the last test is scavenged:
|
//test that the session created in the last test is scavenged:
|
||||||
|
@ -153,6 +156,20 @@ public abstract class AbstractSessionInvalidateAndCreateTest
|
||||||
{
|
{
|
||||||
private boolean unbound = false;
|
private boolean unbound = false;
|
||||||
|
|
||||||
|
public class MySessionBindingListener implements HttpSessionBindingListener, Serializable
|
||||||
|
{
|
||||||
|
|
||||||
|
public void valueUnbound(HttpSessionBindingEvent event)
|
||||||
|
{
|
||||||
|
unbound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void valueBound(HttpSessionBindingEvent event)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
|
protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
|
||||||
{
|
{
|
||||||
|
@ -167,25 +184,16 @@ public abstract class AbstractSessionInvalidateAndCreateTest
|
||||||
HttpSession session = request.getSession(false);
|
HttpSession session = request.getSession(false);
|
||||||
if (session != null)
|
if (session != null)
|
||||||
{
|
{
|
||||||
|
//invalidate existing session
|
||||||
session.invalidate();
|
session.invalidate();
|
||||||
|
|
||||||
//now make a new session
|
//now make a new session
|
||||||
session = request.getSession(true);
|
session = request.getSession(true);
|
||||||
session.setAttribute("identity", "session2");
|
session.setAttribute("identity", "session2");
|
||||||
session.setAttribute("listener", new HttpSessionBindingListener()
|
session.setAttribute("listener", new MySessionBindingListener());
|
||||||
{
|
|
||||||
|
|
||||||
public void valueUnbound(HttpSessionBindingEvent event)
|
|
||||||
{
|
|
||||||
unbound = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void valueBound(HttpSessionBindingEvent event)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
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