org.eclipse.jetty.server.session.Session Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
//
// ========================================================================
// Copyright (c) 1995-2018 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 java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionContext;
import javax.servlet.http.HttpSessionEvent;
import org.eclipse.jetty.io.CyclicTimeout;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Locker;
import org.eclipse.jetty.util.thread.Locker.Lock;
/**
* Session
*
* A heavy-weight Session object representing a HttpSession. Session objects
* relating to a context are kept in a {@link SessionCache}. The purpose of
* the SessionCache is to keep the working set of Session objects in memory
* so that they may be accessed quickly, and facilitate the sharing of a
* Session object amongst multiple simultaneous requests referring to the
* same session id.
*
* The {@link SessionHandler} coordinates
* the lifecycle of Session objects with the help of the SessionCache.
*
* @see SessionHandler
* @see org.eclipse.jetty.server.SessionIdManager
*/
public class Session implements SessionHandler.SessionIf
{
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
/**
*
*/
public final static String SESSION_CREATED_SECURE="org.eclipse.jetty.security.sessionCreatedSecure";
/**
* State
*
* Validity states of a session
*/
public enum State {VALID, INVALID, INVALIDATING};
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 boolean _idChanged;
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 boolean _resident = false;
protected final SessionInactivityTimer _sessionInactivityTimer;
/* ------------------------------------------------------------- */
/**
* SessionInactivityTimer
*
* Each Session has a timer associated with it that fires whenever
* it has been idle (ie not accessed by a request) for a
* configurable amount of time, or the Session expires.
*
* @see SessionCache
*
*/
public class SessionInactivityTimer
{
protected final CyclicTimeout _timer;
protected long _msec = -1;
public SessionInactivityTimer()
{
_timer = new CyclicTimeout((getSessionHandler().getScheduler()))
{
@Override
public void onTimeoutExpired()
{
if (LOG.isDebugEnabled()) LOG.debug("Timer expired for session {}", getId());
getSessionHandler().sessionInactivityTimerExpired(Session.this);
}
};
}
/**
* @param ms the timeout to set; -1 means that the timer will not be scheduled
*/
public void setTimeout(long ms)
{
_msec = ms;
if (LOG.isDebugEnabled()) LOG.debug("Session {} timer={}ms",getId(), ms);
}
public void schedule ()
{
if (_msec > 0)
{
if (LOG.isDebugEnabled()) LOG.debug("(Re)starting timer for session {} at {}ms",getId(), _msec);
_timer.schedule(_msec, TimeUnit.MILLISECONDS);
}
else
{
if (LOG.isDebugEnabled()) LOG.debug("Not starting timer for session {}",getId());
}
}
public void cancel()
{
_timer.cancel();
if (LOG.isDebugEnabled()) LOG.debug("Cancelled timer for session {}",getId());
}
public void destroy ()
{
_timer.destroy();
if (LOG.isDebugEnabled()) LOG.debug("Destroyed timer for session {}",getId());
}
}
/* ------------------------------------------------------------- */
/**
* Create a new session
* @param handler the SessionHandler that manages this session
* @param request the request the session should be based on
* @param data the session data
*/
public Session (SessionHandler handler, HttpServletRequest request, SessionData data)
{
_handler = handler;
_sessionData = data;
_newSession = true;
_sessionData.setDirty(true);
_requests = 1; //access will not be called on this new session, but we are obviously in a request
_sessionInactivityTimer = new SessionInactivityTimer();
}
/* ------------------------------------------------------------- */
/**
* Re-inflate an existing session from some eg persistent store.
*
* @param handler the SessionHandler managing the session
* @param data the session data
*/
public Session (SessionHandler handler, SessionData data)
{
_handler = handler;
_sessionData = data;
_sessionInactivityTimer = new SessionInactivityTimer();
}
/* ------------------------------------------------------------- */
/**
* Returns the current number of requests that are active in the
* Session.
*
* @return the number of active requests for this session
*/
public long getRequests()
{
try (Lock lock = _lock.lock())
{
return _requests;
}
}
/* ------------------------------------------------------------- */
public void setExtendedId (String extendedId)
{
_extendedId = extendedId;
}
/* ------------------------------------------------------------- */
protected void cookieSet()
{
try (Lock lock = _lock.lock())
{
_sessionData.setCookieSet(_sessionData.getAccessed());
}
}
/* ------------------------------------------------------------ */
protected boolean access(long time)
{
try (Lock lock = _lock.lock())
{
if (!isValid())
return false;
_newSession=false;
long lastAccessed = _sessionData.getAccessed();
_sessionData.setAccessed(time);
_sessionData.setLastAccessed(lastAccessed);
_sessionData.calcAndSetExpiry(time);
if (isExpiredAt(time))
{
invalidate();
return false;
}
_requests++;
//temporarily stop the idle timer
if (LOG.isDebugEnabled()) LOG.debug("Session {} accessed, stopping timer, active requests={}",getId(),_requests);
_sessionInactivityTimer.cancel();
return true;
}
}
/* ------------------------------------------------------------ */
protected void complete()
{
try (Lock lock = _lock.lock())
{
_requests--;
if (LOG.isDebugEnabled()) LOG.debug("Session {} complete, active requests={}",getId(),_requests);
//start the inactivity timer
if (_requests == 0)
_sessionInactivityTimer.schedule();
}
}
/* ------------------------------------------------------------- */
/** Check to see if session has expired as at the time given.
*
* @param time the time since the epoch in ms
* @return true if expired
*/
protected boolean isExpiredAt(long time)
{
try (Lock lock = _lock.lock())
{
return _sessionData.isExpiredAt(time);
}
}
/* ------------------------------------------------------------- */
/** Check if the Session has been idle longer than a number of seconds.
*
* @param sec the number of seconds
* @return true if the session has been idle longer than the interval
*/
protected boolean isIdleLongerThan (int sec)
{
long now = System.currentTimeMillis();
try (Lock lock = _lock.lock())
{
return ((_sessionData.getAccessed() + (sec*1000)) <= now);
}
}
/* ------------------------------------------------------------ */
/**
* Call binding and attribute listeners based on the new and old
* values of the attribute.
*
* @param name name of the attribute
* @param newValue new value of the attribute
* @param oldValue previous value of the attribute
* @throws IllegalStateException if no session manager can be find
*/
protected void callSessionAttributeListeners (String name, Object newValue, Object oldValue)
{
if (newValue==null || !newValue.equals(oldValue))
{
if (oldValue!=null)
unbindValue(name,oldValue);
if (newValue!=null)
bindValue(name,newValue);
if (_handler == null)
throw new IllegalStateException ("No session manager for session "+ _sessionData.getId());
_handler.doSessionAttributeListeners(this,name,oldValue,newValue);
}
}
/* ------------------------------------------------------------- */
/**
* 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 value the bound value
*/
public void unbindValue(java.lang.String name, Object value)
{
if (value!=null&&value instanceof HttpSessionBindingListener)
((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this,name));
}
/* ------------------------------------------------------------- */
/**
* 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 value the bound value
*/
public void bindValue(java.lang.String name, Object value)
{
if (value!=null&&value instanceof HttpSessionBindingListener)
((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this,name));
}
/* ------------------------------------------------------------- */
/**
* Call the activation listeners. This must be called holding the
* lock.
*/
public void didActivate()
{
HttpSessionEvent event = new HttpSessionEvent(this);
for (Iterator iter = _sessionData.getKeys().iterator(); iter.hasNext();)
{
Object value = _sessionData.getAttribute(iter.next());
if (value instanceof HttpSessionActivationListener)
{
HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
listener.sessionDidActivate(event);
}
}
}
/* ------------------------------------------------------------- */
/**
* Call the passivation listeners. This must be called holding the
* lock
*/
public void willPassivate()
{
HttpSessionEvent event = new HttpSessionEvent(this);
for (Iterator iter = _sessionData.getKeys().iterator(); iter.hasNext();)
{
Object value = _sessionData.getAttribute(iter.next());
if (value instanceof HttpSessionActivationListener)
{
HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
listener.sessionWillPassivate(event);
}
}
}
/* ------------------------------------------------------------ */
public boolean isValid()
{
try (Lock lock = _lock.lock())
{
return _state==State.VALID;
}
}
/* ------------------------------------------------------------- */
public long getCookieSetTime()
{
try (Lock lock = _lock.lock())
{
return _sessionData.getCookieSet();
}
}
/* ------------------------------------------------------------- */
@Override
public long getCreationTime() throws IllegalStateException
{
try (Lock lock = _lock.lock())
{
checkValidForRead();
return _sessionData.getCreated();
}
}
/**
* @see javax.servlet.http.HttpSession#getId()
*/
@Override
public String getId()
{
try (Lock lock = _lock.lock())
{
return _sessionData.getId();
}
}
public String getExtendedId()
{
return _extendedId;
}
public String getContextPath()
{
return _sessionData.getContextPath();
}
public String getVHost ()
{
return _sessionData.getVhost();
}
/**
* @see javax.servlet.http.HttpSession#getLastAccessedTime()
*/
@Override
public long getLastAccessedTime()
{
try (Lock lock = _lock.lock())
{
return _sessionData.getLastAccessed();
}
}
/**
* @see javax.servlet.http.HttpSession#getServletContext()
*/
@Override
public ServletContext getServletContext()
{
if (_handler == null)
throw new IllegalStateException ("No session manager for session "+ _sessionData.getId());
return _handler._context;
}
/**
* @see javax.servlet.http.HttpSession#setMaxInactiveInterval(int)
*/
@Override
public void setMaxInactiveInterval(int secs)
{
try (Lock lock = _lock.lock())
{
_sessionData.setMaxInactiveMs((long)secs*1000L);
_sessionData.calcAndSetExpiry();
_sessionData.setDirty(true);
updateInactivityTimer();
if (LOG.isDebugEnabled())
{
if (secs <= 0)
LOG.debug("Session {} is now immortal (maxInactiveInterval={})", _sessionData.getId(), secs);
else
LOG.debug("Session {} maxInactiveInterval={}", _sessionData.getId(), secs);
}
}
}
/**
* Set the inactivity timer to the smaller of the session maxInactivity
* (ie session-timeout from web.xml), or the inactive eviction time.
*/
public void updateInactivityTimer ()
{
try (Lock lock = _lock.lock())
{
long maxInactive = _sessionData.getMaxInactiveMs();
int evictionPolicy = getSessionHandler().getSessionCache().getEvictionPolicy();
if (maxInactive <= 0)
{
//sessions are immortal, they never expire
if (evictionPolicy < SessionCache.EVICT_ON_INACTIVITY)
{
//we do not want to evict inactive sessions
_sessionInactivityTimer.setTimeout(-1);
if (LOG.isDebugEnabled()) LOG.debug("Session {} is immortal && no inactivity eviction", getId());
}
else
{
//sessions are immortal but we want to evict after inactivity
_sessionInactivityTimer.setTimeout(TimeUnit.SECONDS.toMillis(evictionPolicy));
if (LOG.isDebugEnabled()) LOG.debug("Session {} is immortal; evict after {} sec inactivity", getId(), evictionPolicy);
}
}
else
{
//sessions are not immortal
if (evictionPolicy == SessionCache.NEVER_EVICT)
{
//timeout is just the maxInactive setting
_sessionInactivityTimer.setTimeout(_sessionData.getMaxInactiveMs());
if (LOG.isDebugEnabled()) LOG.debug("Session {} no eviction", getId());
}
else if (evictionPolicy == SessionCache.EVICT_ON_SESSION_EXIT)
{
//session will not remain in the cache, so no timeout
_sessionInactivityTimer.setTimeout(-1);
if (LOG.isDebugEnabled()) LOG.debug("Session {} evict on exit", getId());
}
else
{
//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)));
if (LOG.isDebugEnabled()) LOG.debug("Session {} timer set to lesser of maxInactive={} and inactivityEvict={}", getId(), maxInactive, evictionPolicy);
}
}
}
}
/**
* @see javax.servlet.http.HttpSession#getMaxInactiveInterval()
*/
@Override
public int getMaxInactiveInterval()
{
try (Lock lock = _lock.lock())
{
long maxInactiveMs = _sessionData.getMaxInactiveMs();
return (int)(maxInactiveMs < 0 ? -1 : maxInactiveMs/1000);
}
}
/**
* @see javax.servlet.http.HttpSession#getSessionContext()
*/
@Override
@Deprecated
public HttpSessionContext getSessionContext()
{
checkValidForRead();
return SessionHandler.__nullSessionContext;
}
public SessionHandler getSessionHandler()
{
return _handler;
}
/* ------------------------------------------------------------- */
/**
* Check that the session can be modified.
*
* @throws IllegalStateException if the session is invalid
*/
protected void checkValidForWrite() throws IllegalStateException
{
checkLocked();
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());
if (_state == State.INVALIDATING)
return; //in the process of being invalidated, listeners may try to remove attributes
if (!isResident())
throw new IllegalStateException("Not valid for write: id="+_sessionData.getId()+" not resident");
}
/* ------------------------------------------------------------- */
/**
* Chech that the session data can be read.
*
* @throws IllegalStateException if the session is invalid
*/
protected void checkValidForRead () throws IllegalStateException
{
checkLocked();
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());
if (_state == State.INVALIDATING)
return;
if (!isResident())
throw new IllegalStateException("Invalid for read: id="+_sessionData.getId()+" not resident");
}
/* ------------------------------------------------------------- */
protected void checkLocked ()
throws IllegalStateException
{
if (!_lock.isLocked())
throw new IllegalStateException("Session not locked");
}
/**
* @see javax.servlet.http.HttpSession#getAttribute(java.lang.String)
*/
@Override
public Object getAttribute(String name)
{
try (Lock lock = _lock.lock())
{
checkValidForRead();
return _sessionData.getAttribute(name);
}
}
/**
* @see javax.servlet.http.HttpSession#getValue(java.lang.String)
*/
@Override
@Deprecated
public Object getValue(String name)
{
try (Lock lock = _lock.lock())
{
return _sessionData.getAttribute(name);
}
}
/**
* @see javax.servlet.http.HttpSession#getAttributeNames()
*/
@Override
public Enumeration getAttributeNames()
{
try (Lock lock = _lock.lock())
{
checkValidForRead();
final Iterator itor = _sessionData.getKeys().iterator();
return new Enumeration ()
{
@Override
public boolean hasMoreElements()
{
return itor.hasNext();
}
@Override
public String nextElement()
{
return itor.next();
}
};
}
}
/* ------------------------------------------------------------ */
public int getAttributes()
{
return _sessionData.getKeys().size();
}
/* ------------------------------------------------------------ */
public Set getNames()
{
return Collections.unmodifiableSet(_sessionData.getKeys());
}
/* ------------------------------------------------------------- */
/**
* @deprecated As of Version 2.2, this method is replaced by
* {@link #getAttributeNames}
*/
@Deprecated
@Override
public String[] getValueNames() throws IllegalStateException
{
try (Lock lock = _lock.lock())
{
checkValidForRead();
Iterator itor = _sessionData.getKeys().iterator();
if (!itor.hasNext())
return new String[0];
ArrayList names = new ArrayList<>();
while (itor.hasNext())
names.add(itor.next());
return names.toArray(new String[names.size()]);
}
}
/* ------------------------------------------------------------- */
/**
* @see javax.servlet.http.HttpSession#setAttribute(java.lang.String, java.lang.Object)
*/
@Override
public void setAttribute(String name, Object value)
{
Object old=null;
try (Lock lock = _lock.lock())
{
//if session is not valid, don't accept the set
checkValidForWrite();
old=_sessionData.setAttribute(name,value);
}
if (value == null && old == null)
return; //if same as remove attribute but attribute was already removed, no change
callSessionAttributeListeners(name, value, old);
}
/* ------------------------------------------------------------- */
/**
* @see javax.servlet.http.HttpSession#putValue(java.lang.String, java.lang.Object)
*/
@Override
@Deprecated
public void putValue(String name, Object value)
{
setAttribute(name,value);
}
/* ------------------------------------------------------------- */
/**
* @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String)
*/
@Override
public void removeAttribute(String name)
{
setAttribute(name, null);
}
/* ------------------------------------------------------------- */
/**
* @see javax.servlet.http.HttpSession#removeValue(java.lang.String)
*/
@Override
@Deprecated
public void removeValue(String name)
{
setAttribute(name, null);
}
/* ------------------------------------------------------------ */
/** Force a change to the id of a session.
*
* @param request the Request associated with the call to change id.
*/
public void renewId(HttpServletRequest request)
{
if (_handler == null)
throw new IllegalStateException ("No session manager for session "+ _sessionData.getId());
String id = null;
String extendedId = null;
try (Lock lock = _lock.lock())
{
checkValidForWrite(); //don't renew id on a session that is not valid
id = _sessionData.getId(); //grab the values as they are now
extendedId = getExtendedId();
}
String newId = _handler._sessionIdManager.renewSessionId(id, extendedId, request);
try (Lock lock = _lock.lock())
{
checkValidForWrite();
_sessionData.setId(newId);
setExtendedId(_handler._sessionIdManager.getExtendedId(newId, request));
}
setIdChanged(true);
}
/* ------------------------------------------------------------- */
/** Called by users to invalidate a session, or called by the
* access method as a request enters the session if the session
* has expired, or called by manager as a result of scavenger
* expiring session
*
* @see javax.servlet.http.HttpSession#invalidate()
*/
@Override
public void invalidate()
{
if (_handler == null)
throw new IllegalStateException ("No session manager for session "+ _sessionData.getId());
boolean result = beginInvalidate();
try
{
//if the session was not already invalid, or in process of being invalidated, do invalidate
if (result)
{
//tell id mgr to remove session from all contexts
_handler.getSessionIdManager().invalidateAll(_sessionData.getId());
}
}
catch (Exception e)
{
LOG.warn(e);
}
}
/* ------------------------------------------------------------- */
/** Grab the lock on the session
* @return the lock
*/
public Lock lock ()
{
return _lock.lock();
}
/* ------------------------------------------------------------- */
/** Grab the lock on the session if it isn't locked already
* @return the lock
*/
public Lock lockIfNotHeld ()
{
return _lock.lock();
}
/* ------------------------------------------------------------- */
/**
* @return true if the session is not already invalid or being invalidated.
*/
protected boolean beginInvalidate()
{
boolean result = false;
try (Lock lock = _lock.lock())
{
switch (_state)
{
case INVALID:
{
throw new IllegalStateException(); //spec does not allow invalidate of already invalid session
}
case VALID:
{
//only first change from valid to invalidating should be actionable
result = true;
_state = State.INVALIDATING;
break;
}
default:
{
if (LOG.isDebugEnabled()) LOG.debug("Session {} already being invalidated", _sessionData.getId());
}
}
}
return result;
}
/* ------------------------------------------------------------- */
/**
* Call HttpSessionAttributeListeners as part of invalidating a Session.
*
* @throws IllegalStateException if no session manager can be find
*/
@Deprecated
protected void doInvalidate() throws IllegalStateException
{
finishInvalidate();
}
/* ------------------------------------------------------------- */
/** Call HttpSessionAttributeListeners as part of invalidating
* a Session.
*
* @throws IllegalStateException if no session manager can be find
*/
protected void finishInvalidate() throws IllegalStateException
{
try (Lock lock = _lock.lock())
{
try
{
if (LOG.isDebugEnabled())
LOG.debug("invalidate {}",_sessionData.getId());
if (_state == State.VALID || _state == State.INVALIDATING)
{
Set keys = null;
do
{
keys = _sessionData.getKeys();
for (String key:keys)
{
Object old=_sessionData.setAttribute(key,null);
if (old == null)
return; //if same as remove attribute but attribute was already removed, no change
callSessionAttributeListeners(key, null, old);
}
}
while (!keys.isEmpty());
}
}
finally
{
// mark as invalid
_state = State.INVALID;
}
}
}
/* ------------------------------------------------------------- */
@Override
public boolean isNew() throws IllegalStateException
{
try (Lock lock = _lock.lock())
{
checkValidForRead();
return _newSession;
}
}
/* ------------------------------------------------------------- */
public void setIdChanged(boolean changed)
{
try (Lock lock = _lock.lock())
{
_idChanged=changed;
}
}
/* ------------------------------------------------------------- */
public boolean isIdChanged ()
{
try (Lock lock = _lock.lock())
{
return _idChanged;
}
}
/* ------------------------------------------------------------- */
@Override
public Session getSession()
{
// TODO why is this used
return this;
}
/* ------------------------------------------------------------- */
protected SessionData getSessionData()
{
return _sessionData;
}
/* ------------------------------------------------------------- */
public void setResident (boolean resident)
{
_resident = resident;
if (_resident)
updateInactivityTimer();
else
_sessionInactivityTimer.destroy();
}
/* ------------------------------------------------------------- */
public boolean isResident ()
{
return _resident;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy