diff options
author | Jan Bartel | 2015-11-19 23:48:50 +0000 |
---|---|---|
committer | Jan Bartel | 2015-11-19 23:48:50 +0000 |
commit | b2497895353561ff504973a0059ee700bca6500a (patch) | |
tree | 5a855a5989621332a6554a53b6d366ac4375160d /jetty-nosql/src | |
parent | 070284643bbc10e59044a3fd6704b5849b36a8fe (diff) | |
download | org.eclipse.jetty.project-b2497895353561ff504973a0059ee700bca6500a.tar.gz org.eclipse.jetty.project-b2497895353561ff504973a0059ee700bca6500a.tar.xz org.eclipse.jetty.project-b2497895353561ff504973a0059ee700bca6500a.zip |
Refactor api to use session id as string, and class for context id; mostly port mongo sessions.
Diffstat (limited to 'jetty-nosql/src')
6 files changed, 714 insertions, 1831 deletions
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSession.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSession.java deleted file mode 100644 index 149caa3479..0000000000 --- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSession.java +++ /dev/null @@ -1,224 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2015 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.nosql; - -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.servlet.http.HttpServletRequest; - -import org.eclipse.jetty.server.session.MemSession; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; - - -/* ------------------------------------------------------------ */ -public class NoSqlSession extends MemSession -{ - private final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session"); - - private final NoSqlSessionManager _manager; - private Set<String> _dirty; - private final AtomicInteger _active = new AtomicInteger(); - private Object _version; - private long _lastSync; - - /* ------------------------------------------------------------ */ - public NoSqlSession(NoSqlSessionManager manager, HttpServletRequest request) - { - super(manager, request); - _manager=manager; - _active.incrementAndGet(); - } - - /* ------------------------------------------------------------ */ - public NoSqlSession(NoSqlSessionManager manager, long created, long accessed, String clusterId, Object version) - { - super(manager, created,accessed,clusterId); - _manager=manager; - _version=version; - } - - /* ------------------------------------------------------------ */ - @Override - public Object doPutOrRemove(String name, Object value) - { - synchronized (this) - { - Object old = super.doPutOrRemove(name,value); - - if (_manager.getSavePeriod()==-2) - { - save(true); - } - return old; - } - } - - - - @Override - public void setAttribute(String name, Object value) - { - Object old = changeAttribute(name,value); - if (value == null && old == null) - return; //not dirty, no change - - if (value==null || !value.equals(old)) - { - if (_dirty==null) - { - _dirty=new HashSet<String>(); - } - - _dirty.add(name); - } - } - - - - @Override - protected void timeout() throws IllegalStateException - { - super.timeout(); - } - - - - /* ------------------------------------------------------------ */ - @Override - protected void checkValid() throws IllegalStateException - { - super.checkValid(); - } - - /* ------------------------------------------------------------ */ - @Override - protected boolean access(long time) - { - __log.debug("NoSqlSession:access:active {} time {}", _active, time); - if (_active.incrementAndGet()==1) - { - long period=_manager.getStalePeriod()*1000L; - if (period==0) - refresh(); - else if (period>0) - { - long stale=time-_lastSync; - __log.debug("NoSqlSession:access:stale "+stale); - if (stale>period) - refresh(); - } - } - - return super.access(time); - } - - /* ------------------------------------------------------------ */ - @Override - protected void complete() - { - super.complete(); - if(_active.decrementAndGet()==0) - { - switch(_manager.getSavePeriod()) - { - case 0: - save(isValid()); - break; - case 1: - if (isDirty()) - save(isValid()); - break; - - } - } - } - - /* ------------------------------------------------------------ */ - @Override - protected void doInvalidate() throws IllegalStateException - { - super.doInvalidate(); - //jb why save here? if the session is invalidated it should be removed - save(false); - } - - /* ------------------------------------------------------------ */ - protected void save(boolean activateAfterSave) - { - synchronized (this) - { - _version=_manager.save(this,_version,activateAfterSave); - _lastSync=getAccessed(); - } - } - - - /* ------------------------------------------------------------ */ - protected void refresh() - { - synchronized (this) - { - _version=_manager.refresh(this,_version); - } - } - - /* ------------------------------------------------------------ */ - public boolean isDirty() - { - synchronized (this) - { - return _dirty!=null && !_dirty.isEmpty(); - } - } - - /* ------------------------------------------------------------ */ - public Set<String> takeDirty() - { - synchronized (this) - { - Set<String> dirty=_dirty; - if (dirty==null) - dirty= new HashSet<String>(); - else - _dirty=null; - return dirty; - } - } - - /* ------------------------------------------------------------ */ - public Object getVersion() - { - return _version; - } - - @Override - public void setClusterId(String clusterId) - { - super.setClusterId(clusterId); - } - - @Override - public void setNodeId(String nodeId) - { - super.setNodeId(nodeId); - } -} diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionDataStore.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionDataStore.java new file mode 100644 index 0000000000..d6acaea04e --- /dev/null +++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionDataStore.java @@ -0,0 +1,99 @@ +// +// ======================================================================== +// Copyright (c) 1995-2015 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.nosql; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jetty.server.session.AbstractSessionDataStore; +import org.eclipse.jetty.server.session.SessionData; +import org.eclipse.jetty.server.session.SessionKey; + + +/** + * NoSqlSessionDataStore + * + * + */ +public abstract class NoSqlSessionDataStore extends AbstractSessionDataStore +{ + + public class NoSqlSessionData extends SessionData + { + private Object _version; + private Set<String> _dirtyAttributes = new HashSet<String>(); + + + /** + * @param id + * @param cpath + * @param vhost + * @param created + * @param accessed + * @param lastAccessed + * @param maxInactiveMs + */ + public NoSqlSessionData(String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs) + { + super(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs); + } + + public void setVersion (Object v) + { + _version = v; + } + + public Object getVersion () + { + return _version; + } + + @Override + public void setDirty(String name) + { + super.setDirty(name); + _dirtyAttributes.add(name); + } + + + public Set<String> takeDirtyAttributes() + { + Set<String> copy = new HashSet<>(_dirtyAttributes); + _dirtyAttributes.clear(); + return copy; + + } + + public Set<String> getAllAttributeNames () + { + return new HashSet<String>(_attributes.keySet()); + } + } + + + @Override + public SessionData newSessionData(String id, long created, long accessed, long lastAccessed, long maxInactiveMs) + { + return new NoSqlSessionData(id, _contextId.getCanonicalContextPath(), _contextId.getVhost(), created, accessed, lastAccessed, maxInactiveMs); + } + + + +} diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java deleted file mode 100644 index 7d5e9dca58..0000000000 --- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java +++ /dev/null @@ -1,432 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2015 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.nosql; - -import java.util.ArrayList; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; - -import javax.servlet.http.HttpServletRequest; - -import org.eclipse.jetty.server.SessionManager; -import org.eclipse.jetty.server.session.AbstractSession; -import org.eclipse.jetty.server.session.AbstractSessionManager; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; - -/** - * NoSqlSessionManager - * - * Base class for SessionManager implementations using nosql frameworks - */ -public abstract class NoSqlSessionManager extends AbstractSessionManager implements SessionManager -{ - private final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session"); - - protected final ConcurrentMap<String,NoSqlSession> _sessions=new ConcurrentHashMap<String,NoSqlSession>(); - - private int _stalePeriod=0; - private int _savePeriod=0; - private int _idlePeriod=-1; - private boolean _invalidateOnStop; - private boolean _preserveOnStop = true; - private boolean _saveAllAttributes; - - /* ------------------------------------------------------------ */ - /** - * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart() - */ - @Override - public void doStart() throws Exception - { - super.doStart(); - - } - - /* ------------------------------------------------------------ */ - @Override - protected void addSession(AbstractSession session) - { - if (isRunning()) - { - //add into memory - _sessions.put(session.getClusterId(),(NoSqlSession)session); - //add into db - ((NoSqlSession)session).save(true); - } - } - - /* ------------------------------------------------------------ */ - @Override - public AbstractSession getSession(String idInCluster) - { - NoSqlSession session = _sessions.get(idInCluster); - __log.debug("getSession {} ", session ); - - if (session==null) - { - //session not in this node's memory, load it - session=loadSession(idInCluster); - - if (session!=null) - { - //session exists, check another request thread hasn't loaded it too - NoSqlSession race=_sessions.putIfAbsent(idInCluster,session); - if (race!=null) - { - session.willPassivate(); - session.clearAttributes(); - session=race; - } - else - __log.debug("session loaded ", idInCluster); - - //check if the session we just loaded has actually expired, maybe while we weren't running - if (getMaxInactiveInterval() > 0 && session.getAccessed() > 0 && ((getMaxInactiveInterval()*1000L)+session.getAccessed()) < System.currentTimeMillis()) - { - __log.debug("session expired ", idInCluster); - expire(idInCluster); - session = null; - } - } - else - __log.debug("session does not exist {}", idInCluster); - } - - return session; - } - - /* ------------------------------------------------------------ */ - @Override - protected void shutdownSessions() throws Exception - { - //If we are stopping, and we're preserving sessions, then we want to - //save all of the sessions (including those that have been added during this method call) - //and then just remove them from memory. - - //If we don't wish to preserve sessions and we're stopping, then we should invalidate - //the session (which may remove it). - long gracefulStopMs = getContextHandler().getServer().getStopTimeout(); - long stopTime = 0; - if (gracefulStopMs > 0) - stopTime = System.nanoTime() + (TimeUnit.NANOSECONDS.convert(gracefulStopMs, TimeUnit.MILLISECONDS)); - - ArrayList<NoSqlSession> sessions=new ArrayList<NoSqlSession>(_sessions.values()); - - // loop while there are sessions, and while there is stop time remaining, or if no stop time, just 1 loop - while (sessions.size() > 0 && ((stopTime > 0 && (System.nanoTime() < stopTime)) || (stopTime == 0))) - { - for (NoSqlSession session : sessions) - { - if (isPreserveOnStop()) - { - //we don't want to delete the session, so save the session - //and remove from memory - session.save(false); - _sessions.remove(session.getClusterId()); - } - else - { - //invalidate the session so listeners will be called and also removes the session - session.invalidate(); - } - } - - //check if we should terminate our loop if we're not using the stop timer - if (stopTime == 0) - { - break; - } - // Get any sessions that were added by other requests during processing and go around the loop again - sessions=new ArrayList<NoSqlSession>(_sessions.values()); - } - } - - - /* ------------------------------------------------------------ */ - @Override - protected AbstractSession newSession(HttpServletRequest request) - { - return new NoSqlSession(this,request); - } - - /* ------------------------------------------------------------ */ - /** Remove the session from the in-memory list for this context. - * Also remove the context sub-document for this session id from the db. - * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String) - */ - @Override - protected boolean removeSession(String idInCluster) - { - NoSqlSession session = _sessions.remove(idInCluster); - - try - { - if (session != null) - { - return remove(session); - } - } - catch (Exception e) - { - __log.warn("Problem deleting session {}", idInCluster,e); - } - - return session != null; - - } - - /* ------------------------------------------------------------ */ - protected void expire( String idInCluster ) - { - //get the session from memory - NoSqlSession session = _sessions.get(idInCluster); - - try - { - if (session == null) - { - //we need to expire the session with its listeners, so load it - session = loadSession(idInCluster); - } - - if (session != null) - session.timeout(); - } - catch (Exception e) - { - __log.warn("Problem expiring session {}", idInCluster,e); - } - } - - - public void invalidateSession (String idInCluster) - { - NoSqlSession session = _sessions.get(idInCluster); - try - { - __log.debug("invalidating session {}", idInCluster); - if (session != null) - { - session.invalidate(); - } - } - catch (Exception e) - { - __log.warn("Problem invalidating session {}", idInCluster,e); - } - } - - - /* ------------------------------------------------------------ */ - /** - * The State Period is the maximum time in seconds that an in memory session is allows to be stale: - * <ul> - * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li> - * <li>If the state period is set to a value < 0, then no staleness check will be made.</li> - * <li>If the state period is set to 0, then a staleness check is made whenever the active request count goes from 0 to 1.</li> - * </ul> - * @return the stalePeriod in seconds - */ - public int getStalePeriod() - { - return _stalePeriod; - } - - /* ------------------------------------------------------------ */ - /** - * The State Period is the maximum time in seconds that an in memory session is allows to be stale: - * <ul> - * <li>If this period is exceeded, the DB will be checked to see if a more recent version is available.</li> - * <li>If the state period is set to a value < 0, then no staleness check will be made.</li> - * <li>If the state period is set to 0, then a staleness check is made whenever the active request count goes from 0 to 1.</li> - * </ul> - * @param stalePeriod the stalePeriod in seconds - */ - public void setStalePeriod(int stalePeriod) - { - _stalePeriod = stalePeriod; - } - - /* ------------------------------------------------------------ */ - /** - * The Save Period is the time in seconds between saves of a dirty session to the DB. - * When this period is exceeded, the a dirty session will be written to the DB: <ul> - * <li>a save period of -2 means the session is written to the DB whenever setAttribute is called.</li> - * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li> - * <li>a save period of 0 means the session is written to the DB whenever the active request count goes from 1 to 0.</li> - * <li>a save period of 1 means the session is written to the DB whenever the active request count goes from 1 to 0 and the session is dirty.</li> - * <li>a save period of > 1 means the session is written after that period in seconds of being dirty.</li> - * </ul> - * @return the savePeriod -2,-1,0,1 or the period in seconds >=2 - */ - public int getSavePeriod() - { - return _savePeriod; - } - - /* ------------------------------------------------------------ */ - /** - * The Save Period is the time in seconds between saves of a dirty session to the DB. - * When this period is exceeded, the a dirty session will be written to the DB: <ul> - * <li>a save period of -2 means the session is written to the DB whenever setAttribute is called.</li> - * <li>a save period of -1 means the session is never saved to the DB other than on a shutdown</li> - * <li>a save period of 0 means the session is written to the DB whenever the active request count goes from 1 to 0.</li> - * <li>a save period of 1 means the session is written to the DB whenever the active request count goes from 1 to 0 and the session is dirty.</li> - * <li>a save period of > 1 means the session is written after that period in seconds of being dirty.</li> - * </ul> - * @param savePeriod the savePeriod -2,-1,0,1 or the period in seconds >=2 - */ - public void setSavePeriod(int savePeriod) - { - _savePeriod = savePeriod; - } - - /* ------------------------------------------------------------ */ - /** - * The Idle Period is the time in seconds before an in memory session is passivated. - * When this period is exceeded, the session will be passivated and removed from memory. If the session was dirty, it will be written to the DB. - * If the idle period is set to a value < 0, then the session is never idled. - * If the save period is set to 0, then the session is idled whenever the active request count goes from 1 to 0. - * @return the idlePeriod - */ - public int getIdlePeriod() - { - return _idlePeriod; - } - - /* ------------------------------------------------------------ */ - /** - * The Idle Period is the time in seconds before an in memory session is passivated. - * When this period is exceeded, the session will be passivated and removed from memory. If the session was dirty, it will be written to the DB. - * If the idle period is set to a value < 0, then the session is never idled. - * If the save period is set to 0, then the session is idled whenever the active request count goes from 1 to 0. - * @param idlePeriod the idlePeriod in seconds - */ - public void setIdlePeriod(int idlePeriod) - { - _idlePeriod = idlePeriod; - } - - /* ------------------------------------------------------------ */ - /** - * Invalidate sessions when the session manager is stopped otherwise save them to the DB. - * @return the invalidateOnStop - */ - public boolean isInvalidateOnStop() - { - return _invalidateOnStop; - } - - /* ------------------------------------------------------------ */ - /** - * Preserve sessions when the session manager is stopped otherwise remove them from the DB. - * @return the removeOnStop - */ - public boolean isPreserveOnStop() - { - return _preserveOnStop; - } - - /* ------------------------------------------------------------ */ - /** - * Invalidate sessions when the session manager is stopped otherwise save them to the DB. - * @param invalidateOnStop the invalidateOnStop to set - */ - public void setInvalidateOnStop(boolean invalidateOnStop) - { - _invalidateOnStop = invalidateOnStop; - } - - /* ------------------------------------------------------------ */ - /** - * Preserve sessions when the session manager is stopped otherwise remove them from the DB. - * @param preserveOnStop the preserveOnStop to set - */ - public void setPreserveOnStop(boolean preserveOnStop) - { - _preserveOnStop = preserveOnStop; - } - - /* ------------------------------------------------------------ */ - /** - * Save all attributes of a session or only update the dirty attributes. - * @return the saveAllAttributes - */ - public boolean isSaveAllAttributes() - { - return _saveAllAttributes; - } - - /* ------------------------------------------------------------ */ - /** - * Save all attributes of a session or only update the dirty attributes. - * @param saveAllAttributes the saveAllAttributes to set - */ - public void setSaveAllAttributes(boolean saveAllAttributes) - { - _saveAllAttributes = saveAllAttributes; - } - - /* ------------------------------------------------------------ */ - @Override - public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId) - { - - // Take the old session out of the list of sessions - // Change to the new id - // Put it back into the list of sessions - // Update permanent storage - - synchronized (this) - { - try - { - NoSqlSession session = _sessions.remove(oldClusterId); - update (session, newClusterId, newNodeId); - session.setClusterId(newClusterId); - session.setNodeId(newNodeId); - _sessions.put(newClusterId, session); - } - catch (Exception e) - { - __log.warn(e); - } - } - super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId); - } - - - /* ------------------------------------------------------------ */ - abstract protected NoSqlSession loadSession(String clusterId); - - /* ------------------------------------------------------------ */ - abstract protected Object save(NoSqlSession session,Object version, boolean activateAfterSave); - - /* ------------------------------------------------------------ */ - abstract protected Object refresh(NoSqlSession session, Object version); - - /* ------------------------------------------------------------ */ - abstract protected boolean remove(NoSqlSession session); - - /* ------------------------------------------------------------ */ - abstract protected void update(NoSqlSession session, String newClusterId, String newNodeId) throws Exception; - -} diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java new file mode 100644 index 0000000000..5eb8bbd1dd --- /dev/null +++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java @@ -0,0 +1,574 @@ +// +// ======================================================================== +// Copyright (c) 1995-2015 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.nosql.mongodb; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jetty.nosql.NoSqlSessionDataStore; +import org.eclipse.jetty.server.session.SessionData; +import org.eclipse.jetty.util.ClassLoadingObjectInputStream; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import com.mongodb.DBObject; +import com.mongodb.WriteConcern; + +/** + * MongoSessionDataStore + * + * The document model is an outer object that contains the elements: + * <ul> + * <li>"id" : session_id </li> + * <li>"created" : create_time </li> + * <li>"accessed": last_access_time </li> + * <li>"maxIdle" : max_idle_time setting as session was created </li> + * <li>"expiry" : time at which session should expire </li> + * <li>"valid" : session_valid </li> + * <li>"context" : a nested object containing 1 nested object per context for which the session id is in use + * </ul> + * Each of the nested objects inside the "context" element contains: + * <ul> + * <li>unique_context_name : nested object containing name:value pairs of the session attributes for that context</li> + * </ul> + * <p> + * One of the name:value attribute pairs will always be the special attribute "__metadata__". The value + * is an object representing a version counter which is incremented every time the attributes change. + * </p> + * <p> + * For example: + * <pre> + * { "_id" : ObjectId("52845534a40b66410f228f23"), + * "accessed" : NumberLong("1384818548903"), + * "maxIdle" : 1, + * "context" : { "::_contextA" : { "A" : "A", + * "__metadata__" : { "version" : NumberLong(2) } + * }, + * "::_contextB" : { "B" : "B", + * "__metadata__" : { "version" : NumberLong(1) } + * } + * }, + * "created" : NumberLong("1384818548903"), + * "expiry" : NumberLong("1384818549903"), + * "id" : "w01ijx2vnalgv1sqrpjwuirprp7", + * "valid" : true + * } + * </pre> + * <p> + * In MongoDB, the nesting level is indicated by "." separators for the key name. Thus to + * interact with a session attribute, the key is composed of: + * <code>"context".unique_context_name.attribute_name</code> + * Eg <code>"context"."::/contextA"."A"</code> + */ +public class MongoSessionDataStore extends NoSqlSessionDataStore +{ + + private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); + + + /** + * Special attribute for a session that is context-specific + */ + private final static String __METADATA = "__metadata__"; + + /** + * Name of nested document field containing 1 sub document per context for which the session id is in use + */ + private final static String __CONTEXT = "context"; + + /** + * Special attribute per session per context, incremented each time attributes are modified + */ + public final static String __VERSION = __METADATA + ".version"; + + /** + * Last access time of session + */ + public final static String __ACCESSED = "accessed"; + + /** + * Time this session will expire, based on last access time and maxIdle + */ + public final static String __EXPIRY = "expiry"; + + /** + * The max idle time of a session (smallest value across all contexts which has a session with the same id) + */ + public final static String __MAX_IDLE = "maxIdle"; + + /** + * Time of session creation + */ + private final static String __CREATED = "created"; + + /** + * Whether or not session is valid + */ + public final static String __VALID = "valid"; + + /** + * Session id + */ + public final static String __ID = "id"; + + + + /** + * Utility value of 1 for a session version for this context + */ + private DBObject _version_1; + + /** + * Access to MongoDB + */ + private DBCollection _dbSessions; + + + private long _gracePeriodMs = 1000L * 60 * 60; //default grace period is 1hr + + public void setDBCollection (DBCollection collection) + { + _dbSessions = collection; + } + + + public DBCollection getDBCollection () + { + return _dbSessions; + } + + public int getGracePeriodSec () + { + return (int)(_gracePeriodMs == 0L? 0 : _gracePeriodMs/1000L); + } + + public void setGracePeriodSec (int sec) + { + if (sec < 0) + _gracePeriodMs = 0; + else + _gracePeriodMs = sec * 1000L; + } + + /** + * @see org.eclipse.jetty.server.session.SessionDataStore#load(org.eclipse.jetty.server.session.SessionKey) + */ + @Override + public SessionData load(String id) throws Exception + { + DBObject sessionDocument = _dbSessions.findOne(new BasicDBObject(__ID, id)); + + if (LOG.isDebugEnabled()) + LOG.debug("id={} loaded={}", id, sessionDocument); + if (sessionDocument == null) + return null; + + Boolean valid = (Boolean)sessionDocument.get(__VALID); + + if (LOG.isDebugEnabled()) + LOG.debug("id={} valid={}", id, valid); + if (valid == null || !valid) + return null; + + try + { + Object version = sessionDocument.get(getContextSubfield( __VERSION)); + Long created = (Long)sessionDocument.get(__CREATED); + Long accessed = (Long)sessionDocument.get(__ACCESSED); + Long maxInactive = (Long)sessionDocument.get(__MAX_IDLE); + Long expiry = (Long)sessionDocument.get(__EXPIRY); + + NoSqlSessionData data = null; + + // get the session for the context + DBObject sessionSubDocumentForContext = (DBObject)getNestedValue(sessionDocument,getContextField()); + + if (LOG.isDebugEnabled()) LOG.debug("attrs {}", sessionSubDocumentForContext); + + if (sessionSubDocumentForContext != null) + { + if (LOG.isDebugEnabled()) + LOG.debug("Session {} present for context {}", id, _contextId); + + //only load a session if it exists for this context + data = (NoSqlSessionData)newSessionData(id, created, accessed, accessed, maxInactive); + data.setVersion(version); + data.setExpiry(expiry); + data.setContextPath(_contextId.getCanonicalContextPath()); + data.setVhost(_contextId.getVhost()); + + HashMap<String, Object> attributes = new HashMap<>(); + for (String name : sessionSubDocumentForContext.keySet()) + { + //skip special metadata attribute which is not one of the actual session attributes + if ( __METADATA.equals(name) ) + continue; + + String attr = decodeName(name); + Object value = decodeValue(sessionSubDocumentForContext.get(name)); + + attributes.put(attr,value); + } + + data.putAllAttributes(attributes); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("Session {} not present for context {}", id, _contextId); + } + + return data; + } + catch (Exception e) + { + LOG.warn(e); + } + + return null; + } + + /** + * @see org.eclipse.jetty.server.session.SessionDataStore#delete(org.eclipse.jetty.server.session.SessionKey) + */ + @Override + public boolean delete(String id) throws Exception + { + if (LOG.isDebugEnabled()) + LOG.debug("Remove:session {} for context ",id, _contextId); + + /* + * Check if the session exists and if it does remove the context + * associated with this session + */ + BasicDBObject mongoKey = new BasicDBObject(__ID, id); + + DBObject sessionDocument = _dbSessions.findOne(mongoKey,_version_1); + + if (sessionDocument != null) + { + BasicDBObject remove = new BasicDBObject(); + BasicDBObject unsets = new BasicDBObject(); + unsets.put(getContextField(),1); + remove.put("$unset",unsets); + _dbSessions.update(mongoKey,remove,false,false,WriteConcern.SAFE); + + return true; + } + else + { + return false; + } + + } + + /** + * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set) + */ + @Override + public Set<String> getExpired(Set<String> candidates) + { + long upperBound = System.currentTimeMillis(); + Set<String> expiredSessions = new HashSet<>(); + + //firstly ask mongo to verify if these candidate ids have expired + BasicDBObject query = new BasicDBObject(); + query.put(__ID,new BasicDBObject("$in", candidates)); + query.put(__EXPIRY, new BasicDBObject("$gt", 0)); + query.put(__EXPIRY, new BasicDBObject("$lt", upperBound)); + + DBCursor verifiedExpiredSessions = null; + try + { + verifiedExpiredSessions = _dbSessions.find(query, new BasicDBObject(__ID, 1)); + for ( DBObject session : verifiedExpiredSessions ) + { + String id = (String)session.get(__ID); + if (LOG.isDebugEnabled()) LOG.debug("Mongo confirmed expired session {}", id); + expiredSessions.add(id); + } + } + finally + { + if (verifiedExpiredSessions != null) verifiedExpiredSessions.close(); + } + + + //now ask mongo to find sessions that expired a while ago + upperBound = upperBound - (3 * _gracePeriodMs); + query.clear(); + query.put(__EXPIRY, new BasicDBObject("$gt", 0)); + query.put(__EXPIRY, new BasicDBObject("$lt", upperBound)); + + DBCursor oldExpiredSessions = null; + try + { + oldExpiredSessions = _dbSessions.find(query, new BasicDBObject(__ID, 1)); + for (DBObject session : oldExpiredSessions) + { + String id = (String)session.get(__ID); + if (LOG.isDebugEnabled()) LOG.debug("Mongo found old expired session {}", id); + expiredSessions.add(id); + } + + } + finally + { + oldExpiredSessions.close(); + } + + return expiredSessions; + } + + /** + * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(org.eclipse.jetty.server.session.SessionKey, org.eclipse.jetty.server.session.SessionData, boolean) + */ + @Override + public void doStore(String id, SessionData data, boolean isNew) throws Exception + { + // TODO + NoSqlSessionData nsqd = (NoSqlSessionData)data; + + // Form query for upsert + BasicDBObject key = new BasicDBObject(__ID, id); + + // Form updates + BasicDBObject update = new BasicDBObject(); + boolean upsert = false; + BasicDBObject sets = new BasicDBObject(); + BasicDBObject unsets = new BasicDBObject(); + + Object version = ((NoSqlSessionData)data).getVersion(); + + // New session + if (isNew) + { + upsert = true; + version = new Long(1); + sets.put(__CREATED,nsqd.getCreated()); + sets.put(__VALID,true); + + sets.put(getContextSubfield(__VERSION),version); + sets.put(__MAX_IDLE, nsqd.getMaxInactiveMs()); + sets.put(__EXPIRY, nsqd.getExpiry()); + } + else + { + version = new Long(((Number)version).longValue() + 1); + update.put("$inc",_version_1); + //if max idle time and/or expiry is smaller for this context, then choose that for the whole session doc + BasicDBObject fields = new BasicDBObject(); + fields.append(__MAX_IDLE, true); + fields.append(__EXPIRY, true); + DBObject o = _dbSessions.findOne(new BasicDBObject("id", id), fields); + if (o != null) + { + Integer currentMaxIdle = (Integer)o.get(__MAX_IDLE); + Long currentExpiry = (Long)o.get(__EXPIRY); + if (currentMaxIdle != null && nsqd.getMaxInactiveMs() > 0 && nsqd.getMaxInactiveMs() < currentMaxIdle) + sets.put(__MAX_IDLE, nsqd.getMaxInactiveMs()); + if (currentExpiry != null && nsqd.getExpiry() > 0 && nsqd.getExpiry() != currentExpiry) + sets.put(__EXPIRY, nsqd.getExpiry()); + } + else + LOG.warn("Session {} not found, can't update", id); + } + + sets.put(__ACCESSED, nsqd.getAccessed()); + + Set<String> names = nsqd.takeDirtyAttributes(); + if (isNew) + names.addAll(nsqd.getAllAttributeNames()); // note dirty may include removed names + + + for (String name : names) + { + Object value = data.getAttribute(name); + if (value == null) + unsets.put(getContextField() + "." + encodeName(name),1); + else + sets.put(getContextField() + "." + encodeName(name),encodeName(value)); + } + + // Do the upsert + if (!sets.isEmpty()) + update.put("$set",sets); + if (!unsets.isEmpty()) + update.put("$unset",unsets); + + _dbSessions.update(key,update,upsert,false,WriteConcern.SAFE); + + if (LOG.isDebugEnabled()) + LOG.debug("Save:db.sessions.update( {}, {} )", key, update); + } + + + + + @Override + protected void doStart() throws Exception + { + if (_dbSessions == null) + throw new IllegalStateException("DBCollection not set"); + + _version_1 = new BasicDBObject(getContextSubfield(__VERSION),1); + + super.doStart(); + } + + @Override + protected void doStop() throws Exception + { + // TODO Auto-generated method stub + super.doStop(); + } + + /*------------------------------------------------------------ */ + private String getContextField () + { + return __CONTEXT + "." + canonicalizeVHost(_contextId.getVhost()) + ":" + _contextId.getCanonicalContextPath(); + } + + + + private String canonicalizeVHost (String vhost) + { + if (vhost == null) + return ""; + + return vhost.replace('.', '_'); + } + + + private String getContextSubfield (String attr) + { + return getContextField () +"."+ attr; + } + + + /*------------------------------------------------------------ */ + protected Object decodeValue(final Object valueToDecode) throws IOException, ClassNotFoundException + { + if (valueToDecode == null || valueToDecode instanceof Number || valueToDecode instanceof String || valueToDecode instanceof Boolean || valueToDecode instanceof Date) + { + return valueToDecode; + } + else if (valueToDecode instanceof byte[]) + { + final byte[] decodeObject = (byte[])valueToDecode; + final ByteArrayInputStream bais = new ByteArrayInputStream(decodeObject); + final ClassLoadingObjectInputStream objectInputStream = new ClassLoadingObjectInputStream(bais); + return objectInputStream.readUnshared(); + } + else if (valueToDecode instanceof DBObject) + { + Map<String, Object> map = new HashMap<String, Object>(); + for (String name : ((DBObject)valueToDecode).keySet()) + { + String attr = decodeName(name); + map.put(attr,decodeValue(((DBObject)valueToDecode).get(name))); + } + return map; + } + else + { + throw new IllegalStateException(valueToDecode.getClass().toString()); + } + } + /*------------------------------------------------------------ */ + protected String decodeName(String name) + { + return name.replace("%2E",".").replace("%25","%"); + } + + + /*------------------------------------------------------------ */ + protected String encodeName(String name) + { + return name.replace("%","%25").replace(".","%2E"); + } + + + /*------------------------------------------------------------ */ + protected Object encodeName(Object value) throws IOException + { + if (value instanceof Number || value instanceof String || value instanceof Boolean || value instanceof Date) + { + return value; + } + else if (value.getClass().equals(HashMap.class)) + { + BasicDBObject o = new BasicDBObject(); + for (Map.Entry<?, ?> entry : ((Map<?, ?>)value).entrySet()) + { + if (!(entry.getKey() instanceof String)) + { + o = null; + break; + } + o.append(encodeName(entry.getKey().toString()),encodeName(entry.getValue())); + } + + if (o != null) + return o; + } + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bout); + out.reset(); + out.writeUnshared(value); + out.flush(); + return bout.toByteArray(); + } + + /*------------------------------------------------------------ */ + /** + * Dig through a given dbObject for the nested value + */ + private Object getNestedValue(DBObject dbObject, String nestedKey) + { + String[] keyChain = nestedKey.split("\\."); + + DBObject temp = dbObject; + + for (int i = 0; i < keyChain.length - 1; ++i) + { + temp = (DBObject)temp.get(keyChain[i]); + + if ( temp == null ) + { + return null; + } + } + + return temp.get(keyChain[keyChain.length - 1]); + } + +} diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java index ab7a7f564b..f50d74fd69 100644 --- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java +++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java @@ -20,93 +20,40 @@ package org.eclipse.jetty.nosql.mongodb; import java.net.UnknownHostException; -import java.util.HashSet; -import java.util.Iterator; import java.util.Random; import java.util.Set; -import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.SessionManager; -import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.session.AbstractSessionIdManager; -import org.eclipse.jetty.server.session.SessionHandler; +import org.eclipse.jetty.server.session.Session; import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; -import org.eclipse.jetty.util.thread.Scheduler; import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DBCollection; -import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.Mongo; import com.mongodb.MongoException; /** - * Based partially on the JDBCSessionIdManager. - * <p> - * Theory is that we really only need the session id manager for the local - * instance so we have something to scavenge on, namely the list of known ids - * <p> - * This class has a timer that runs a periodic scavenger thread to query - * for all id's known to this node whose precalculated expiry time has passed. - * <p> - * These found sessions are then run through the invalidateAll(id) method that - * is a bit hinky but is supposed to notify all handlers this id is now DOA and - * ought to be cleaned up. this ought to result in a save operation on the session - * that will change the valid field to false (this conjecture is unvalidated atm) + * Manager of session ids based on sessions stored in Mongo. + * */ public class MongoSessionIdManager extends AbstractSessionIdManager { - private final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session"); + private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); - final static DBObject __version_1 = new BasicDBObject(MongoSessionManager.__VERSION,1); - final static DBObject __valid_false = new BasicDBObject(MongoSessionManager.__VALID,false); - final static DBObject __valid_true = new BasicDBObject(MongoSessionManager.__VALID,true); + final static DBObject __version_1 = new BasicDBObject(MongoSessionDataStore.__VERSION,1); + final static DBObject __valid_false = new BasicDBObject(MongoSessionDataStore.__VALID,false); + final static DBObject __valid_true = new BasicDBObject(MongoSessionDataStore.__VALID,true); - final static long __defaultScavengePeriod = 30 * 60 * 1000; // every 30 minutes - final DBCollection _sessions; protected Server _server; - private Scheduler _scheduler; - private boolean _ownScheduler; - private Scheduler.Task _scavengerTask; - private Scheduler.Task _purgerTask; - - - - private long _scavengePeriod = __defaultScavengePeriod; - - - /** - * purge process is enabled by default - */ - private boolean _purge = true; - - /** - * purge process would run daily by default - */ - private long _purgeDelay = 24 * 60 * 60 * 1000; // every day - - /** - * how long do you want to persist sessions that are no longer - * valid before removing them completely - */ - private long _purgeInvalidAge = 24 * 60 * 60 * 1000; // default 1 day - - /** - * how long do you want to leave sessions that are still valid before - * assuming they are dead and removing them - */ - private long _purgeValidAge = 7 * 24 * 60 * 60 * 1000; // default 1 week /** @@ -114,57 +61,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager */ protected final Set<String> _sessionsIds = new ConcurrentHashSet<>(); - /** - * The maximum number of items to return from a purge query. - */ - private int _purgeLimit = 0; - - private int _scavengeBlockSize; - - - /** - * Scavenger - * - */ - protected class Scavenger implements Runnable - { - @Override - public void run() - { - try - { - scavenge(); - } - finally - { - if (_scheduler != null && _scheduler.isRunning()) - _scavengerTask = _scheduler.schedule(this, _scavengePeriod, TimeUnit.MILLISECONDS); - } - } - } - - - /** - * Purger - * - */ - protected class Purger implements Runnable - { - @Override - public void run() - { - try - { - purge(); - } - finally - { - if (_scheduler != null && _scheduler.isRunning()) - _purgerTask = _scheduler.schedule(this, _purgeDelay, TimeUnit.MILLISECONDS); - } - } - } - + @@ -193,187 +90,11 @@ public class MongoSessionIdManager extends AbstractSessionIdManager // so that we can take advantage of index prefixes // http://docs.mongodb.org/manual/core/index-compound/#compound-index-prefix _sessions.ensureIndex( - BasicDBObjectBuilder.start().add(MongoSessionManager.__VALID, 1).add(MongoSessionManager.__ACCESSED, 1).get(), + BasicDBObjectBuilder.start().add(MongoSessionDataStore.__VALID, 1).add(MongoSessionDataStore.__ACCESSED, 1).get(), BasicDBObjectBuilder.start().add("sparse", false).add("background", true).get()); } - /* ------------------------------------------------------------ */ - /** - * Scavenge is a process that periodically checks the tracked session - * ids of this given instance of the session id manager to see if they - * are past the point of expiration. - */ - protected void scavenge() - { - long now = System.currentTimeMillis(); - __log.debug("SessionIdManager:scavenge:at {}", now); - /* - * run a query returning results that: - * - are in the known list of sessionIds - * - the expiry time has passed - * - * we limit the query to return just the __ID so we are not sucking back full sessions - * - * break scavenge query into blocks for faster mongo queries - */ - Set<String> block = new HashSet<String>(); - - Iterator<String> itor = _sessionsIds.iterator(); - while (itor.hasNext()) - { - block.add(itor.next()); - if ((_scavengeBlockSize > 0) && (block.size() == _scavengeBlockSize)) - { - //got a block - scavengeBlock (now, block); - //reset for next run - block.clear(); - } - } - - //non evenly divisble block size, or doing it all at once - if (!block.isEmpty()) - scavengeBlock(now, block); - } - - - /* ------------------------------------------------------------ */ - /** - * Check a block of session ids for expiry and thus scavenge. - * - * @param atTime purge at time - * @param ids set of session ids - */ - protected void scavengeBlock (long atTime, Set<String> ids) - { - if (ids == null) - return; - - BasicDBObject query = new BasicDBObject(); - query.put(MongoSessionManager.__ID,new BasicDBObject("$in", ids )); - query.put(MongoSessionManager.__EXPIRY, new BasicDBObject("$gt", 0)); - query.put(MongoSessionManager.__EXPIRY, new BasicDBObject("$lt", atTime)); - - DBCursor checkSessions = _sessions.find(query, new BasicDBObject(MongoSessionManager.__ID, 1)); - - for ( DBObject session : checkSessions ) - { - __log.debug("SessionIdManager:scavenge: expiring session {}", (String)session.get(MongoSessionManager.__ID)); - expireAll((String)session.get(MongoSessionManager.__ID)); - } - } - - /* ------------------------------------------------------------ */ - /** - * ScavengeFully will expire all sessions. In most circumstances - * you should never need to call this method. - * - * <b>USE WITH CAUTION</b> - */ - protected void scavengeFully() - { - __log.debug("SessionIdManager:scavengeFully"); - - DBCursor checkSessions = _sessions.find(); - - for (DBObject session : checkSessions) - { - expireAll((String)session.get(MongoSessionManager.__ID)); - } - - } - - /* ------------------------------------------------------------ */ - /** - * Purge is a process that cleans the mongodb cluster of old sessions that are no - * longer valid. - * - * There are two checks being done here: - * - * - if the accessed time is older than the current time minus the purge invalid age - * and it is no longer valid then remove that session - * - if the accessed time is older then the current time minus the purge valid age - * then we consider this a lost record and remove it - * - * NOTE: if your system supports long lived sessions then the purge valid age should be - * set to zero so the check is skipped. - * - * The second check was added to catch sessions that were being managed on machines - * that might have crashed without marking their sessions as 'valid=false' - */ - protected void purge() - { - __log.debug("PURGING"); - BasicDBObject invalidQuery = new BasicDBObject(); - - invalidQuery.put(MongoSessionManager.__VALID, false); - invalidQuery.put(MongoSessionManager.__ACCESSED, new BasicDBObject("$lt",System.currentTimeMillis() - _purgeInvalidAge)); - - DBCursor oldSessions = _sessions.find(invalidQuery, new BasicDBObject(MongoSessionManager.__ID, 1)); - - if (_purgeLimit > 0) - { - oldSessions.limit(_purgeLimit); - } - - for (DBObject session : oldSessions) - { - String id = (String)session.get("id"); - - __log.debug("MongoSessionIdManager:purging invalid session {}", id); - - _sessions.remove(session); - } - - if (_purgeValidAge != 0) - { - BasicDBObject validQuery = new BasicDBObject(); - - validQuery.put(MongoSessionManager.__VALID, true); - validQuery.put(MongoSessionManager.__ACCESSED,new BasicDBObject("$lt",System.currentTimeMillis() - _purgeValidAge)); - - oldSessions = _sessions.find(validQuery,new BasicDBObject(MongoSessionManager.__ID,1)); - - if (_purgeLimit > 0) - { - oldSessions.limit(_purgeLimit); - } - - for (DBObject session : oldSessions) - { - String id = (String)session.get(MongoSessionManager.__ID); - - __log.debug("MongoSessionIdManager:purging valid session {}", id); - - _sessions.remove(session); - } - } - - } - - /* ------------------------------------------------------------ */ - /** - * Purge is a process that cleans the mongodb cluster of old sessions that are no - * longer valid. - * - */ - protected void purgeFully() - { - BasicDBObject invalidQuery = new BasicDBObject(); - invalidQuery.put(MongoSessionManager.__VALID, false); - - DBCursor oldSessions = _sessions.find(invalidQuery, new BasicDBObject(MongoSessionManager.__ID, 1)); - - for (DBObject session : oldSessions) - { - String id = (String)session.get(MongoSessionManager.__ID); - - __log.debug("MongoSessionIdManager:purging invalid session {}", id); - - _sessions.remove(session); - } - - } + /* ------------------------------------------------------------ */ @@ -382,165 +103,18 @@ public class MongoSessionIdManager extends AbstractSessionIdManager return _sessions; } - - /* ------------------------------------------------------------ */ - public boolean isPurgeEnabled() - { - return _purge; - } - - /* ------------------------------------------------------------ */ - public void setPurge(boolean purge) - { - this._purge = purge; - } - - - /* ------------------------------------------------------------ */ - /** - * The period in seconds between scavenge checks. - * - * @param scavengePeriod the scavenge period in seconds - */ - public void setScavengePeriod(long scavengePeriod) - { - if (scavengePeriod <= 0) - _scavengePeriod = __defaultScavengePeriod; - else - _scavengePeriod = TimeUnit.SECONDS.toMillis(scavengePeriod); - } - - /* ------------------------------------------------------------ */ - /** When scavenging, the max number of session ids in the query. - * - * @param size the scavenge block size - */ - public void setScavengeBlockSize (int size) - { - _scavengeBlockSize = size; - } - - /* ------------------------------------------------------------ */ - public int getScavengeBlockSize () - { - return _scavengeBlockSize; - } - - - /* ------------------------------------------------------------ */ - /** - * The maximum number of items to return from a purge query. If <= 0 there is no limit. Defaults to 0 - * - * @param purgeLimit the purge limit - */ - public void setPurgeLimit(int purgeLimit) - { - _purgeLimit = purgeLimit; - } - - /* ------------------------------------------------------------ */ - public int getPurgeLimit() - { - return _purgeLimit; - } - - - - /* ------------------------------------------------------------ */ - public void setPurgeDelay(long purgeDelay) - { - if ( isRunning() ) - { - throw new IllegalStateException(); - } - - this._purgeDelay = purgeDelay; - } - - /* ------------------------------------------------------------ */ - public long getPurgeInvalidAge() - { - return _purgeInvalidAge; - } - - /* ------------------------------------------------------------ */ - /** - * sets how old a session is to be persisted past the point it is - * no longer valid - * @param purgeValidAge the purge valid age - */ - public void setPurgeInvalidAge(long purgeValidAge) - { - this._purgeInvalidAge = purgeValidAge; - } - - /* ------------------------------------------------------------ */ - public long getPurgeValidAge() - { - return _purgeValidAge; - } - - /* ------------------------------------------------------------ */ - /** - * sets how old a session is to be persist past the point it is - * considered no longer viable and should be removed - * - * NOTE: set this value to 0 to disable purging of valid sessions - * @param purgeValidAge the purge valid age - */ - public void setPurgeValidAge(long purgeValidAge) - { - this._purgeValidAge = purgeValidAge; - } /* ------------------------------------------------------------ */ @Override protected void doStart() throws Exception { - __log.debug("MongoSessionIdManager:starting"); + LOG.debug("MongoSessionIdManager:starting"); synchronized (this) { - //try and use a common scheduler, fallback to own - _scheduler =_server.getBean(Scheduler.class); - if (_scheduler == null) - { - _scheduler = new ScheduledExecutorScheduler(); - _ownScheduler = true; - _scheduler.start(); - } - else if (!_scheduler.isStarted()) - throw new IllegalStateException("Shared scheduler not started"); - - - //setup the scavenger thread - if (_scavengePeriod > 0) - { - if (_scavengerTask != null) - { - _scavengerTask.cancel(); - _scavengerTask = null; - } - - _scavengerTask = _scheduler.schedule(new Scavenger(), _scavengePeriod, TimeUnit.MILLISECONDS); - } - else if (__log.isDebugEnabled()) - __log.debug("Scavenger disabled"); - - - //if purging is enabled, setup the purge thread - if ( _purge ) - { - if (_purgerTask != null) - { - _purgerTask.cancel(); - _purgerTask = null; - } - _purgerTask = _scheduler.schedule(new Purger(), _purgeDelay, TimeUnit.MILLISECONDS); - } - else if (__log.isDebugEnabled()) - __log.debug("Purger disabled"); + + } } @@ -548,26 +122,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager @Override protected void doStop() throws Exception { - synchronized (this) - { - if (_scavengerTask != null) - { - _scavengerTask.cancel(); - _scavengerTask = null; - } - - if (_purgerTask != null) - { - _purgerTask.cancel(); - _purgerTask = null; - } - - if (_ownScheduler && _scheduler != null) - { - _scheduler.stop(); - _scheduler = null; - } - } + super.doStop(); } @@ -585,7 +140,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager if ( o != null ) { - Boolean valid = (Boolean)o.get(MongoSessionManager.__VALID); + Boolean valid = (Boolean)o.get(MongoSessionDataStore.__VALID); if ( valid == null ) { return false; @@ -599,7 +154,7 @@ public class MongoSessionIdManager extends AbstractSessionIdManager /* ------------------------------------------------------------ */ @Override - public void addSession(HttpSession session) + public void useId(Session session) { if (session == null) { @@ -610,22 +165,22 @@ public class MongoSessionIdManager extends AbstractSessionIdManager * already a part of the index in mongo... */ - __log.debug("MongoSessionIdManager:addSession {}", session.getId()); + LOG.debug("MongoSessionIdManager:addSession {}", session.getId()); _sessionsIds.add(session.getId()); - } + /* ------------------------------------------------------------ */ @Override - public void removeSession(HttpSession session) + public void removeId(String id) { - if (session == null) + if (id == null) { return; } - _sessionsIds.remove(session.getId()); + _sessionsIds.remove(id); } /* ------------------------------------------------------------ */ @@ -637,81 +192,23 @@ public class MongoSessionIdManager extends AbstractSessionIdManager @Override public void expireAll(String sessionId) { - _sessionsIds.remove(sessionId); - - //tell all contexts that may have a session object with this id to - //get rid of them - Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class); - for (int i=0; contexts!=null && i<contexts.length; i++) - { - SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class); - if (sessionHandler != null) - { - SessionManager manager = sessionHandler.getSessionManager(); - - if (manager != null && manager instanceof MongoSessionManager) - { - ((MongoSessionManager)manager).invalidateSession(sessionId); - } - } - } + synchronized(_sessionsIds) + { + super.expireAll(sessionId); + } } - /* ------------------------------------------------------------ */ - /** - * Expire this session for all contexts that are sharing the session - * id. - * @param sessionId the session id - */ - public void expireAll (String sessionId) - { - _sessionsIds.remove(sessionId); - - - //tell all contexts that may have a session object with this id to - //get rid of them - Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class); - for (int i=0; contexts!=null && i<contexts.length; i++) - { - SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class); - if (sessionHandler != null) - { - SessionManager manager = sessionHandler.getSessionManager(); - - if (manager != null && manager instanceof MongoSessionManager) - { - ((MongoSessionManager)manager).expire(sessionId); - } - } - } - } + /* ------------------------------------------------------------ */ @Override public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request) { - //generate a new id - String newClusterId = newSessionId(request.hashCode()); - - _sessionsIds.remove(oldClusterId);//remove the old one from the list - _sessionsIds.add(newClusterId); //add in the new session id to the list - - //tell all contexts to update the id - Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class); - for (int i=0; contexts!=null && i<contexts.length; i++) + synchronized (_sessionsIds) { - SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class); - if (sessionHandler != null) - { - SessionManager manager = sessionHandler.getSessionManager(); - - if (manager != null && manager instanceof MongoSessionManager) - { - ((MongoSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getExtendedId(newClusterId, request)); - } - } + super.renewSessionId(oldClusterId, oldNodeId, request); } } - + } diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java index df8f917188..bd214eb5e4 100644 --- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java +++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java @@ -29,9 +29,11 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import org.eclipse.jetty.nosql.NoSqlSession; -import org.eclipse.jetty.nosql.NoSqlSessionManager; + import org.eclipse.jetty.server.SessionIdManager; +import org.eclipse.jetty.server.session.AbstractSessionStore; +import org.eclipse.jetty.server.session.MemorySessionStore; +import org.eclipse.jetty.server.session.SessionManager; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; @@ -93,71 +95,10 @@ import com.mongodb.WriteConcern; * Eg <code>"context"."::/contextA"."A"</code> */ @ManagedObject("Mongo Session Manager") -public class MongoSessionManager extends NoSqlSessionManager +public class MongoSessionManager extends SessionManager { - private static final Logger LOG = Log.getLogger(MongoSessionManager.class); - - private final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session"); - - /* - * strings used as keys or parts of keys in mongo - */ - /** - * Special attribute for a session that is context-specific - */ - private final static String __METADATA = "__metadata__"; - - - /** - * Session id - */ - public final static String __ID = "id"; - - /** - * Time of session creation - */ - private final static String __CREATED = "created"; - - /** - * Whether or not session is valid - */ - public final static String __VALID = "valid"; - - /** - * Time at which session was invalidated - */ - public final static String __INVALIDATED = "invalidated"; - - /** - * Last access time of session - */ - public final static String __ACCESSED = "accessed"; - - /** - * Time this session will expire, based on last access time and maxIdle - */ - public final static String __EXPIRY = "expiry"; - - /** - * The max idle time of a session (smallest value across all contexts which has a session with the same id) - */ - public final static String __MAX_IDLE = "maxIdle"; - - /** - * Name of nested document field containing 1 sub document per context for which the session id is in use - */ - private final static String __CONTEXT = "context"; - - - /** - * Special attribute per session per context, incremented each time attributes are modified - */ - public final static String __VERSION = __METADATA + ".version"; + private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); - /** - * the context id is only set when this class has been started - */ - private String _contextId = null; /** @@ -166,16 +107,14 @@ public class MongoSessionManager extends NoSqlSessionManager private DBCollection _dbSessions; - /** - * Utility value of 1 for a session version for this context - */ - private DBObject _version_1; + private MongoSessionDataStore _sessionDataStore; /* ------------------------------------------------------------ */ public MongoSessionManager() throws UnknownHostException, MongoException { - + _sessionStore = new MemorySessionStore(); + _sessionDataStore = new MongoSessionDataStore(); } @@ -183,22 +122,9 @@ public class MongoSessionManager extends NoSqlSessionManager /*------------------------------------------------------------ */ @Override public void doStart() throws Exception - { + { + ((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore); super.doStart(); - String[] hosts = getContextHandler().getVirtualHosts(); - - if (hosts == null || hosts.length == 0) - hosts = new String[] - { "::" }; // IPv6 equiv of 0.0.0.0 - - String contextPath = getContext().getContextPath(); - if (contextPath == null || "".equals(contextPath)) - { - contextPath = "*"; - } - - _contextId = createContextId(hosts,contextPath); - _version_1 = new BasicDBObject(getContextAttributeKey(__VERSION),1); } /* ------------------------------------------------------------ */ @@ -214,489 +140,8 @@ public class MongoSessionManager extends NoSqlSessionManager } - /* ------------------------------------------------------------ */ - @Override - protected synchronized Object save(NoSqlSession session, Object version, boolean activateAfterSave) - { - try - { - __log.debug("MongoSessionManager:save session {}", session.getClusterId()); - session.willPassivate(); - - // Form query for upsert - BasicDBObject key = new BasicDBObject(__ID,session.getClusterId()); - - // Form updates - BasicDBObject update = new BasicDBObject(); - boolean upsert = false; - BasicDBObject sets = new BasicDBObject(); - BasicDBObject unsets = new BasicDBObject(); - - - // handle valid or invalid - if (session.isValid()) - { - long expiry = (session.getMaxInactiveInterval() > 0?(session.getAccessed()+(1000L*getMaxInactiveInterval())):0); - __log.debug("MongoSessionManager: calculated expiry {} for session {}", expiry, session.getId()); - - // handle new or existing - if (version == null) - { - // New session - upsert = true; - version = new Long(1); - sets.put(__CREATED,session.getCreationTime()); - sets.put(__VALID,true); - - sets.put(getContextAttributeKey(__VERSION),version); - sets.put(__MAX_IDLE, getMaxInactiveInterval()); - sets.put(__EXPIRY, expiry); - } - else - { - version = new Long(((Number)version).longValue() + 1); - update.put("$inc",_version_1); - //if max idle time and/or expiry is smaller for this context, then choose that for the whole session doc - BasicDBObject fields = new BasicDBObject(); - fields.append(__MAX_IDLE, true); - fields.append(__EXPIRY, true); - DBObject o = _dbSessions.findOne(new BasicDBObject("id",session.getClusterId()), fields); - if (o != null) - { - Integer currentMaxIdle = (Integer)o.get(__MAX_IDLE); - Long currentExpiry = (Long)o.get(__EXPIRY); - if (currentMaxIdle != null && getMaxInactiveInterval() > 0 && getMaxInactiveInterval() < currentMaxIdle) - sets.put(__MAX_IDLE, getMaxInactiveInterval()); - if (currentExpiry != null && expiry > 0 && expiry != currentExpiry) - sets.put(__EXPIRY, expiry); - } - } - - sets.put(__ACCESSED,session.getAccessed()); - Set<String> names = session.takeDirty(); - if (isSaveAllAttributes() || upsert) - { - names.addAll(session.getNames()); // note dirty may include removed names - } - - for (String name : names) - { - Object value = session.getAttribute(name); - if (value == null) - unsets.put(getContextKey() + "." + encodeName(name),1); - else - sets.put(getContextKey() + "." + encodeName(name),encodeName(value)); - } - } - else - { - sets.put(__VALID,false); - sets.put(__INVALIDATED, System.currentTimeMillis()); - unsets.put(getContextKey(),1); - } - - // Do the upsert - if (!sets.isEmpty()) - update.put("$set",sets); - if (!unsets.isEmpty()) - update.put("$unset",unsets); - - _dbSessions.update(key,update,upsert,false,WriteConcern.SAFE); - - if (__log.isDebugEnabled()) - __log.debug("MongoSessionManager:save:db.sessions.update( {}, {} )", key, update); - - if (activateAfterSave) - session.didActivate(); - - return version; - } - catch (Exception e) - { - LOG.warn(e); - } - return null; - } - - /*------------------------------------------------------------ */ - @Override - protected Object refresh(NoSqlSession session, Object version) - { - __log.debug("MongoSessionManager:refresh session {}", session.getId()); - - // check if our in memory version is the same as what is on the disk - if (version != null) - { - DBObject o = _dbSessions.findOne(new BasicDBObject(__ID,session.getClusterId()),_version_1); - - if (o != null) - { - Object saved = getNestedValue(o, getContextAttributeKey(__VERSION)); - - if (saved != null && saved.equals(version)) - { - __log.debug("MongoSessionManager:refresh not needed session {}", session.getId()); - return version; - } - version = saved; - } - } - - // If we are here, we have to load the object - DBObject o = _dbSessions.findOne(new BasicDBObject(__ID,session.getClusterId())); - - // If it doesn't exist, invalidate - if (o == null) - { - __log.debug("MongoSessionManager:refresh:marking session {} invalid, no object", session.getClusterId()); - session.invalidate(); - return null; - } - - // If it has been flagged invalid, invalidate - Boolean valid = (Boolean)o.get(__VALID); - if (valid == null || !valid) - { - __log.debug("MongoSessionManager:refresh:marking session {} invalid, valid flag {}", session.getClusterId(), valid); - session.invalidate(); - return null; - } - - // We need to update the attributes. We will model this as a passivate, - // followed by bindings and then activation. - session.willPassivate(); - try - { - DBObject attrs = (DBObject)getNestedValue(o,getContextKey()); - //if disk version now has no attributes, get rid of them - if (attrs == null || attrs.keySet().size() == 0) - { - session.clearAttributes(); - } - else - { - //iterate over the names of the attributes on the disk version, updating the value - for (String name : attrs.keySet()) - { - //skip special metadata field which is not one of the session attributes - if (__METADATA.equals(name)) - continue; - - String attr = decodeName(name); - Object value = decodeValue(attrs.get(name)); - - //session does not already contain this attribute, so bind it - if (session.getAttribute(attr) == null) - { - session.doPutOrRemove(attr,value); - session.bindValue(attr,value); - } - else //session already contains this attribute, update its value - { - session.doPutOrRemove(attr,value); - } - - } - // cleanup, remove values from session, that don't exist in data anymore: - for (String str : session.getNames()) - { - if (!attrs.keySet().contains(encodeName(str))) - { - session.doPutOrRemove(str,null); - session.unbindValue(str,session.getAttribute(str)); - } - } - } - - /* - * We are refreshing so we should update the last accessed time. - */ - BasicDBObject key = new BasicDBObject(__ID,session.getClusterId()); - BasicDBObject sets = new BasicDBObject(); - // Form updates - BasicDBObject update = new BasicDBObject(); - sets.put(__ACCESSED,System.currentTimeMillis()); - // Do the upsert - if (!sets.isEmpty()) - { - update.put("$set",sets); - } - - _dbSessions.update(key,update,false,false,WriteConcern.SAFE); - - session.didActivate(); - - return version; - } - catch (Exception e) - { - LOG.warn(e); - } - - return null; - } - - /*------------------------------------------------------------ */ - @Override - protected synchronized NoSqlSession loadSession(String clusterId) - { - DBObject o = _dbSessions.findOne(new BasicDBObject(__ID,clusterId)); - - __log.debug("MongoSessionManager:id={} loaded={}", clusterId, o); - if (o == null) - return null; - - Boolean valid = (Boolean)o.get(__VALID); - __log.debug("MongoSessionManager:id={} valid={}", clusterId, valid); - if (valid == null || !valid) - return null; - - try - { - Object version = o.get(getContextAttributeKey(__VERSION)); - Long created = (Long)o.get(__CREATED); - Long accessed = (Long)o.get(__ACCESSED); - - NoSqlSession session = null; - - // get the session for the context - DBObject attrs = (DBObject)getNestedValue(o,getContextKey()); - - __log.debug("MongoSessionManager:attrs {}", attrs); - if (attrs != null) - { - __log.debug("MongoSessionManager: session {} present for context {}", clusterId, getContextKey()); - //only load a session if it exists for this context - session = new NoSqlSession(this,created,accessed,clusterId,version); - - for (String name : attrs.keySet()) - { - //skip special metadata attribute which is not one of the actual session attributes - if ( __METADATA.equals(name) ) - continue; - - String attr = decodeName(name); - Object value = decodeValue(attrs.get(name)); - - session.doPutOrRemove(attr,value); - session.bindValue(attr,value); - } - session.didActivate(); - } - else - __log.debug("MongoSessionManager: session {} not present for context {}",clusterId, getContextKey()); - - return session; - } - catch (Exception e) - { - LOG.warn(e); - } - return null; - } - - - - /*------------------------------------------------------------ */ - /** - * Remove the per-context sub document for this session id. - * @see org.eclipse.jetty.nosql.NoSqlSessionManager#remove(org.eclipse.jetty.nosql.NoSqlSession) - */ - @Override - protected boolean remove(NoSqlSession session) - { - __log.debug("MongoSessionManager:remove:session {} for context {}",session.getClusterId(), getContextKey()); - - /* - * Check if the session exists and if it does remove the context - * associated with this session - */ - BasicDBObject key = new BasicDBObject(__ID,session.getClusterId()); - - DBObject o = _dbSessions.findOne(key,_version_1); - - if (o != null) - { - BasicDBObject remove = new BasicDBObject(); - BasicDBObject unsets = new BasicDBObject(); - unsets.put(getContextKey(),1); - remove.put("$unset",unsets); - _dbSessions.update(key,remove,false,false,WriteConcern.SAFE); - - return true; - } - else - { - return false; - } - } - - - - /** - * @see org.eclipse.jetty.nosql.NoSqlSessionManager#expire(java.lang.String) - */ - @Override - protected void expire (String idInCluster) - { - __log.debug("MongoSessionManager:expire session {} ", idInCluster); - - //Expire the session for this context - super.expire(idInCluster); - - //If the outer session document has not already been marked invalid, do so. - DBObject validKey = new BasicDBObject(__VALID, true); - DBObject o = _dbSessions.findOne(new BasicDBObject(__ID,idInCluster), validKey); - - if (o != null && (Boolean)o.get(__VALID)) - { - BasicDBObject update = new BasicDBObject(); - BasicDBObject sets = new BasicDBObject(); - sets.put(__VALID,false); - sets.put(__INVALIDATED, System.currentTimeMillis()); - update.put("$set",sets); - - BasicDBObject key = new BasicDBObject(__ID,idInCluster); - _dbSessions.update(key,update,false,false,WriteConcern.SAFE); - } - } - - - /*------------------------------------------------------------ */ - /** - * Change the session id. Note that this will change the session id for all contexts for which the session id is in use. - * @see org.eclipse.jetty.nosql.NoSqlSessionManager#update(org.eclipse.jetty.nosql.NoSqlSession, java.lang.String, java.lang.String) - */ - @Override - protected void update(NoSqlSession session, String newClusterId, String newNodeId) throws Exception - { - BasicDBObject key = new BasicDBObject(__ID, session.getClusterId()); - BasicDBObject sets = new BasicDBObject(); - BasicDBObject update = new BasicDBObject(__ID, newClusterId); - sets.put("$set", update); - _dbSessions.update(key, sets, false, false,WriteConcern.SAFE); - } - - /*------------------------------------------------------------ */ - protected String encodeName(String name) - { - return name.replace("%","%25").replace(".","%2E"); - } - - /*------------------------------------------------------------ */ - protected String decodeName(String name) - { - return name.replace("%2E",".").replace("%25","%"); - } - - /*------------------------------------------------------------ */ - protected Object encodeName(Object value) throws IOException - { - if (value instanceof Number || value instanceof String || value instanceof Boolean || value instanceof Date) - { - return value; - } - else if (value.getClass().equals(HashMap.class)) - { - BasicDBObject o = new BasicDBObject(); - for (Map.Entry<?, ?> entry : ((Map<?, ?>)value).entrySet()) - { - if (!(entry.getKey() instanceof String)) - { - o = null; - break; - } - o.append(encodeName(entry.getKey().toString()),encodeName(entry.getValue())); - } - - if (o != null) - return o; - } - - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(bout); - out.reset(); - out.writeUnshared(value); - out.flush(); - return bout.toByteArray(); - } - - /*------------------------------------------------------------ */ - protected Object decodeValue(final Object valueToDecode) throws IOException, ClassNotFoundException - { - if (valueToDecode == null || valueToDecode instanceof Number || valueToDecode instanceof String || valueToDecode instanceof Boolean || valueToDecode instanceof Date) - { - return valueToDecode; - } - else if (valueToDecode instanceof byte[]) - { - final byte[] decodeObject = (byte[])valueToDecode; - final ByteArrayInputStream bais = new ByteArrayInputStream(decodeObject); - final ClassLoadingObjectInputStream objectInputStream = new ClassLoadingObjectInputStream(bais); - return objectInputStream.readUnshared(); - } - else if (valueToDecode instanceof DBObject) - { - Map<String, Object> map = new HashMap<String, Object>(); - for (String name : ((DBObject)valueToDecode).keySet()) - { - String attr = decodeName(name); - map.put(attr,decodeValue(((DBObject)valueToDecode).get(name))); - } - return map; - } - else - { - throw new IllegalStateException(valueToDecode.getClass().toString()); - } - } + - - /*------------------------------------------------------------ */ - private String getContextKey() - { - return __CONTEXT + "." + _contextId; - } - - /*------------------------------------------------------------ */ - /** Get a dot separated key for - * @param key - * @return - */ - private String getContextAttributeKey(String attr) - { - return getContextKey()+ "." + attr; - } - - /*------------------------------------------------------------ */ - @ManagedOperation(value="purge invalid sessions in the session store based on normal criteria", impact="ACTION") - public void purge() - { - ((MongoSessionIdManager)_sessionIdManager).purge(); - } - - - /*------------------------------------------------------------ */ - @ManagedOperation(value="full purge of invalid sessions in the session store", impact="ACTION") - public void purgeFully() - { - ((MongoSessionIdManager)_sessionIdManager).purgeFully(); - } - - /*------------------------------------------------------------ */ - @ManagedOperation(value="scavenge sessions known to this manager", impact="ACTION") - public void scavenge() - { - ((MongoSessionIdManager)_sessionIdManager).scavenge(); - } - - /*------------------------------------------------------------ */ - @ManagedOperation(value="scanvenge all sessions", impact="ACTION") - public void scavengeFully() - { - ((MongoSessionIdManager)_sessionIdManager).scavengeFully(); - } - /*------------------------------------------------------------ */ /** * returns the total number of session objects in the session store @@ -710,81 +155,5 @@ public class MongoSessionManager extends NoSqlSessionManager { return _dbSessions.find().count(); } - - /*------------------------------------------------------------ */ - /** - * MongoDB keys are . delimited for nesting so .'s are protected characters - * - * @param virtualHosts - * @param contextPath - * @return - */ - private String createContextId(String[] virtualHosts, String contextPath) - { - String contextId = virtualHosts[0] + contextPath; - - contextId.replace('/', '_'); - contextId.replace('.','_'); - contextId.replace('\\','_'); - - return contextId; - } - - /*------------------------------------------------------------ */ - /** - * Dig through a given dbObject for the nested value - */ - private Object getNestedValue(DBObject dbObject, String nestedKey) - { - String[] keyChain = nestedKey.split("\\."); - - DBObject temp = dbObject; - - for (int i = 0; i < keyChain.length - 1; ++i) - { - temp = (DBObject)temp.get(keyChain[i]); - - if ( temp == null ) - { - return null; - } - } - - return temp.get(keyChain[keyChain.length - 1]); - } - - - /*------------------------------------------------------------ */ - /** - * ClassLoadingObjectInputStream - * - * - */ - 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); - } - } - } - } |