diff options
Diffstat (limited to 'jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java')
-rw-r--r-- | jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java | 664 |
1 files changed, 8 insertions, 656 deletions
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java index 68e5227e94..188c7ca067 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java @@ -16,681 +16,33 @@ // ======================================================================== // -package org.eclipse.jetty.server.session; - -import java.io.DataInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; - -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; - -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.util.ClassLoadingObjectInputStream; -import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; -import org.eclipse.jetty.util.thread.Scheduler; +package org.eclipse.jetty.server.session; -/* ------------------------------------------------------------ */ -/** +/** * HashSessionManager - * - * An in-memory implementation of SessionManager. - * <p> - * This manager supports saving sessions to disk, either periodically or at shutdown. - * Sessions can also have their content idle saved to disk to reduce the memory overheads of large idle sessions. - * <p> - * This manager will create it's own Timer instance to scavenge threads, unless it discovers a shared Timer instance - * set as the "org.eclipse.jetty.server.session.timer" attribute of the ContextHandler. * + * In memory-only session manager. + * */ -public class HashSessionManager extends AbstractSessionManager +public class HashSessionManager extends SessionManager { - final static Logger LOG = SessionHandler.LOG; - - protected final ConcurrentMap<String,HashedSession> _sessions=new ConcurrentHashMap<String,HashedSession>(); - private Scheduler _timer; - private Scheduler.Task _task; - long _scavengePeriodMs=30000; - long _savePeriodMs=0; //don't do period saves by default - long _idleSavePeriodMs = 0; // don't idle save sessions by default. - private Scheduler.Task _saveTask; - File _storeDir; - private boolean _lazyLoad=false; - private volatile boolean _sessionsLoaded=false; - private boolean _deleteUnrestorableSessions=false; + protected NullSessionDataStore _sessionDataStore = new NullSessionDataStore(); - /** - * Scavenger - * - */ - protected class Scavenger implements Runnable - { - @Override - public void run() - { - try - { - scavenge(); - } - finally - { - if (_timer != null && _timer.isRunning()) { - _task = _timer.schedule(this, _scavengePeriodMs, TimeUnit.MILLISECONDS); - } - } - } - } - - /** - * Saver - * - */ - protected class Saver implements Runnable - { - @Override - public void run() - { - try - { - saveSessions(true); - } - catch (Exception e) - { - LOG.warn(e); - } - finally - { - if (_timer != null && _timer.isRunning()) - _saveTask = _timer.schedule(this, _savePeriodMs, TimeUnit.MILLISECONDS); - } - } - } - - - /* ------------------------------------------------------------ */ - public HashSessionManager() - { - super(); - } - - /* ------------------------------------------------------------ */ - /** - * @see AbstractSessionManager#doStart() - */ @Override public void doStart() throws Exception { - //try shared scheduler from Server first - _timer = getSessionHandler().getServer().getBean(Scheduler.class); - if (_timer == null) - { - //try one passed into the context - ServletContext context = ContextHandler.getCurrentContext(); - if (context!=null) - _timer = (Scheduler)context.getAttribute("org.eclipse.jetty.server.session.timer"); - } - - if (_timer == null) - { - //make a scheduler if none useable - _timer=new ScheduledExecutorScheduler(toString()+"Timer",true); - addBean(_timer,true); - } - else - addBean(_timer,false); + _sessionStore = new MemorySessionStore(); + ((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore); super.doStart(); - - setScavengePeriod(getScavengePeriod()); - - if (_storeDir!=null) - { - if (!_storeDir.exists()) - _storeDir.mkdirs(); - - if (!_lazyLoad) - restoreSessions(); - } - - setSavePeriod(getSavePeriod()); } - /* ------------------------------------------------------------ */ - /** - * @see AbstractSessionManager#doStop() - */ @Override public void doStop() throws Exception { - // stop the scavengers - synchronized(this) - { - if (_saveTask!=null) - _saveTask.cancel(); - - _saveTask=null; - if (_task!=null) - _task.cancel(); - - _task=null; - - //if we're managing our own timer, remove it - if (isManaged(_timer)) - removeBean(_timer); - - _timer=null; - } - - - // This will callback invalidate sessions - where we decide if we will save super.doStop(); - - _sessions.clear(); - } - - /* ------------------------------------------------------------ */ - /** - * @return the period in seconds at which a check is made for sessions to be invalidated. - */ - public int getScavengePeriod() - { - return (int)(_scavengePeriodMs/1000); - } - - - /* ------------------------------------------------------------ */ - @Override - public int getSessions() - { - int sessions=super.getSessions(); - if (LOG.isDebugEnabled()) - { - if (_sessions.size()!=sessions) - LOG.warn("sessions: "+_sessions.size()+"!="+sessions); - } - return sessions; - } - - /* ------------------------------------------------------------ */ - /** - * @return seconds Idle period after which a session is saved - */ - public int getIdleSavePeriod() - { - if (_idleSavePeriodMs <= 0) - return 0; - - return (int)(_idleSavePeriodMs / 1000); - } - - /* ------------------------------------------------------------ */ - /** - * Configures the period in seconds after which a session is deemed idle and saved - * to save on session memory. - * - * The session is persisted, the values attribute map is cleared and the session set to idled. - * - * @param seconds Idle period after which a session is saved - */ - public void setIdleSavePeriod(int seconds) - { - _idleSavePeriodMs = seconds * 1000L; - } - - /* ------------------------------------------------------------ */ - @Override - public void setMaxInactiveInterval(int seconds) - { - super.setMaxInactiveInterval(seconds); - if (_dftMaxIdleSecs>0&&_scavengePeriodMs>_dftMaxIdleSecs*1000L) - setScavengePeriod((_dftMaxIdleSecs+9)/10); } - - /* ------------------------------------------------------------ */ - /** - * @param seconds the period is seconds at which sessions are periodically saved to disk - */ - public void setSavePeriod (int seconds) - { - long period = (seconds * 1000L); - if (period < 0) - period=0; - _savePeriodMs=period; - - if (_timer!=null) - { - synchronized (this) - { - if (_saveTask!=null) - _saveTask.cancel(); - _saveTask = null; - if (_savePeriodMs > 0 && _storeDir!=null) //only save if we have a directory configured - { - _saveTask = _timer.schedule(new Saver(),_savePeriodMs,TimeUnit.MILLISECONDS); - } - } - } - } - - /* ------------------------------------------------------------ */ - /** - * @return the period in seconds at which sessions are periodically saved to disk - */ - public int getSavePeriod () - { - if (_savePeriodMs<=0) - return 0; - - return (int)(_savePeriodMs/1000); - } - - /* ------------------------------------------------------------ */ - /** - * @param seconds the period in seconds at which a check is made for sessions to be invalidated. - */ - public void setScavengePeriod(int seconds) - { - if (seconds==0) - seconds=60; - - long old_period=_scavengePeriodMs; - long period=seconds*1000L; - if (period>60000) - period=60000; - if (period<1000) - period=1000; - - _scavengePeriodMs=period; - synchronized (this) - { - if (_timer!=null && (period!=old_period || _task==null)) - { - if (_task!=null) - { - _task.cancel(); - _task = null; - } - - _task = _timer.schedule(new Scavenger(),_scavengePeriodMs, TimeUnit.MILLISECONDS); - } - } - } - - /* -------------------------------------------------------------- */ - /** - * Find sessions that have timed out and invalidate them. This runs in the - * SessionScavenger thread. - */ - protected void scavenge() - { - //don't attempt to scavenge if we are shutting down - if (isStopping() || isStopped()) - return; - - Thread thread=Thread.currentThread(); - ClassLoader old_loader=thread.getContextClassLoader(); - try - { - if (_loader!=null) - thread.setContextClassLoader(_loader); - - // For each session - long now=System.currentTimeMillis(); - __log.debug("Scavenging sessions at {}", now); - - for (Iterator<HashedSession> i=_sessions.values().iterator(); i.hasNext();) - { - HashedSession session=i.next(); - long idleTime=session.getMaxInactiveInterval()*1000L; - if (idleTime>0&&session.getAccessed()+idleTime<now) - { - // Found a stale session, add it to the list - try - { - session.timeout(); - } - catch (Exception e) - { - __log.warn("Problem scavenging sessions", e); - } - } - else if (_idleSavePeriodMs > 0 && session.getAccessed()+_idleSavePeriodMs < now) - { - try - { - session.idle(); - } - catch (Exception e) - { - __log.warn("Problem idling session "+ session.getId(), e); - } - } - } - } - finally - { - thread.setContextClassLoader(old_loader); - } - } - - /* ------------------------------------------------------------ */ - @Override - protected void addSession(AbstractSession session) - { - if (isRunning()) - _sessions.put(session.getClusterId(),(HashedSession)session); - } - - /* ------------------------------------------------------------ */ - @Override - public AbstractSession getSession(String idInCluster) - { - if ( _lazyLoad && !_sessionsLoaded) - { - try - { - restoreSessions(); - } - catch(Exception e) - { - LOG.warn(e); - } - } - - Map<String,HashedSession> sessions=_sessions; - if (sessions==null) - return null; - - HashedSession session = sessions.get(idInCluster); - - if (session == null && _lazyLoad) - session=restoreSession(idInCluster); - if (session == null) - return null; - - if (_idleSavePeriodMs!=0) - session.deIdle(); - - return session; - } - - /* ------------------------------------------------------------ */ - @Override - protected void shutdownSessions() throws Exception - { - // Invalidate all sessions to cause unbind events - ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values()); - int loop=100; - while (sessions.size()>0 && loop-->0) - { - // If we are called from doStop - if (isStopping() && _storeDir != null && _storeDir.exists() && _storeDir.canWrite()) - { - // Then we only save and remove the session from memory- it is not invalidated. - for (HashedSession session : sessions) - { - session.save(false); - _sessions.remove(session.getClusterId()); - } - } - else - { - for (HashedSession session : sessions) - session.invalidate(); - } - - // check that no new sessions were created while we were iterating - sessions=new ArrayList<HashedSession>(_sessions.values()); - } - } - - - - /* ------------------------------------------------------------ */ - /** - * @see org.eclipse.jetty.server.SessionManager#renewSessionId(java.lang.String, java.lang.String, java.lang.String, java.lang.String) - */ - @Override - public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId) - { - try - { - Map<String,HashedSession> sessions=_sessions; - if (sessions == null) - return; - - HashedSession session = sessions.remove(oldClusterId); - if (session == null) - return; - - session.remove(); //delete any previously saved session - session.setClusterId(newClusterId); //update ids - session.setNodeId(newNodeId); - session.save(); //save updated session: TODO consider only saving file if idled - sessions.put(newClusterId, session); - - super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId); - } - catch (Exception e) - { - LOG.warn(e); - } - } - - /* ------------------------------------------------------------ */ - @Override - protected AbstractSession newSession(HttpServletRequest request) - { - return new HashedSession(this, request); - } - - /* ------------------------------------------------------------ */ - protected AbstractSession newSession(long created, long accessed, String clusterId) - { - return new HashedSession(this, created,accessed, clusterId); - } - - /* ------------------------------------------------------------ */ - @Override - protected boolean removeSession(String clusterId) - { - return _sessions.remove(clusterId)!=null; - } - - /* ------------------------------------------------------------ */ - public void setStoreDirectory (File dir) throws IOException - { - // CanonicalFile is used to capture the base store directory in a way that will - // work on Windows. Case differences may through off later checks using this directory. - _storeDir=dir.getCanonicalFile(); - } - - /* ------------------------------------------------------------ */ - public File getStoreDirectory () - { - return _storeDir; - } - - /* ------------------------------------------------------------ */ - public void setLazyLoad(boolean lazyLoad) - { - _lazyLoad = lazyLoad; - } - - /* ------------------------------------------------------------ */ - public boolean isLazyLoad() - { - return _lazyLoad; - } - - /* ------------------------------------------------------------ */ - public boolean isDeleteUnrestorableSessions() - { - return _deleteUnrestorableSessions; - } - - /* ------------------------------------------------------------ */ - public void setDeleteUnrestorableSessions(boolean deleteUnrestorableSessions) - { - _deleteUnrestorableSessions = deleteUnrestorableSessions; - } - - /* ------------------------------------------------------------ */ - public void restoreSessions () throws Exception - { - _sessionsLoaded = true; - - if (_storeDir==null || !_storeDir.exists()) - { - return; - } - - if (!_storeDir.canRead()) - { - LOG.warn ("Unable to restore Sessions: Cannot read from Session storage directory "+_storeDir.getAbsolutePath()); - return; - } - - String[] files = _storeDir.list(); - for (int i=0;files!=null&&i<files.length;i++) - { - restoreSession(files[i]); - } - } - - /* ------------------------------------------------------------ */ - protected synchronized HashedSession restoreSession(String idInCuster) - { - File file = new File(_storeDir,idInCuster); - - Exception error = null; - if (!file.exists()) - { - if (LOG.isDebugEnabled()) - { - LOG.debug("Not loading: {}",file); - } - return null; - } - - try (FileInputStream in = new FileInputStream(file)) - { - HashedSession session = restoreSession(in,null); - addSession(session,false); - session.didActivate(); - return session; - } - catch (Exception e) - { - error = e; - } - finally - { - if (error != null) - { - if (isDeleteUnrestorableSessions() && file.exists() && file.getParentFile().equals(_storeDir) ) - { - file.delete(); - LOG.warn("Deleting file for unrestorable session {} {}",idInCuster,error); - __log.debug(error); - } - else - { - __log.warn("Problem restoring session {} {}",idInCuster, error); - __log.debug(error); - } - } - else - { - // delete successfully restored file - file.delete(); - } - } - return null; - } - - /* ------------------------------------------------------------ */ - public void saveSessions(boolean reactivate) throws Exception - { - if (_storeDir==null || !_storeDir.exists()) - { - return; - } - - if (!_storeDir.canWrite()) - { - LOG.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable"); - return; - } - - for (HashedSession session : _sessions.values()) - session.save(reactivate); - } - - - /* ------------------------------------------------------------ */ - public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception - { - DataInputStream di = new DataInputStream(is); - - String clusterId = di.readUTF(); - di.readUTF(); // nodeId - - long created = di.readLong(); - long accessed = di.readLong(); - int requests = di.readInt(); - - if (session == null) - session = (HashedSession)newSession(created, accessed, clusterId); - - session.setRequests(requests); - - // Attributes - int size = di.readInt(); - - restoreSessionAttributes(di, size, session); - - try - { - int maxIdle = di.readInt(); - session.setMaxInactiveInterval(maxIdle); - } - catch (IOException e) - { - LOG.debug("No maxInactiveInterval persisted for session "+clusterId); - LOG.ignore(e); - } - - return session; - } - - - @SuppressWarnings("resource") - private void restoreSessionAttributes (InputStream is, int size, HashedSession session) - throws Exception - { - if (size>0) - { - // input stream should not be closed here - ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is); - for (int i=0; i<size;i++) - { - String key = ois.readUTF(); - Object value = ois.readObject(); - session.setAttribute(key,value); - } - } - } } |