//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.server.session;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import jakarta.servlet.http.HttpServletRequest;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.thread.AutoLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
AbstractSessionCache A base implementation of the SessionCache
interface for managing a set of Session objects pertaining to a context in memory. This implementation ensures that multiple requests for the same session id always return the same Session object. It will delay writing out a session to the SessionDataStore until the last request exits the session. If the SessionDataStore supports passivation then the session passivation and activation listeners are called appropriately as the session is written. This implementation also supports evicting idle Session objects. An idle Session is one that is still valid, has not expired, but has not been accessed by a request for a configurable amount of time. An idle session will be first passivated before it is evicted from the cache. /**
* AbstractSessionCache
*
* A base implementation of the {@link SessionCache} interface for managing a set of
* Session objects pertaining to a context in memory.
*
* This implementation ensures that multiple requests for the same session id
* always return the same Session object.
*
* It will delay writing out a session to the SessionDataStore until the
* last request exits the session. If the SessionDataStore supports passivation
* then the session passivation and activation listeners are called appropriately as
* the session is written.
*
* This implementation also supports evicting idle Session objects. An idle Session
* is one that is still valid, has not expired, but has not been accessed by a
* request for a configurable amount of time. An idle session will be first
* passivated before it is evicted from the cache.
*/
@ManagedObject
public abstract class AbstractSessionCache extends ContainerLifeCycle implements SessionCache
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractSessionCache.class);
The authoritative source of session data
/**
* The authoritative source of session data
*/
protected SessionDataStore _sessionDataStore;
The SessionHandler related to this SessionCache
/**
* The SessionHandler related to this SessionCache
*/
protected final SessionHandler _handler;
Information about the context to which this SessionCache pertains
/**
* Information about the context to which this SessionCache pertains
*/
protected SessionContext _context;
When, if ever, to evict sessions: never; only when the last request for them finishes; after inactivity time (expressed as secs)
/**
* When, if ever, to evict sessions: never; only when the last request for them finishes; after inactivity time (expressed as secs)
*/
protected int _evictionPolicy = SessionCache.NEVER_EVICT;
If true, as soon as a new session is created, it will be persisted to the SessionDataStore
/**
* If true, as soon as a new session is created, it will be persisted to the SessionDataStore
*/
protected boolean _saveOnCreate = false;
If true, a session that will be evicted from the cache because it has been
inactive too long will be saved before being evicted.
/**
* If true, a session that will be evicted from the cache because it has been
* inactive too long will be saved before being evicted.
*/
protected boolean _saveOnInactiveEviction;
If true, a Session whose data cannot be read will be
deleted from the SessionDataStore.
/**
* If true, a Session whose data cannot be read will be
* deleted from the SessionDataStore.
*/
protected boolean _removeUnloadableSessions;
If true, when a response is about to be committed back to the client,
a dirty session will be flushed to the session store.
/**
* If true, when a response is about to be committed back to the client,
* a dirty session will be flushed to the session store.
*/
protected boolean _flushOnResponseCommit;
If true, when the server shuts down, all sessions in the
cache will be invalidated before being removed.
/**
* If true, when the server shuts down, all sessions in the
* cache will be invalidated before being removed.
*/
protected boolean _invalidateOnShutdown;
Create a new Session object from pre-existing session data
Params: - data – the session data
Returns: a new Session object
/**
* Create a new Session object from pre-existing session data
*
* @param data the session data
* @return a new Session object
*/
@Override
public abstract Session newSession(SessionData data);
Create a new Session for a request.
Params: - request – the request
- data – the session data
Returns: the new session
/**
* Create a new Session for a request.
*
* @param request the request
* @param data the session data
* @return the new session
*/
public abstract Session newSession(HttpServletRequest request, SessionData data);
Get the session matching the key from the cache. Does not load
the session.
Params: - id – session id
Returns: the Session object matching the id
/**
* Get the session matching the key from the cache. Does not load
* the session.
*
* @param id session id
* @return the Session object matching the id
*/
protected abstract Session doGet(String id);
Put the session into the map if it wasn't already there
Params: - id – the identity of the session
- session – the session object
Returns: null if the session wasn't already in the map, or the existing entry otherwise
/**
* Put the session into the map if it wasn't already there
*
* @param id the identity of the session
* @param session the session object
* @return null if the session wasn't already in the map, or the existing entry otherwise
*/
protected abstract Session doPutIfAbsent(String id, Session session);
Compute the mappingFunction to create a Session object iff the session with the given id isn't already in the map, otherwise return the existing Session. This method is expected to have precisely the same behaviour as ConcurrentHashMap.computeIfAbsent
Params: - id – the session id
- mappingFunction – the function to load the data for the session
Returns: an existing Session from the cache
/**
* Compute the mappingFunction to create a Session object iff the session
* with the given id isn't already in the map, otherwise return the existing Session.
* This method is expected to have precisely the same behaviour as
* {@link java.util.concurrent.ConcurrentHashMap#computeIfAbsent}
*
* @param id the session id
* @param mappingFunction the function to load the data for the session
* @return an existing Session from the cache
*/
protected abstract Session doComputeIfAbsent(String id, Function<String, Session> mappingFunction);
Replace the mapping from id to oldValue with newValue
Params: - id – the id
- oldValue – the old value
- newValue – the new value
Returns: true if replacement was done
/**
* Replace the mapping from id to oldValue with newValue
*
* @param id the id
* @param oldValue the old value
* @param newValue the new value
* @return true if replacement was done
*/
protected abstract boolean doReplace(String id, Session oldValue, Session newValue);
Remove the session with this identity from the store
Params: - id – the id
Returns: Session that was removed or null
/**
* Remove the session with this identity from the store
*
* @param id the id
* @return Session that was removed or null
*/
public abstract Session doDelete(String id);
Params: - handler – the
SessionHandler
to use
/**
* @param handler the {@link SessionHandler} to use
*/
public AbstractSessionCache(SessionHandler handler)
{
_handler = handler;
}
Returns: the SessionManger
/**
* @return the SessionManger
*/
@Override
public SessionHandler getSessionHandler()
{
return _handler;
}
@Override
public void initialize(SessionContext context)
{
if (isStarted())
throw new IllegalStateException("Context set after session store started");
_context = context;
}
@Override
protected void doStart() throws Exception
{
if (_sessionDataStore == null)
throw new IllegalStateException("No session data store configured");
if (_handler == null)
throw new IllegalStateException("No session manager");
if (_context == null)
throw new IllegalStateException("No ContextId");
_sessionDataStore.initialize(_context);
super.doStart();
}
@Override
protected void doStop() throws Exception
{
_sessionDataStore.stop();
super.doStop();
}
Returns: the SessionDataStore or null if there isn't one
/**
* @return the SessionDataStore or null if there isn't one
*/
@Override
public SessionDataStore getSessionDataStore()
{
return _sessionDataStore;
}
@Override
public void setSessionDataStore(SessionDataStore sessionStore)
{
updateBean(_sessionDataStore, sessionStore);
_sessionDataStore = sessionStore;
}
See Also: - getEvictionPolicy.getEvictionPolicy()
/**
* @see org.eclipse.jetty.server.session.SessionCache#getEvictionPolicy()
*/
@ManagedAttribute(value = "session eviction policy", readonly = true)
@Override
public int getEvictionPolicy()
{
return _evictionPolicy;
}
-1 means we never evict inactive sessions.
0 means we evict a session after the last request for it exits
>0 is the number of seconds after which we evict inactive sessions from the cache
See Also: - setEvictionPolicy.setEvictionPolicy(int)
/**
* -1 means we never evict inactive sessions.
* 0 means we evict a session after the last request for it exits
* >0 is the number of seconds after which we evict inactive sessions from the cache
*
* @see org.eclipse.jetty.server.session.SessionCache#setEvictionPolicy(int)
*/
@Override
public void setEvictionPolicy(int evictionTimeout)
{
_evictionPolicy = evictionTimeout;
}
@ManagedAttribute(value = "immediately save new sessions", readonly = true)
@Override
public boolean isSaveOnCreate()
{
return _saveOnCreate;
}
@Override
public void setSaveOnCreate(boolean saveOnCreate)
{
_saveOnCreate = saveOnCreate;
}
Returns: true if sessions that can't be loaded are deleted from the store
/**
* @return true if sessions that can't be loaded are deleted from the store
*/
@ManagedAttribute(value = "delete unreadable stored sessions", readonly = true)
@Override
public boolean isRemoveUnloadableSessions()
{
return _removeUnloadableSessions;
}
If a session's data cannot be loaded from the store without error, remove
it from the persistent store.
Params: - removeUnloadableSessions – if
true
unloadable sessions will be removed from session store
/**
* If a session's data cannot be loaded from the store without error, remove
* it from the persistent store.
*
* @param removeUnloadableSessions if <code>true</code> unloadable sessions will be removed from session store
*/
@Override
public void setRemoveUnloadableSessions(boolean removeUnloadableSessions)
{
_removeUnloadableSessions = removeUnloadableSessions;
}
@Override
public void setFlushOnResponseCommit(boolean flushOnResponseCommit)
{
_flushOnResponseCommit = flushOnResponseCommit;
}
@Override
public boolean isFlushOnResponseCommit()
{
return _flushOnResponseCommit;
}
Get a session object.
If the session object is not in this session store, try getting
the data for it from a SessionDataStore associated with the
session manager. The usage count of the session is incremented.
See Also: - get.get(String)
/**
* Get a session object.
*
* If the session object is not in this session store, try getting
* the data for it from a SessionDataStore associated with the
* session manager. The usage count of the session is incremented.
*
* @see org.eclipse.jetty.server.session.SessionCache#get(java.lang.String)
*/
@Override
public Session get(String id) throws Exception
{
return getAndEnter(id, true);
}
Get a session object.
If the session object is not in this session store, try getting
the data for it from a SessionDataStore associated with the
session manager.
Params: - id – The session to retrieve
- enter – if true, the usage count of the session will be incremented
Throws: - Exception – if the session cannot be loaded
Returns: the session if it exists, null otherwise
/** Get a session object.
*
* If the session object is not in this session store, try getting
* the data for it from a SessionDataStore associated with the
* session manager.
*
* @param id The session to retrieve
* @param enter if true, the usage count of the session will be incremented
* @return the session if it exists, null otherwise
* @throws Exception if the session cannot be loaded
*/
protected Session getAndEnter(String id, boolean enter) throws Exception
{
Session session = null;
AtomicReference<Exception> exception = new AtomicReference<Exception>();
session = doComputeIfAbsent(id, k ->
{
if (LOG.isDebugEnabled())
LOG.debug("Session {} not found locally in {}, attempting to load", id, this);
try
{
Session s = loadSession(k);
if (s != null)
{
try (AutoLock lock = s.lock())
{
s.setResident(true); //ensure freshly loaded session is resident
}
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("Session {} not loaded by store", id);
}
return s;
}
catch (Exception e)
{
exception.set(e);
return null;
}
});
Exception ex = exception.get();
if (ex != null)
throw ex;
if (session != null)
{
try (AutoLock lock = session.lock())
{
if (!session.isResident()) //session isn't marked as resident in cache
{
if (LOG.isDebugEnabled())
LOG.debug("Non-resident session {} in cache", id);
return null;
}
else if (enter)
{
session.use();
}
}
}
return session;
}
Load the info for the session from the session data store
Params: - id – the id
Returns: a Session object filled with data or null if the session doesn't exist
/**
* Load the info for the session from the session data store
*
* @param id the id
* @return a Session object filled with data or null if the session doesn't exist
*/
private Session loadSession(String id)
throws Exception
{
SessionData data = null;
Session session = null;
if (_sessionDataStore == null)
return null; //can't load it
try
{
data = _sessionDataStore.load(id);
if (data == null) //session doesn't exist
return null;
data.setLastNode(_context.getWorkerName());//we are going to manage the node
session = newSession(data);
return session;
}
catch (UnreadableSessionDataException e)
{
//can't load the session, delete it
if (isRemoveUnloadableSessions())
_sessionDataStore.delete(id);
throw e;
}
}
Add an entirely new session (created by the application calling Request.getSession(true))
to the cache. The usage count of the fresh session is incremented.
Params: - id – the id
- session –
/**
* Add an entirely new session (created by the application calling Request.getSession(true))
* to the cache. The usage count of the fresh session is incremented.
*
* @param id the id
* @param session
*/
@Override
public void add(String id, Session session) throws Exception
{
if (id == null || session == null)
throw new IllegalArgumentException("Add key=" + id + " session=" + (session == null ? "null" : session.getId()));
try (AutoLock lock = session.lock())
{
if (session.getSessionHandler() == null)
throw new IllegalStateException("Session " + id + " is not managed");
if (!session.isValid())
throw new IllegalStateException("Session " + id + " is not valid");
if (doPutIfAbsent(id, session) == null)
{
session.setResident(true); //its in the cache
session.use(); //the request is using it
}
else
throw new IllegalStateException("Session " + id + " already in cache");
}
}
A response that has accessed this session is about to
be returned to the client. Pass the session to the store
to persist, so that any changes will be visible to
subsequent requests on the same node (if using NullSessionCache),
or on other nodes.
/**
* A response that has accessed this session is about to
* be returned to the client. Pass the session to the store
* to persist, so that any changes will be visible to
* subsequent requests on the same node (if using NullSessionCache),
* or on other nodes.
*/
@Override
public void commit(Session session) throws Exception
{
if (session == null)
return;
try (AutoLock lock = session.lock())
{
//only write the session out at this point if the attributes changed. If only
//the lastAccess/expiry time changed defer the write until the last request exits
if (session.isValid() && session.getSessionData().isDirty() && _flushOnResponseCommit)
{
if (LOG.isDebugEnabled())
LOG.debug("Flush session {} on response commit", session);
//save the session
if (!_sessionDataStore.isPassivating())
{
_sessionDataStore.store(session.getId(), session.getSessionData());
}
else
{
session.willPassivate();
_sessionDataStore.store(session.getId(), session.getSessionData());
session.didActivate();
}
}
}
}
Deprecated: use release(String, Session)
instead
/**
* @deprecated use {@link #release(String, Session)} instead
*/
@Override
@Deprecated
public void put(String id, Session session) throws Exception
{
release(id, session);
}
Finish using the Session object.
This should be called when a request exists the session. Only when the last
simultaneous request exists the session will any action be taken.
If there is a SessionDataStore write the session data through to it.
If the SessionDataStore supports passivation, call the passivate/active listeners.
If the evictionPolicy == SessionCache.EVICT_ON_SESSION_EXIT then after we have saved
the session, we evict it from the cache.
See Also: - release.release(String, Session)
/**
* Finish using the Session object.
*
* This should be called when a request exists the session. Only when the last
* simultaneous request exists the session will any action be taken.
*
* If there is a SessionDataStore write the session data through to it.
*
* If the SessionDataStore supports passivation, call the passivate/active listeners.
*
* If the evictionPolicy == SessionCache.EVICT_ON_SESSION_EXIT then after we have saved
* the session, we evict it from the cache.
*
* @see org.eclipse.jetty.server.session.SessionCache#release(java.lang.String, org.eclipse.jetty.server.session.Session)
*/
@Override
public void release(String id, Session session) throws Exception
{
if (id == null || session == null)
throw new IllegalArgumentException("Put key=" + id + " session=" + (session == null ? "null" : session.getId()));
try (AutoLock lock = session.lock())
{
if (session.getSessionHandler() == null)
throw new IllegalStateException("Session " + id + " is not managed");
if (session.isInvalid())
return;
session.complete();
//don't do anything with the session until the last request for it has finished
if ((session.getRequests() <= 0))
{
//save the session
if (!_sessionDataStore.isPassivating())
{
//if our backing datastore isn't the passivating kind, just save the session
_sessionDataStore.store(id, session.getSessionData());
//if we evict on session exit, boot it from the cache
if (getEvictionPolicy() == EVICT_ON_SESSION_EXIT)
{
if (LOG.isDebugEnabled())
LOG.debug("Eviction on request exit id={}", id);
doDelete(session.getId());
session.setResident(false);
}
else
{
session.setResident(true);
doPutIfAbsent(id, session); //ensure it is in our map
if (LOG.isDebugEnabled())
LOG.debug("Non passivating SessionDataStore, session in SessionCache only id={}", id);
}
}
else
{
//backing store supports passivation, call the listeners
session.willPassivate();
if (LOG.isDebugEnabled())
LOG.debug("Session passivating id={}", id);
_sessionDataStore.store(id, session.getSessionData());
if (getEvictionPolicy() == EVICT_ON_SESSION_EXIT)
{
//throw out the passivated session object from the map
doDelete(id);
session.setResident(false);
if (LOG.isDebugEnabled())
LOG.debug("Evicted on request exit id={}", id);
}
else
{
//reactivate the session
session.didActivate();
session.setResident(true);
doPutIfAbsent(id, session);//ensure it is in our map
if (LOG.isDebugEnabled())
LOG.debug("Session reactivated id={}", id);
}
}
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("Req count={} for id={}", session.getRequests(), id);
session.setResident(true);
doPutIfAbsent(id, session); //ensure it is the map, but don't save it to the backing store until the last request exists
}
}
}
Check to see if a session corresponding to the id exists.
This method will first check with the object store. If it
doesn't exist in the object store (might be passivated etc),
it will check with the data store.
Throws: - Exception – the Exception
See Also:
/**
* Check to see if a session corresponding to the id exists.
*
* This method will first check with the object store. If it
* doesn't exist in the object store (might be passivated etc),
* it will check with the data store.
*
* @throws Exception the Exception
* @see org.eclipse.jetty.server.session.SessionCache#exists(java.lang.String)
*/
@Override
public boolean exists(String id) throws Exception
{
//try the object store first
Session s = doGet(id);
if (s != null)
{
try (AutoLock lock = s.lock())
{
//wait for the lock and check the validity of the session
return s.isValid();
}
}
//not there, so find out if session data exists for it
return _sessionDataStore.exists(id);
}
Check to see if this cache contains an entry for the session
corresponding to the session id.
See Also: - contains.contains(String)
/**
* Check to see if this cache contains an entry for the session
* corresponding to the session id.
*
* @see org.eclipse.jetty.server.session.SessionCache#contains(java.lang.String)
*/
@Override
public boolean contains(String id) throws Exception
{
//just ask our object cache, not the store
return (doGet(id) != null);
}
Remove a session object from this store and from any backing store.
See Also: - delete.delete(String)
/**
* Remove a session object from this store and from any backing store.
*
* @see org.eclipse.jetty.server.session.SessionCache#delete(java.lang.String)
*/
@Override
public Session delete(String id) throws Exception
{
//get the session, if its not in memory, this will load it
Session session = getAndEnter(id, false);
//Always delete it from the backing data store
if (_sessionDataStore != null)
{
boolean dsdel = _sessionDataStore.delete(id);
if (LOG.isDebugEnabled())
LOG.debug("Session id={} deleted in session data store {}", id, dsdel);
}
//delete it from the session object store
if (session != null)
{
session.setResident(false);
}
return doDelete(id);
}
@Override
public Set<String> checkExpiration(Set<String> candidates)
{
if (!isStarted())
return Collections.emptySet();
if (LOG.isDebugEnabled())
LOG.debug("{} checking expiration on {}", this, candidates);
Set<String> allCandidates = _sessionDataStore.getExpired(candidates);
Set<String> sessionsInUse = new HashSet<>();
if (allCandidates != null)
{
for (String c : allCandidates)
{
Session s = doGet(c);
if (s != null && s.getRequests() > 0) //if the session is in my cache, check its not in use first
sessionsInUse.add(c);
}
try
{
allCandidates.removeAll(sessionsInUse);
}
catch (UnsupportedOperationException e)
{
Set<String> tmp = new HashSet<>(allCandidates);
tmp.removeAll(sessionsInUse);
allCandidates = tmp;
}
}
return allCandidates;
}
Check a session for being inactive and
thus being able to be evicted, if eviction
is enabled.
Params: - session – session to check
/**
* Check a session for being inactive and
* thus being able to be evicted, if eviction
* is enabled.
*
* @param session session to check
*/
@Override
public void checkInactiveSession(Session session)
{
if (session == null)
return;
if (LOG.isDebugEnabled())
LOG.debug("Checking for idle {}", session.getId());
try (AutoLock s = session.lock())
{
if (getEvictionPolicy() > 0 && session.isIdleLongerThan(getEvictionPolicy()) &&
session.isValid() && session.isResident() && session.getRequests() <= 0)
{
//Be careful with saveOnInactiveEviction - you may be able to re-animate a session that was
//being managed on another node and has expired.
try
{
if (LOG.isDebugEnabled())
LOG.debug("Evicting idle session {}", session.getId());
//save before evicting
if (isSaveOnInactiveEviction() && _sessionDataStore != null)
{
if (_sessionDataStore.isPassivating())
session.willPassivate();
//Fake being dirty to force the write
session.getSessionData().setDirty(true);
_sessionDataStore.store(session.getId(), session.getSessionData());
}
doDelete(session.getId()); //detach from this cache
session.setResident(false);
}
catch (Exception e)
{
LOG.warn("Passivation of idle session {} failed", session.getId(), e);
}
}
}
}
@Override
public Session renewSessionId(String oldId, String newId, String oldExtendedId, String newExtendedId)
throws Exception
{
if (StringUtil.isBlank(oldId))
throw new IllegalArgumentException("Old session id is null");
if (StringUtil.isBlank(newId))
throw new IllegalArgumentException("New session id is null");
Session session = getAndEnter(oldId, true);
renewSessionId(session, newId, newExtendedId);
return session;
}
Swap the id on a session.
Params: - session – the session for which to do the swap
- newId – the new id
- newExtendedId – the full id plus node id
Throws: - Exception – if there was a failure saving the change
/**
* 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)
return;
try (AutoLock lock = session.lock())
{
String oldId = session.getId();
session.checkValidForWrite(); //can't change id on invalid session
session.getSessionData().setId(newId);
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.setExtendedId(newExtendedId); //remember the new extended id
session.setIdChanged(true); //session id changed
doPutIfAbsent(newId, session); //put the new id into our map
doDelete(oldId); //take old out of map
if (_sessionDataStore != null)
{
_sessionDataStore.delete(oldId); //delete the session data with the old id
_sessionDataStore.store(newId, session.getSessionData()); //save the session data with the new id
}
if (LOG.isDebugEnabled())
LOG.debug("Session id={} swapped for new id={}", oldId, newId);
}
}
@Override
public void setSaveOnInactiveEviction(boolean saveOnEvict)
{
_saveOnInactiveEviction = saveOnEvict;
}
@Override
public void setInvalidateOnShutdown(boolean invalidateOnShutdown)
{
_invalidateOnShutdown = invalidateOnShutdown;
}
@Override
public boolean isInvalidateOnShutdown()
{
return _invalidateOnShutdown;
}
Whether we should save a session that has been inactive before
we boot it from the cache.
Returns: true if an inactive session will be saved before being evicted
/**
* Whether we should save a session that has been inactive before
* we boot it from the cache.
*
* @return true if an inactive session will be saved before being evicted
*/
@ManagedAttribute(value = "save sessions before evicting from cache", readonly = true)
@Override
public boolean isSaveOnInactiveEviction()
{
return _saveOnInactiveEviction;
}
@Override
public Session newSession(HttpServletRequest request, String id, long time, long maxInactiveMs)
{
if (LOG.isDebugEnabled())
LOG.debug("Creating new session id={}", id);
Session session = newSession(request, _sessionDataStore.newSessionData(id, time, time, time, maxInactiveMs));
session.getSessionData().setLastNode(_context.getWorkerName());
try
{
if (isSaveOnCreate() && _sessionDataStore != null)
_sessionDataStore.store(id, session.getSessionData());
}
catch (Exception e)
{
LOG.warn("Save of new session {} failed", id, e);
}
return session;
}
@Override
public String toString()
{
return String.format("%s@%x[evict=%d,removeUnloadable=%b,saveOnCreate=%b,saveOnInactiveEvict=%b]",
this.getClass().getName(), this.hashCode(), _evictionPolicy,
_removeUnloadableSessions, _saveOnCreate, _saveOnInactiveEviction);
}
}