Issue #2646 - Better handle concurrent calls to change session id and invalidate within a context (#2670)

* Issue #2646 handle concurrent invalidate/changeid calls

Signed-off-by: Jan Bartel <janb@webtide.com>
This commit is contained in:
Jan Bartel 2018-11-23 16:15:27 +01:00 committed by GitHub
parent 54319589a4
commit 4e672c6b27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 649 additions and 316 deletions

View File

@ -151,8 +151,9 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
/** /**
* Remove the session with this identity from the store * Remove the session with this identity from the store
*
* @param id the id * @param id the id
* @return true if removed false otherwise * @return Session that was removed or null
*/ */
public abstract Session doDelete (String id); public abstract Session doDelete (String id);
@ -727,12 +728,8 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
/**
* @see org.eclipse.jetty.server.session.SessionCache#renewSessionId(java.lang.String, java.lang.String)
*/
@Override @Override
public Session renewSessionId (String oldId, String newId) public Session renewSessionId (String oldId, String newId, String oldExtendedId, String newExtendedId)
throws Exception throws Exception
{ {
if (StringUtil.isBlank(oldId)) if (StringUtil.isBlank(oldId))
@ -741,17 +738,40 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
throw new IllegalArgumentException ("New session id is null"); throw new IllegalArgumentException ("New session id is null");
Session session = get(oldId); Session session = get(oldId);
renewSessionId(session, newId, newExtendedId);
return session;
}
/**
* Swap the id on a session.
*
* @param session the session for which to do the swap
* @param newId the new id
* @param newExtendedId the full id plus node id
*
* @throws Exception if there was a failure saving the change
*/
protected void renewSessionId (Session session, String newId, String newExtendedId)
throws Exception
{
if (session == null) if (session == null)
return null; return;
try (Lock lock = session.lock()) try (Lock lock = session.lock())
{ {
String oldId = session.getId();
session.checkValidForWrite(); //can't change id on invalid session session.checkValidForWrite(); //can't change id on invalid session
session.getSessionData().setId(newId); session.getSessionData().setId(newId);
session.getSessionData().setLastSaved(0); //pretend that the session has never been saved before to get a full save session.getSessionData().setLastSaved(0); //pretend that the session has never been saved before to get a full save
session.getSessionData().setDirty(true); //ensure we will try to write the session out session.getSessionData().setDirty(true); //ensure we will try to write the session out
session.setExtendedId(newExtendedId); //remember the new extended id
session.setIdChanged(true); //session id changed
doPutIfAbsent(newId, session); //put the new id into our map doPutIfAbsent(newId, session); //put the new id into our map
doDelete (oldId); //take old out of map doDelete (oldId); //take old out of map
if (_sessionDataStore != null) if (_sessionDataStore != null)
{ {
_sessionDataStore.delete(oldId); //delete the session data with the old id _sessionDataStore.delete(oldId); //delete the session data with the old id
@ -759,7 +779,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
} }
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug ("Session id {} swapped for new id {}", oldId, newId); LOG.debug ("Session id {} swapped for new id {}", oldId, newId);
return session;
} }
} }

View File

@ -25,6 +25,7 @@ import java.util.Enumeration;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -41,20 +42,17 @@ import org.eclipse.jetty.util.thread.Locker;
import org.eclipse.jetty.util.thread.Locker.Lock; import org.eclipse.jetty.util.thread.Locker.Lock;
/** /**
* Session * Session
* *
* A heavy-weight Session object representing a HttpSession. Session objects * A heavy-weight Session object representing a HttpSession. Session objects
* relating to a context are kept in a {@link SessionCache}. The purpose of * relating to a context are kept in a {@link SessionCache}. The purpose of the
* the SessionCache is to keep the working set of Session objects in memory * SessionCache is to keep the working set of Session objects in memory so that
* so that they may be accessed quickly, and facilitate the sharing of a * they may be accessed quickly, and facilitate the sharing of a Session object
* Session object amongst multiple simultaneous requests referring to the * amongst multiple simultaneous requests referring to the same session id.
* same session id.
* *
* The {@link SessionHandler} coordinates * The {@link SessionHandler} coordinates the lifecycle of Session objects with
* the lifecycle of Session objects with the help of the SessionCache. * the help of the SessionCache.
* *
* @see SessionHandler * @see SessionHandler
* @see org.eclipse.jetty.server.SessionIdManager * @see org.eclipse.jetty.server.SessionIdManager
@ -63,44 +61,54 @@ public class Session implements SessionHandler.SessionIf
{ {
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
/** /**
* *
*/ */
public final static String SESSION_CREATED_SECURE="org.eclipse.jetty.security.sessionCreatedSecure"; public final static String SESSION_CREATED_SECURE = "org.eclipse.jetty.security.sessionCreatedSecure";
/** /**
* State * State
* *
* Validity states of a session * Validity states of a session
*/ */
public enum State {VALID, INVALID, INVALIDATING}; public enum State
{
VALID, INVALID, INVALIDATING, CHANGING
};
public enum IdState
{
SET, CHANGING
};
protected final SessionData _sessionData; // the actual data associated with
// a session
protected final SessionHandler _handler; // the manager of the session
protected String _extendedId; // the _id plus the worker name
protected final SessionData _sessionData; //the actual data associated with a session
protected final SessionHandler _handler; //the manager of the session
protected String _extendedId; //the _id plus the worker name
protected long _requests; protected long _requests;
protected boolean _idChanged; protected boolean _idChanged;
protected boolean _newSession; protected boolean _newSession;
protected State _state = State.VALID; //state of the session:valid,invalid or being invalidated
protected Locker _lock = new Locker(); //sync lock protected State _state = State.VALID; // state of the session:valid,invalid
// or being invalidated
protected Locker _lock = new Locker(); // sync lock
protected Condition _stateChangeCompleted = _lock.newCondition();
protected boolean _resident = false; protected boolean _resident = false;
protected final SessionInactivityTimer _sessionInactivityTimer; protected final SessionInactivityTimer _sessionInactivityTimer;
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* SessionInactivityTimer * SessionInactivityTimer
* *
* Each Session has a timer associated with it that fires whenever * Each Session has a timer associated with it that fires whenever it has
* it has been idle (ie not accessed by a request) for a * been idle (ie not accessed by a request) for a configurable amount of
* configurable amount of time, or the Session expires. * time, or the Session expires.
* *
* @see SessionCache * @see SessionCache
* *
@ -117,7 +125,8 @@ public class Session implements SessionHandler.SessionIf
@Override @Override
public void onTimeoutExpired() public void onTimeoutExpired()
{ {
if (LOG.isDebugEnabled()) LOG.debug("Timer expired for session {}", getId()); if (LOG.isDebugEnabled())
LOG.debug("Timer expired for session {}", getId());
getSessionHandler().sessionInactivityTimerExpired(Session.this); getSessionHandler().sessionInactivityTimerExpired(Session.this);
} }
@ -126,45 +135,47 @@ public class Session implements SessionHandler.SessionIf
/** /**
* @param ms the timeout to set; -1 means that the timer will not be scheduled * @param ms the timeout to set; -1 means that the timer will not be
* scheduled
*/ */
public void setTimeout(long ms) public void setTimeout(long ms)
{ {
_msec = ms; _msec = ms;
if (LOG.isDebugEnabled()) LOG.debug("Session {} timer={}ms",getId(), ms); if (LOG.isDebugEnabled())
LOG.debug("Session {} timer={}ms", getId(), ms);
} }
public void schedule()
public void schedule ()
{ {
if (_msec > 0) if (_msec > 0)
{ {
if (LOG.isDebugEnabled()) LOG.debug("(Re)starting timer for session {} at {}ms",getId(), _msec); if (LOG.isDebugEnabled())
LOG.debug("(Re)starting timer for session {} at {}ms", getId(), _msec);
_timer.schedule(_msec, TimeUnit.MILLISECONDS); _timer.schedule(_msec, TimeUnit.MILLISECONDS);
} }
else else
{ {
if (LOG.isDebugEnabled()) LOG.debug("Not starting timer for session {}",getId()); if (LOG.isDebugEnabled())
LOG.debug("Not starting timer for session {}", getId());
} }
} }
public void cancel() public void cancel()
{ {
_timer.cancel(); _timer.cancel();
if (LOG.isDebugEnabled()) LOG.debug("Cancelled timer for session {}",getId()); if (LOG.isDebugEnabled())
LOG.debug("Cancelled timer for session {}", getId());
} }
public void destroy()
public void destroy ()
{ {
_timer.destroy(); _timer.destroy();
if (LOG.isDebugEnabled()) LOG.debug("Destroyed timer for session {}",getId()); if (LOG.isDebugEnabled())
LOG.debug("Destroyed timer for session {}", getId());
} }
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* Create a new session * Create a new session
@ -172,18 +183,18 @@ public class Session implements SessionHandler.SessionIf
* @param request the request the session should be based on * @param request the request the session should be based on
* @param data the session data * @param data the session data
*/ */
public Session (SessionHandler handler, HttpServletRequest request, SessionData data) public Session(SessionHandler handler, HttpServletRequest request, SessionData data)
{ {
_handler = handler; _handler = handler;
_sessionData = data; _sessionData = data;
_newSession = true; _newSession = true;
_sessionData.setDirty(true); _sessionData.setDirty(true);
_requests = 1; //access will not be called on this new session, but we are obviously in a request _requests = 1; // access will not be called on this new session, but we
// are obviously in a request
_sessionInactivityTimer = new SessionInactivityTimer(); _sessionInactivityTimer = new SessionInactivityTimer();
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* Re-inflate an existing session from some eg persistent store. * Re-inflate an existing session from some eg persistent store.
@ -191,7 +202,7 @@ public class Session implements SessionHandler.SessionIf
* @param handler the SessionHandler managing the session * @param handler the SessionHandler managing the session
* @param data the session data * @param data the session data
*/ */
public Session (SessionHandler handler, SessionData data) public Session(SessionHandler handler, SessionData data)
{ {
_handler = handler; _handler = handler;
_sessionData = data; _sessionData = data;
@ -201,8 +212,7 @@ public class Session implements SessionHandler.SessionIf
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* Returns the current number of requests that are active in the * Returns the current number of requests that are active in the Session.
* Session.
* *
* @return the number of active requests for this session * @return the number of active requests for this session
*/ */
@ -216,10 +226,8 @@ public class Session implements SessionHandler.SessionIf
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
public void setExtendedId (String extendedId) public void setExtendedId(String extendedId)
{ {
_extendedId = extendedId; _extendedId = extendedId;
} }
@ -239,7 +247,7 @@ public class Session implements SessionHandler.SessionIf
{ {
if (!isValid()) if (!isValid())
return false; return false;
_newSession=false; _newSession = false;
long lastAccessed = _sessionData.getAccessed(); long lastAccessed = _sessionData.getAccessed();
_sessionData.setAccessed(time); _sessionData.setAccessed(time);
_sessionData.setLastAccessed(lastAccessed); _sessionData.setLastAccessed(lastAccessed);
@ -251,8 +259,9 @@ public class Session implements SessionHandler.SessionIf
} }
_requests++; _requests++;
//temporarily stop the idle timer // temporarily stop the idle timer
if (LOG.isDebugEnabled()) LOG.debug("Session {} accessed, stopping timer, active requests={}",getId(),_requests); if (LOG.isDebugEnabled())
LOG.debug("Session {} accessed, stopping timer, active requests={}", getId(), _requests);
_sessionInactivityTimer.cancel(); _sessionInactivityTimer.cancel();
@ -267,18 +276,19 @@ public class Session implements SessionHandler.SessionIf
{ {
_requests--; _requests--;
if (LOG.isDebugEnabled()) LOG.debug("Session {} complete, active requests={}",getId(),_requests); if (LOG.isDebugEnabled())
LOG.debug("Session {} complete, active requests={}", getId(), _requests);
//start the inactivity timer // start the inactivity timer
if (_requests == 0) if (_requests == 0)
_sessionInactivityTimer.schedule(); _sessionInactivityTimer.schedule();
} }
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** Check to see if session has expired as at the time given. /**
* Check to see if session has expired as at the time given.
* *
* @param time the time since the epoch in ms * @param time the time since the epoch in ms
* @return true if expired * @return true if expired
@ -291,82 +301,84 @@ public class Session implements SessionHandler.SessionIf
} }
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** Check if the Session has been idle longer than a number of seconds. /**
* Check if the Session has been idle longer than a number of seconds.
* *
* @param sec the number of seconds * @param sec the number of seconds
* @return true if the session has been idle longer than the interval * @return true if the session has been idle longer than the interval
*/ */
protected boolean isIdleLongerThan (int sec) protected boolean isIdleLongerThan(int sec)
{ {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{ {
return ((_sessionData.getAccessed() + (sec*1000)) <= now); return ((_sessionData.getAccessed() + (sec * 1000)) <= now);
} }
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** /**
* Call binding and attribute listeners based on the new and old * Call binding and attribute listeners based on the new and old values of
* values of the attribute. * the attribute.
* *
* @param name name of the attribute * @param name name of the attribute
* @param newValue new value of the attribute * @param newValue new value of the attribute
* @param oldValue previous value of the attribute * @param oldValue previous value of the attribute
* @throws IllegalStateException if no session manager can be find * @throws IllegalStateException if no session manager can be find
*/ */
protected void callSessionAttributeListeners (String name, Object newValue, Object oldValue) protected void callSessionAttributeListeners(String name, Object newValue, Object oldValue)
{ {
if (newValue==null || !newValue.equals(oldValue)) if (newValue == null || !newValue.equals(oldValue))
{ {
if (oldValue!=null) if (oldValue != null)
unbindValue(name,oldValue); unbindValue(name, oldValue);
if (newValue!=null) if (newValue != null)
bindValue(name,newValue); bindValue(name, newValue);
if (_handler == null) if (_handler == null)
throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); throw new IllegalStateException("No session manager for session " + _sessionData.getId());
_handler.doSessionAttributeListeners(this,name,oldValue,newValue); _handler.doSessionAttributeListeners(this, name, oldValue, newValue);
} }
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* Unbind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)}) * Unbind value if value implements {@link HttpSessionBindingListener}
* (calls
* {@link HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)})
*
* @param name the name with which the object is bound or unbound * @param name the name with which the object is bound or unbound
* @param value the bound value * @param value the bound value
*/ */
public void unbindValue(java.lang.String name, Object value) public void unbindValue(java.lang.String name, Object value)
{ {
if (value!=null&&value instanceof HttpSessionBindingListener) if (value != null && value instanceof HttpSessionBindingListener)
((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this,name)); ((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name));
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* Bind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)}) * Bind value if value implements {@link HttpSessionBindingListener} (calls
* {@link HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)})
*
* @param name the name with which the object is bound or unbound * @param name the name with which the object is bound or unbound
* @param value the bound value * @param value the bound value
*/ */
public void bindValue(java.lang.String name, Object value) public void bindValue(java.lang.String name, Object value)
{ {
if (value!=null&&value instanceof HttpSessionBindingListener) if (value != null && value instanceof HttpSessionBindingListener)
((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this,name)); ((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(this, name));
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* Call the activation listeners. This must be called holding the * Call the activation listeners. This must be called holding the lock.
* lock.
*/ */
public void didActivate() public void didActivate()
{ {
@ -385,8 +397,7 @@ public class Session implements SessionHandler.SessionIf
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* Call the passivation listeners. This must be called holding the * Call the passivation listeners. This must be called holding the lock
* lock
*/ */
public void willPassivate() public void willPassivate()
{ {
@ -407,10 +418,16 @@ public class Session implements SessionHandler.SessionIf
{ {
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{ {
return _state==State.VALID; return _state == State.VALID;
} }
} }
/* ------------------------------------------------------------ */
public boolean isChanging()
{
checkLocked();
return _state == State.CHANGING;
}
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
public long getCookieSetTime() public long getCookieSetTime()
@ -433,8 +450,6 @@ public class Session implements SessionHandler.SessionIf
} }
} }
/** /**
* @see javax.servlet.http.HttpSession#getId() * @see javax.servlet.http.HttpSession#getId()
*/ */
@ -458,8 +473,7 @@ public class Session implements SessionHandler.SessionIf
return _sessionData.getContextPath(); return _sessionData.getContextPath();
} }
public String getVHost()
public String getVHost ()
{ {
return _sessionData.getVhost(); return _sessionData.getVhost();
} }
@ -484,7 +498,7 @@ public class Session implements SessionHandler.SessionIf
public ServletContext getServletContext() public ServletContext getServletContext()
{ {
if (_handler == null) if (_handler == null)
throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); throw new IllegalStateException("No session manager for session " + _sessionData.getId());
return _handler._context; return _handler._context;
} }
@ -496,7 +510,7 @@ public class Session implements SessionHandler.SessionIf
{ {
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{ {
_sessionData.setMaxInactiveMs((long)secs*1000L); _sessionData.setMaxInactiveMs((long) secs * 1000L);
_sessionData.calcAndSetExpiry(); _sessionData.calcAndSetExpiry();
_sessionData.setDirty(true); _sessionData.setDirty(true);
updateInactivityTimer(); updateInactivityTimer();
@ -510,12 +524,11 @@ public class Session implements SessionHandler.SessionIf
} }
} }
/** /**
* Set the inactivity timer to the smaller of the session maxInactivity * Set the inactivity timer to the smaller of the session maxInactivity (ie
* (ie session-timeout from web.xml), or the inactive eviction time. * session-timeout from web.xml), or the inactive eviction time.
*/ */
public void updateInactivityTimer () public void updateInactivityTimer()
{ {
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{ {
@ -524,47 +537,53 @@ public class Session implements SessionHandler.SessionIf
if (maxInactive <= 0) if (maxInactive <= 0)
{ {
//sessions are immortal, they never expire // sessions are immortal, they never expire
if (evictionPolicy < SessionCache.EVICT_ON_INACTIVITY) if (evictionPolicy < SessionCache.EVICT_ON_INACTIVITY)
{ {
//we do not want to evict inactive sessions // we do not want to evict inactive sessions
_sessionInactivityTimer.setTimeout(-1); _sessionInactivityTimer.setTimeout(-1);
if (LOG.isDebugEnabled()) LOG.debug("Session {} is immortal && no inactivity eviction", getId()); if (LOG.isDebugEnabled())
LOG.debug("Session {} is immortal && no inactivity eviction", getId());
} }
else else
{ {
//sessions are immortal but we want to evict after inactivity // sessions are immortal but we want to evict after
// inactivity
_sessionInactivityTimer.setTimeout(TimeUnit.SECONDS.toMillis(evictionPolicy)); _sessionInactivityTimer.setTimeout(TimeUnit.SECONDS.toMillis(evictionPolicy));
if (LOG.isDebugEnabled()) LOG.debug("Session {} is immortal; evict after {} sec inactivity", getId(), evictionPolicy); if (LOG.isDebugEnabled())
LOG.debug("Session {} is immortal; evict after {} sec inactivity", getId(), evictionPolicy);
} }
} }
else else
{ {
//sessions are not immortal // sessions are not immortal
if (evictionPolicy == SessionCache.NEVER_EVICT) if (evictionPolicy == SessionCache.NEVER_EVICT)
{ {
//timeout is just the maxInactive setting // timeout is just the maxInactive setting
_sessionInactivityTimer.setTimeout(_sessionData.getMaxInactiveMs()); _sessionInactivityTimer.setTimeout(_sessionData.getMaxInactiveMs());
if (LOG.isDebugEnabled()) LOG.debug("Session {} no eviction", getId()); if (LOG.isDebugEnabled())
LOG.debug("Session {} no eviction", getId());
} }
else if (evictionPolicy == SessionCache.EVICT_ON_SESSION_EXIT) else if (evictionPolicy == SessionCache.EVICT_ON_SESSION_EXIT)
{ {
//session will not remain in the cache, so no timeout // session will not remain in the cache, so no timeout
_sessionInactivityTimer.setTimeout(-1); _sessionInactivityTimer.setTimeout(-1);
if (LOG.isDebugEnabled()) LOG.debug("Session {} evict on exit", getId()); if (LOG.isDebugEnabled())
LOG.debug("Session {} evict on exit", getId());
} }
else else
{ {
//want to evict on idle: timer is lesser of the session's maxInactive and eviction timeout // want to evict on idle: timer is lesser of the session's
// maxInactive and eviction timeout
_sessionInactivityTimer.setTimeout(Math.min(maxInactive, TimeUnit.SECONDS.toMillis(evictionPolicy))); _sessionInactivityTimer.setTimeout(Math.min(maxInactive, TimeUnit.SECONDS.toMillis(evictionPolicy)));
if (LOG.isDebugEnabled()) LOG.debug("Session {} timer set to lesser of maxInactive={} and inactivityEvict={}", getId(), maxInactive, evictionPolicy); if (LOG.isDebugEnabled())
LOG.debug("Session {} timer set to lesser of maxInactive={} and inactivityEvict={}", getId(), maxInactive, evictionPolicy);
} }
} }
} }
} }
/** /**
* @see javax.servlet.http.HttpSession#getMaxInactiveInterval() * @see javax.servlet.http.HttpSession#getMaxInactiveInterval()
*/ */
@ -574,7 +593,7 @@ public class Session implements SessionHandler.SessionIf
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{ {
long maxInactiveMs = _sessionData.getMaxInactiveMs(); long maxInactiveMs = _sessionData.getMaxInactiveMs();
return (int)(maxInactiveMs < 0 ? -1 : maxInactiveMs/1000); return (int) (maxInactiveMs < 0 ? -1 : maxInactiveMs / 1000);
} }
} }
@ -595,7 +614,6 @@ public class Session implements SessionHandler.SessionIf
return _handler; return _handler;
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* Check that the session can be modified. * Check that the session can be modified.
@ -607,40 +625,58 @@ public class Session implements SessionHandler.SessionIf
checkLocked(); checkLocked();
if (_state == State.INVALID) if (_state == State.INVALID)
throw new IllegalStateException("Not valid for write: id="+_sessionData.getId()+" created="+_sessionData.getCreated()+" accessed="+_sessionData.getAccessed()+" lastaccessed="+_sessionData.getLastAccessed()+" maxInactiveMs="+_sessionData.getMaxInactiveMs()+" expiry="+_sessionData.getExpiry()); throw new IllegalStateException("Not valid for write: id=" + _sessionData.getId()
+ " created="
+ _sessionData.getCreated()
+ " accessed="
+ _sessionData.getAccessed()
+ " lastaccessed="
+ _sessionData.getLastAccessed()
+ " maxInactiveMs="
+ _sessionData.getMaxInactiveMs()
+ " expiry="
+ _sessionData.getExpiry());
if (_state == State.INVALIDATING) if (_state == State.INVALIDATING)
return; //in the process of being invalidated, listeners may try to remove attributes return; // in the process of being invalidated, listeners may try to
// remove attributes
if (!isResident()) if (!isResident())
throw new IllegalStateException("Not valid for write: id="+_sessionData.getId()+" not resident"); throw new IllegalStateException("Not valid for write: id=" + _sessionData.getId() + " not resident");
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* Chech that the session data can be read. * Chech that the session data can be read.
* *
* @throws IllegalStateException if the session is invalid * @throws IllegalStateException if the session is invalid
*/ */
protected void checkValidForRead () throws IllegalStateException protected void checkValidForRead() throws IllegalStateException
{ {
checkLocked(); checkLocked();
if (_state == State.INVALID) if (_state == State.INVALID)
throw new IllegalStateException("Invalid for read: id="+_sessionData.getId()+" created="+_sessionData.getCreated()+" accessed="+_sessionData.getAccessed()+" lastaccessed="+_sessionData.getLastAccessed()+" maxInactiveMs="+_sessionData.getMaxInactiveMs()+" expiry="+_sessionData.getExpiry()); throw new IllegalStateException("Invalid for read: id=" + _sessionData.getId()
+ " created="
+ _sessionData.getCreated()
+ " accessed="
+ _sessionData.getAccessed()
+ " lastaccessed="
+ _sessionData.getLastAccessed()
+ " maxInactiveMs="
+ _sessionData.getMaxInactiveMs()
+ " expiry="
+ _sessionData.getExpiry());
if (_state == State.INVALIDATING) if (_state == State.INVALIDATING)
return; return;
if (!isResident()) if (!isResident())
throw new IllegalStateException("Invalid for read: id="+_sessionData.getId()+" not resident"); throw new IllegalStateException("Invalid for read: id=" + _sessionData.getId() + " not resident");
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
protected void checkLocked () protected void checkLocked() throws IllegalStateException
throws IllegalStateException
{ {
if (!_lock.isLocked()) if (!_lock.isLocked())
throw new IllegalStateException("Session not locked"); throw new IllegalStateException("Session not locked");
@ -682,7 +718,7 @@ public class Session implements SessionHandler.SessionIf
{ {
checkValidForRead(); checkValidForRead();
final Iterator<String> itor = _sessionData.getKeys().iterator(); final Iterator<String> itor = _sessionData.getKeys().iterator();
return new Enumeration<String> () return new Enumeration<String>()
{ {
@Override @Override
@ -702,8 +738,6 @@ public class Session implements SessionHandler.SessionIf
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public int getAttributes() public int getAttributes()
{ {
@ -711,8 +745,6 @@ public class Session implements SessionHandler.SessionIf
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public Set<String> getNames() public Set<String> getNames()
{ {
@ -744,38 +776,37 @@ public class Session implements SessionHandler.SessionIf
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* @see javax.servlet.http.HttpSession#setAttribute(java.lang.String, java.lang.Object) * @see javax.servlet.http.HttpSession#setAttribute(java.lang.String,
* java.lang.Object)
*/ */
@Override @Override
public void setAttribute(String name, Object value) public void setAttribute(String name, Object value)
{ {
Object old=null; Object old = null;
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{ {
//if session is not valid, don't accept the set // if session is not valid, don't accept the set
checkValidForWrite(); checkValidForWrite();
old=_sessionData.setAttribute(name,value); old = _sessionData.setAttribute(name, value);
} }
if (value == null && old == null) if (value == null && old == null)
return; //if same as remove attribute but attribute was already removed, no change return; // if same as remove attribute but attribute was already
// removed, no change
callSessionAttributeListeners(name, value, old); callSessionAttributeListeners(name, value, old);
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* @see javax.servlet.http.HttpSession#putValue(java.lang.String, java.lang.Object) * @see javax.servlet.http.HttpSession#putValue(java.lang.String,
* java.lang.Object)
*/ */
@Override @Override
@Deprecated @Deprecated
public void putValue(String name, Object value) public void putValue(String name, Object value)
{ {
setAttribute(name,value); setAttribute(name, value);
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String) * @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String)
@ -786,8 +817,6 @@ public class Session implements SessionHandler.SessionIf
setAttribute(name, null); setAttribute(name, null);
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** /**
* @see javax.servlet.http.HttpSession#removeValue(java.lang.String) * @see javax.servlet.http.HttpSession#removeValue(java.lang.String)
@ -800,40 +829,91 @@ public class Session implements SessionHandler.SessionIf
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Force a change to the id of a session. /**
* Force a change to the id of a session.
* *
* @param request the Request associated with the call to change id. * @param request the Request associated with the call to change id.
*/ */
public void renewId(HttpServletRequest request) public void renewId(HttpServletRequest request)
{ {
if (_handler == null) if (_handler == null)
throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); throw new IllegalStateException("No session manager for session " + _sessionData.getId());
String id = null; String id = null;
String extendedId = null; String extendedId = null;
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{ {
checkValidForWrite(); //don't renew id on a session that is not valid while (true)
id = _sessionData.getId(); //grab the values as they are now {
switch (_state)
{
case INVALID:
case INVALIDATING:
throw new IllegalStateException();
case CHANGING:
try
{
_stateChangeCompleted.await();
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
continue;
case VALID:
_state = State.CHANGING;
break;
default:
throw new IllegalStateException();
}
break;
}
id = _sessionData.getId(); // grab the values as they are now
extendedId = getExtendedId(); extendedId = getExtendedId();
} }
String newId = _handler._sessionIdManager.renewSessionId(id, extendedId, request); String newId = _handler._sessionIdManager.renewSessionId(id, extendedId, request);
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{ {
checkValidForWrite(); switch (_state)
{
case CHANGING:
if (id.equals(newId))
throw new IllegalStateException("Unable to change session id");
// this shouldn't be necessary to do here EXCEPT that when a
// null session cache is
// used, a new Session object will be created during the
// call to renew, so this
// Session object will not have been modified.
_sessionData.setId(newId); _sessionData.setId(newId);
setExtendedId(_handler._sessionIdManager.getExtendedId(newId, request)); setExtendedId(_handler._sessionIdManager.getExtendedId(newId, request));
}
setIdChanged(true); setIdChanged(true);
}
_state = State.VALID;
_stateChangeCompleted.signalAll();
break;
case INVALID:
case INVALIDATING:
throw new IllegalStateException("Session invalid");
default:
throw new IllegalStateException();
}
}
}
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** Called by users to invalidate a session, or called by the /**
* access method as a request enters the session if the session * Called by users to invalidate a session, or called by the access method
* has expired, or called by manager as a result of scavenger * as a request enters the session if the session has expired, or called by
* expiring session * manager as a result of scavenger expiring session
* *
* @see javax.servlet.http.HttpSession#invalidate() * @see javax.servlet.http.HttpSession#invalidate()
*/ */
@ -841,16 +921,28 @@ public class Session implements SessionHandler.SessionIf
public void invalidate() public void invalidate()
{ {
if (_handler == null) if (_handler == null)
throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); throw new IllegalStateException("No session manager for session " + _sessionData.getId());
boolean result = beginInvalidate(); boolean result = beginInvalidate();
try try
{ {
//if the session was not already invalid, or in process of being invalidated, do invalidate // if the session was not already invalid, or in process of being
// invalidated, do invalidate
if (result) if (result)
{ {
//tell id mgr to remove session from all contexts try
{
// do the invalidation
_handler.callSessionDestroyedListeners(this);
}
finally
{
// call the attribute removed listeners and finally mark it
// as invalid
finishInvalidate();
}
// tell id mgr to remove sessions with same id from all contexts
_handler.getSessionIdManager().invalidateAll(_sessionData.getId()); _handler.getSessionIdManager().invalidateAll(_sessionData.getId());
} }
} }
@ -861,20 +953,23 @@ public class Session implements SessionHandler.SessionIf
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** Grab the lock on the session /**
* Grab the lock on the session
*
* @return the lock * @return the lock
*/ */
public Lock lock () public Lock lock()
{ {
return _lock.lock(); return _lock.lock();
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** Grab the lock on the session if it isn't locked already /**
* Grab the lock on the session if it isn't locked already
*
* @return the lock * @return the lock
*/ */
public Lock lockIfNotHeld () public Lock lockIfNotHeld()
{ {
return _lock.lock(); return _lock.lock();
} }
@ -888,24 +983,49 @@ public class Session implements SessionHandler.SessionIf
boolean result = false; boolean result = false;
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{
while (true)
{ {
switch (_state) switch (_state)
{ {
case INVALID: case INVALID:
{ {
throw new IllegalStateException(); //spec does not allow invalidate of already invalid session throw new IllegalStateException(); // spec does not
// allow invalidate
// of already invalid
// session
}
case INVALIDATING:
{
if (LOG.isDebugEnabled())
LOG.debug("Session {} already being invalidated", _sessionData.getId());
break;
}
case CHANGING:
{
try
{
_stateChangeCompleted.await();
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
continue;
} }
case VALID: case VALID:
{ {
//only first change from valid to invalidating should be actionable // only first change from valid to invalidating should
// be actionable
result = true; result = true;
_state = State.INVALIDATING; _state = State.INVALIDATING;
break; break;
} }
default: default:
{ throw new IllegalStateException();
if (LOG.isDebugEnabled()) LOG.debug("Session {} already being invalidated", _sessionData.getId());
} }
break;
} }
} }
@ -924,10 +1044,9 @@ public class Session implements SessionHandler.SessionIf
finishInvalidate(); finishInvalidate();
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/** Call HttpSessionAttributeListeners as part of invalidating /**
* a Session. * Call HttpSessionAttributeListeners as part of invalidating a Session.
* *
* @throws IllegalStateException if no session manager can be find * @throws IllegalStateException if no session manager can be find
*/ */
@ -938,21 +1057,22 @@ public class Session implements SessionHandler.SessionIf
try try
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("invalidate {}",_sessionData.getId()); LOG.debug("invalidate {}", _sessionData.getId());
if (_state == State.VALID || _state == State.INVALIDATING) if (_state == State.VALID || _state == State.INVALIDATING)
{ {
Set<String> keys = null; Set<String> keys = null;
do do
{ {
keys = _sessionData.getKeys(); keys = _sessionData.getKeys();
for (String key:keys) for (String key : keys)
{ {
Object old=_sessionData.setAttribute(key,null); Object old = _sessionData.setAttribute(key, null);
// if same as remove attribute but attribute was
// already removed, no change
if (old == null) if (old == null)
return; //if same as remove attribute but attribute was already removed, no change continue;
callSessionAttributeListeners(key, null, old); callSessionAttributeListeners(key, null, old);
} }
} }
while (!keys.isEmpty()); while (!keys.isEmpty());
} }
@ -961,6 +1081,8 @@ public class Session implements SessionHandler.SessionIf
{ {
// mark as invalid // mark as invalid
_state = State.INVALID; _state = State.INVALID;
_handler.recordSessionTime(this);
_stateChangeCompleted.signalAll();
} }
} }
} }
@ -977,19 +1099,17 @@ public class Session implements SessionHandler.SessionIf
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
public void setIdChanged(boolean changed) public void setIdChanged(boolean changed)
{ {
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{ {
_idChanged=changed; _idChanged = changed;
} }
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
public boolean isIdChanged () public boolean isIdChanged()
{ {
try (Lock lock = _lock.lock()) try (Lock lock = _lock.lock())
{ {
@ -997,7 +1117,6 @@ public class Session implements SessionHandler.SessionIf
} }
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
@Override @Override
public Session getSession() public Session getSession()
@ -1013,7 +1132,7 @@ public class Session implements SessionHandler.SessionIf
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
public void setResident (boolean resident) public void setResident(boolean resident)
{ {
_resident = resident; _resident = resident;
@ -1024,7 +1143,7 @@ public class Session implements SessionHandler.SessionIf
} }
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
public boolean isResident () public boolean isResident()
{ {
return _resident; return _resident;
} }

View File

@ -91,16 +91,41 @@ public interface SessionCache extends LifeCycle
*/ */
Session newSession (SessionData data); Session newSession (SessionData data);
/**
* Change the id of a session.
*
* This method has been superceded by the 4 arg renewSessionId method and
* should no longer be called.
*
* @param oldId the old id
* @param newId the new id
* @return the changed Session
* @throws Exception if anything went wrong
* @deprecated use
* {@link #renewSessionId(String oldId, String newId, String oldExtendedId, String newExtendedId)}
*/
@Deprecated
default Session renewSessionId(String oldId, String newId) throws Exception
{
return null;
}
/** /**
* Change the id of a Session. * Change the id of a Session.
* *
* @param oldId the current session id * @param oldId the current session id
* @param newId the new session id * @param newId the new session id
* @param oldExtendedId the current extended session id
* @param newExtendedId the new extended session id
* @return the Session after changing its id * @return the Session after changing its id
* @throws Exception if any error occurred * @throws Exception if any error occurred
*/ */
Session renewSessionId (String oldId, String newId) throws Exception; default Session renewSessionId(String oldId, String newId, String oldExtendedId, String newExtendedId) throws Exception
{
return renewSessionId(oldId, newId);
}
/** /**

View File

@ -18,6 +18,8 @@
package org.eclipse.jetty.server.session; package org.eclipse.jetty.server.session;
import static java.lang.Math.round;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -65,8 +67,6 @@ import org.eclipse.jetty.util.thread.Locker.Lock;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.Scheduler;
import static java.lang.Math.round;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** /**
* SessionHandler. * SessionHandler.
@ -344,6 +344,60 @@ public class SessionHandler extends ScopedHandler
_sessionIdListeners.clear(); _sessionIdListeners.clear();
} }
/**
* Call the session lifecycle listeners
* @param session the session on which to call the lifecycle listeners
*/
protected void callSessionDestroyedListeners (Session session)
{
if (session == null)
return;
if (_sessionListeners!=null)
{
HttpSessionEvent event=new HttpSessionEvent(session);
for (int i = _sessionListeners.size()-1; i>=0; i--)
{
_sessionListeners.get(i).sessionDestroyed(event);
}
}
}
/**
* Call the session lifecycle listeners
* @param session the session on which to call the lifecycle listeners
*/
protected void callSessionCreatedListeners (Session session)
{
if (session == null)
return;
if (_sessionListeners!=null)
{
HttpSessionEvent event=new HttpSessionEvent(session);
for (int i = _sessionListeners.size()-1; i>=0; i--)
{
_sessionListeners.get(i).sessionCreated(event);
}
}
}
protected void callSessionIdListeners (Session session, String oldId)
{
//inform the listeners
if (!_sessionIdListeners.isEmpty())
{
HttpSessionEvent event = new HttpSessionEvent(session);
for (HttpSessionIdListener l:_sessionIdListeners)
{
l.sessionIdChanged(event, oldId);
}
}
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** /**
* Called by the {@link SessionHandler} when a session is last accessed by a request. * Called by the {@link SessionHandler} when a session is last accessed by a request.
@ -793,15 +847,10 @@ public class SessionHandler extends ScopedHandler
_sessionCache.put(id, session); _sessionCache.put(id, session);
_sessionsCreatedStats.increment(); _sessionsCreatedStats.increment();
if (request.isSecure()) if (request!=null && request.isSecure())
session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE); session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE);
if (_sessionListeners!=null) callSessionCreatedListeners(session);
{
HttpSessionEvent event=new HttpSessionEvent(session);
for (HttpSessionListener listener : _sessionListeners)
listener.sessionCreated(event);
}
return session; return session;
} }
@ -1183,24 +1232,15 @@ public class SessionHandler extends ScopedHandler
{ {
try try
{ {
Session session = _sessionCache.renewSessionId (oldId, newId); //swap the id over Session session = _sessionCache.renewSessionId (oldId, newId, oldExtendedId, newExtendedId); //swap the id over
if (session == null) if (session == null)
{ {
//session doesn't exist on this context //session doesn't exist on this context
return; return;
} }
session.setExtendedId(newExtendedId); //remember the extended id
//inform the listeners //inform the listeners
if (!_sessionIdListeners.isEmpty()) callSessionIdListeners(session, oldId);
{
HttpSessionEvent event = new HttpSessionEvent(session);
for (HttpSessionIdListener l:_sessionIdListeners)
{
l.sessionIdChanged(event, oldId);
}
}
} }
catch (Exception e) catch (Exception e)
{ {
@ -1208,30 +1248,64 @@ public class SessionHandler extends ScopedHandler
} }
} }
/**
* Record length of time session has been active. Called when the
* session is about to be invalidated.
*
* @param session the session whose time to record
*/
protected void recordSessionTime (Session session)
{
_sessionTimeStats.record(round((System.currentTimeMillis() - session.getSessionData().getCreated())/1000.0));
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** /**
* Called when a session has expired. * Called by SessionIdManager to remove a session that has been invalidated,
* either by this context or another context. Also called by
* SessionIdManager when a session has expired in either this context or
* another context.
* *
* @param id the id to invalidate * @param id the session id to invalidate
*/ */
public void invalidate (String id) public void invalidate (String id)
{ {
if (StringUtil.isBlank(id)) if (StringUtil.isBlank(id))
return; return;
try try
{ {
//remove the session and call the destroy listeners // Remove the Session object from the session cache and any backing
Session session = removeSession(id, true); // data store
Session session = _sessionCache.delete(id);
if (session != null) if (session != null)
{ {
_sessionTimeStats.record(round((System.currentTimeMillis() - session.getSessionData().getCreated())/1000.0)); //start invalidating if it is not already begun, and call the listeners
try
{
if (session.beginInvalidate())
{
try
{
callSessionDestroyedListeners(session);
}
catch (Exception e)
{
LOG.warn("Session listener threw exception", e);
}
//call the attribute removed listeners and finally mark it as invalid
session.finishInvalidate(); session.finishInvalidate();
} }
} }
catch (IllegalStateException e)
{
if (LOG.isDebugEnabled()) LOG.debug("Session {} already invalid", session);
LOG.ignore(e);
}
}
}
catch (Exception e) catch (Exception e)
{ {
LOG.warn(e); LOG.warn(e);

View File

@ -34,7 +34,9 @@ import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionEvent;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SessionIdManager;
import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletContextHandler;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -65,6 +67,100 @@ public class DefaultSessionCacheTest
} }
@Test
public void testRenewIdMultipleRequests() throws Exception
{
//Test that invalidation happens on ALL copies of the session that are in-use by requests
Server server = new Server();
SessionIdManager sessionIdManager = new DefaultSessionIdManager(server);
server.setSessionIdManager(sessionIdManager);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/test");
context.setServer(server);
context.getSessionHandler().setMaxInactiveInterval((int)TimeUnit.DAYS.toSeconds(1));
context.getSessionHandler().setSessionIdManager(sessionIdManager);
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
cacheFactory.setSaveOnCreate(true); //ensures that a session is persisted as soon as it is created
DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler());
TestSessionDataStore store = new TestSessionDataStore();
cache.setSessionDataStore(store);
context.getSessionHandler().setSessionCache(cache);
TestHttpSessionListener listener = new TestHttpSessionListener();
context.getSessionHandler().addEventListener(listener);
server.setHandler(context);
try
{
server.start();
//create a new session
Session s = (Session)context.getSessionHandler().newHttpSession(null);
String id = s.getId();
context.getSessionHandler().access(s, false); //simulate accessing the request
context.getSessionHandler().complete(s); //simulate completing the request
//make 1st request
final Session session = context.getSessionHandler().getSession(id); //get the session again
assertNotNull(session);
context.getSessionHandler().access(session, false); //simulate accessing the request
//make 2nd request
final Session session2 = context.getSessionHandler().getSession(id); //get the session again
context.getSessionHandler().access(session2, false); //simulate accessing the request
assertNotNull(session2);
assertTrue(session == session2);
Thread t2 = new Thread(new Runnable()
{
@Override
public void run()
{
System.err.println("Starting session id renewal");
session2.renewId(new Request(null,null));
System.err.println("Finished session id renewal");
}
});
t2.start();
Thread t = new Thread(new Runnable()
{
@Override
public void run()
{
System.err.println("Starting invalidation");
try{Thread.sleep(1000L);}catch (Exception e) {e.printStackTrace();}
session.invalidate();
System.err.println("Finished invalidation");
}
}
);
t.start();
t.join();
t2.join();
}
finally
{
server.stop();
}
}
/** /**
* Test sessions are saved when shutdown with a store. * Test sessions are saved when shutdown with a store.
@ -180,7 +276,7 @@ public class DefaultSessionCacheTest
cache.put("1234", session); cache.put("1234", session);
assertTrue(cache.contains("1234")); assertTrue(cache.contains("1234"));
cache.renewSessionId("1234", "5678"); cache.renewSessionId("1234", "5678", "1234.foo", "5678.foo");
assertTrue(cache.contains("5678")); assertTrue(cache.contains("5678"));
assertFalse(cache.contains("1234")); assertFalse(cache.contains("1234"));