Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Bartel2015-11-19 23:48:50 +0000
committerJan Bartel2015-11-19 23:48:50 +0000
commitb2497895353561ff504973a0059ee700bca6500a (patch)
tree5a855a5989621332a6554a53b6d366ac4375160d /jetty-nosql/src
parent070284643bbc10e59044a3fd6704b5849b36a8fe (diff)
downloadorg.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')
-rw-r--r--jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSession.java224
-rw-r--r--jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionDataStore.java99
-rw-r--r--jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java432
-rw-r--r--jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java574
-rw-r--r--jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionIdManager.java561
-rw-r--r--jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionManager.java655
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 &lt; 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 &lt; 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 &gt; 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 &gt;=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 &gt; 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 &gt;=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 &lt; 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 &lt; 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 &lt;= 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);
- }
- }
- }
-
}

Back to the top