Mercurial Hosting > luan
diff src/org/eclipse/jetty/server/session/HashSessionManager.java @ 802:3428c60d7cfc
replace jetty jars with source
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 07 Sep 2016 21:15:48 -0600 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/eclipse/jetty/server/session/HashSessionManager.java Wed Sep 07 21:15:48 2016 -0600 @@ -0,0 +1,640 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.log.Logger; + + +/* ------------------------------------------------------------ */ +/** + * 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. + * + */ +public class HashSessionManager extends AbstractSessionManager +{ + final static Logger __log = SessionHandler.LOG; + + protected final ConcurrentMap<String,HashedSession> _sessions=new ConcurrentHashMap<String,HashedSession>(); + private static int __id; + private Timer _timer; + private boolean _timerStop=false; + private TimerTask _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 TimerTask _saveTask; + File _storeDir; + private boolean _lazyLoad=false; + private volatile boolean _sessionsLoaded=false; + private boolean _deleteUnrestorableSessions=false; + + + + + /* ------------------------------------------------------------ */ + public HashSessionManager() + { + super(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.servlet.AbstractSessionManager#doStart() + */ + @Override + public void doStart() throws Exception + { + super.doStart(); + + _timerStop=false; + ServletContext context = ContextHandler.getCurrentContext(); + if (context!=null) + _timer=(Timer)context.getAttribute("org.eclipse.jetty.server.session.timer"); + if (_timer==null) + { + _timerStop=true; + _timer=new Timer("HashSessionScavenger-"+__id++, true); + } + + setScavengePeriod(getScavengePeriod()); + + if (_storeDir!=null) + { + if (!_storeDir.exists()) + _storeDir.mkdirs(); + + if (!_lazyLoad) + restoreSessions(); + } + + setSavePeriod(getSavePeriod()); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.servlet.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 (_timer!=null && _timerStop) + _timer.cancel(); + _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(); + if (_savePeriodMs > 0 && _storeDir!=null) //only save if we have a directory configured + { + _saveTask = new TimerTask() + { + @Override + public void run() + { + try + { + saveSessions(true); + } + catch (Exception e) + { + __log.warn(e); + } + } + }; + _timer.schedule(_saveTask,_savePeriodMs,_savePeriodMs); + } + } + } + } + + /* ------------------------------------------------------------ */ + /** + * @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; + + if (_timer!=null && (period!=old_period || _task==null)) + { + synchronized (this) + { + if (_task!=null) + _task.cancel(); + _task = new TimerTask() + { + @Override + public void run() + { + scavenge(); + } + }; + _timer.schedule(_task,_scavengePeriodMs,_scavengePeriodMs); + } + } + } + + /* -------------------------------------------------------------- */ + /** + * 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(); + + 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 invalidateSessions() 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 - it is not invalidated. + for (HashedSession session : sessions) + { + session.save(false); + removeSession(session,false); + } + } + else + { + for (HashedSession session : sessions) + session.invalidate(); + } + + // check that no new sessions were created while we were iterating + sessions=new ArrayList<HashedSession>(_sessions.values()); + } + } + + /* ------------------------------------------------------------ */ + @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); + + FileInputStream in = null; + Exception error = null; + try + { + if (file.exists()) + { + in = new FileInputStream(file); + HashedSession session = restoreSession(in, null); + addSession(session, false); + session.didActivate(); + return session; + } + } + catch (Exception e) + { + error = e; + } + finally + { + if (in != null) IO.close(in); + + if (error != null) + { + if (isDeleteUnrestorableSessions() && file.exists() && file.getParentFile().equals(_storeDir) ) + { + file.delete(); + __log.warn("Deleting file for unrestorable session "+idInCuster, error); + } + else + { + __log.warn("Problem restoring session "+idInCuster, error); + } + } + else + file.delete(); //delete successfully restored file + + } + 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(true); + } + + /* ------------------------------------------------------------ */ + public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception + { + /* + * Take care of this class's fields first by calling + * defaultReadObject + */ + DataInputStream in = new DataInputStream(is); + try + { + String clusterId = in.readUTF(); + in.readUTF(); // nodeId + long created = in.readLong(); + long accessed = in.readLong(); + int requests = in.readInt(); + + if (session == null) + session = (HashedSession)newSession(created, accessed, clusterId); + session.setRequests(requests); + int size = in.readInt(); + if (size>0) + { + ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(in); + try + { + for (int i=0; i<size;i++) + { + String key = ois.readUTF(); + Object value = ois.readObject(); + session.setAttribute(key,value); + } + } + finally + { + IO.close(ois); + } + } + return session; + } + finally + { + IO.close(in); + } + } + + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + protected class ClassLoadingObjectInputStream extends ObjectInputStream + { + /* ------------------------------------------------------------ */ + public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException + { + super(in); + } + + /* ------------------------------------------------------------ */ + public ClassLoadingObjectInputStream () throws IOException + { + super(); + } + + /* ------------------------------------------------------------ */ + @Override + public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException + { + try + { + return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader()); + } + catch (ClassNotFoundException e) + { + return super.resolveClass(cl); + } + } + } +}