org.eclipse.jetty.server.session.HashSessionManager Maven / Gradle / Ivy
// ========================================================================
// Copyright (c) 1996-2009 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.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.log.Log;
/* ------------------------------------------------------------ */
/** An in-memory implementation of SessionManager.
*
*
*/
public class HashSessionManager extends AbstractSessionManager
{
private static int __id;
private Timer _timer;
private TimerTask _task;
private int _scavengePeriodMs=30000;
private int _savePeriodMs=0; //don't do period saves by default
private TimerTask _saveTask;
protected Map _sessions;
private File _storeDir;
private boolean _lazyLoad=false;
private boolean _sessionsLoaded=false;
/* ------------------------------------------------------------ */
public HashSessionManager()
{
super();
}
/* ------------------------------------------------------------ */
/* (non-Javadoc)
* @see org.eclipse.jetty.servlet.AbstractSessionManager#doStart()
*/
@Override
public void doStart() throws Exception
{
_sessions=new ConcurrentHashMap();
super.doStart();
_timer=new Timer("HashSessionScavenger-"+__id++, true);
setScavengePeriod(getScavengePeriod());
if (_storeDir!=null)
{
if (!_storeDir.exists())
_storeDir.mkdir();
if (!_lazyLoad)
restoreSessions();
}
setSavePeriod(getSavePeriod());
}
/* ------------------------------------------------------------ */
/* (non-Javadoc)
* @see org.eclipse.jetty.servlet.AbstractSessionManager#doStop()
*/
@Override
public void doStop() throws Exception
{
if (_storeDir != null)
saveSessions();
super.doStop();
_sessions.clear();
_sessions=null;
// stop the scavenger
synchronized(this)
{
if (_saveTask!=null)
_saveTask.cancel();
if (_task!=null)
_task.cancel();
if (_timer!=null)
_timer.cancel();
_timer=null;
}
}
/* ------------------------------------------------------------ */
/**
* @return seconds
*/
public int getScavengePeriod()
{
return _scavengePeriodMs/1000;
}
/* ------------------------------------------------------------ */
@Override
public Map getSessionMap()
{
return Collections.unmodifiableMap(_sessions);
}
/* ------------------------------------------------------------ */
@Override
public int getSessions()
{
int sessions=super.getSessions();
if (Log.isDebugEnabled())
{
if (_sessions.size()!=sessions)
Log.warn("sessions: "+_sessions.size()+"!="+sessions);
}
return sessions;
}
/* ------------------------------------------------------------ */
@Override
public void setMaxInactiveInterval(int seconds)
{
super.setMaxInactiveInterval(seconds);
if (_dftMaxIdleSecs>0&&_scavengePeriodMs>_dftMaxIdleSecs*1000)
setScavengePeriod((_dftMaxIdleSecs+9)/10);
}
/* ------------------------------------------------------------ */
public void setSavePeriod (int seconds)
{
int period = (seconds * 1000);
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();
}
catch (Exception e)
{
Log.warn(e);
}
}
};
_timer.schedule(_saveTask,_savePeriodMs,_savePeriodMs);
}
}
}
}
/* ------------------------------------------------------------ */
public int getSavePeriod ()
{
if (_savePeriodMs<=0)
return 0;
return _savePeriodMs/1000;
}
/* ------------------------------------------------------------ */
/**
* @param seconds
*/
public void setScavengePeriod(int seconds)
{
if (seconds==0)
seconds=60;
int old_period=_scavengePeriodMs;
int period=seconds*1000;
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.
*/
private 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);
long now=System.currentTimeMillis();
try
{
if (!_sessionsLoaded && _lazyLoad)
restoreSessions();
}
catch(Exception e)
{
Log.debug(e);
}
// Since Hashtable enumeration is not safe over deletes,
// we build a list of stale sessions, then go back and invalidate
// them
Object stale=null;
synchronized (HashSessionManager.this)
{
// For each session
for (Iterator i=_sessions.values().iterator(); i.hasNext();)
{
HashedSession session=i.next();
long idleTime=session._maxIdleMs;
if (idleTime>0&&session._accessed+idleTime0;)
{
// check it has not been accessed in the meantime
HashedSession session=(HashedSession)LazyList.get(stale,i);
long idleTime=session._maxIdleMs;
if (idleTime>0&&session._accessed+idleTime sessions=_sessions;
if (sessions==null)
return null;
return sessions.get(idInCluster);
}
/* ------------------------------------------------------------ */
@Override
protected void invalidateSessions()
{
// Invalidate all sessions to cause unbind events
ArrayList sessions=new ArrayList(_sessions.values());
for (Iterator i=sessions.iterator(); i.hasNext();)
{
HashedSession session=(HashedSession)i.next();
session.invalidate();
}
_sessions.clear();
}
/* ------------------------------------------------------------ */
@Override
protected AbstractSessionManager.Session newSession(HttpServletRequest request)
{
return new HashedSession(request);
}
/* ------------------------------------------------------------ */
protected AbstractSessionManager.Session newSession(long created, long accessed, String clusterId)
{
return new HashedSession(created,accessed, clusterId);
}
/* ------------------------------------------------------------ */
@Override
protected void removeSession(String clusterId)
{
_sessions.remove(clusterId);
}
/* ------------------------------------------------------------ */
public void setStoreDirectory (File dir)
{
_storeDir=dir;
}
/* ------------------------------------------------------------ */
public File getStoreDirectory ()
{
return _storeDir;
}
/* ------------------------------------------------------------ */
public void setLazyLoad(boolean lazyLoad)
{
_lazyLoad = lazyLoad;
}
/* ------------------------------------------------------------ */
public boolean isLazyLoad()
{
return _lazyLoad;
}
/* ------------------------------------------------------------ */
public void restoreSessions () throws Exception
{
if (_storeDir==null || !_storeDir.exists())
{
return;
}
if (!_storeDir.canRead())
{
Log.warn ("Unable to restore Sessions: Cannot read from Session storage directory "+_storeDir.getAbsolutePath());
return;
}
File[] files = _storeDir.listFiles();
for (int i=0;files!=null&&i> itor = _sessions.entrySet().iterator();
while (itor.hasNext())
{
Map.Entry entry = itor.next();
String id = (String)entry.getKey();
HashedSession session = (HashedSession)entry.getValue();
try
{
File file = new File (_storeDir, id);
if (file.exists())
file.delete();
file.createNewFile();
FileOutputStream fos = new FileOutputStream (file);
session.willPassivate();
session.save(fos);
session.didActivate();
fos.close();
}
catch (Exception e)
{
Log.warn("Problem persisting session "+id, e);
}
}
}
}
/* ------------------------------------------------------------ */
public HashedSession restoreSession (InputStream is) throws Exception
{
/*
* Take care of this class's fields first by calling
* defaultReadObject
*/
DataInputStream in = new DataInputStream(is);
String clusterId = in.readUTF();
String nodeId = in.readUTF();
boolean idChanged = in.readBoolean();
long created = in.readLong();
long cookieSet = in.readLong();
long accessed = in.readLong();
long lastAccessed = in.readLong();
//boolean invalid = in.readBoolean();
//boolean invalidate = in.readBoolean();
//long maxIdle = in.readLong();
//boolean isNew = in.readBoolean();
int requests = in.readInt();
HashedSession session = (HashedSession)newSession(created, System.currentTimeMillis(), clusterId);
session._cookieSet = cookieSet;
session._lastAccessed = lastAccessed;
int size = in.readInt();
if (size > 0)
{
ArrayList keys = new ArrayList();
for (int i=0; i0&&(_maxIdleMs/10)<_scavengePeriodMs)
HashSessionManager.this.setScavengePeriod((secs+9)/10);
}
/* ------------------------------------------------------------ */
@Override
protected Map newAttributeMap()
{
return new HashMap(3);
}
/* ------------------------------------------------------------ */
@Override
public void invalidate ()
throws IllegalStateException
{
super.invalidate();
remove();
}
/* ------------------------------------------------------------ */
public void remove()
{
String id=getId();
if (id==null)
return;
//all sessions are invalidated when jetty is stopped, make sure we don't
//remove all the sessions in this case
if (isStopping() || isStopped())
return;
if (_storeDir==null || !_storeDir.exists())
{
return;
}
File f = new File(_storeDir, id);
f.delete();
}
/* ------------------------------------------------------------ */
public void save(OutputStream os) throws IOException
{
DataOutputStream out = new DataOutputStream(os);
out.writeUTF(_clusterId);
out.writeUTF(_nodeId);
out.writeBoolean(_idChanged);
out.writeLong( _created);
out.writeLong(_cookieSet);
out.writeLong(_accessed);
out.writeLong(_lastAccessed);
/* Don't write these out, as they don't make sense to store because they
* either they cannot be true or their value will be restored in the
* Session constructor.
*/
//out.writeBoolean(_invalid);
//out.writeBoolean(_doInvalidate);
//out.writeLong(_maxIdleMs);
//out.writeBoolean( _newSession);
out.writeInt(_requests);
if (_values != null)
{
out.writeInt(_values.size());
Iterator itor = _values.keySet().iterator();
while (itor.hasNext())
{
String key = (String)itor.next();
out.writeUTF(key);
}
itor = _values.values().iterator();
ObjectOutputStream oos = new ObjectOutputStream(out);
while (itor.hasNext())
{
oos.writeObject(itor.next());
}
oos.close();
}
else
out.writeInt(0);
out.close();
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
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);
}
}
}
}