Merge branch 'master' into session-refactor

Conflicts:
	jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSession.java
	jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java
	jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java
	jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
	jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java
	jetty-server/src/main/java/org/eclipse/jetty/server/session/MemSession.java
	jetty-server/src/test/java/org/eclipse/jetty/server/session/FileSessionManagerTest.java
	tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java
	tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerPreserveSessionTest.java
diff --git a/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java
new file mode 100644
index 0000000..57d0907
--- /dev/null
+++ b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionDataStore.java
@@ -0,0 +1,389 @@
+//
+//  ========================================================================
+//  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.gcloud.session;
+
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.server.session.AbstractSessionDataStore;
+import org.eclipse.jetty.server.session.SessionContext;
+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.google.gcloud.datastore.Blob;
+import com.google.gcloud.datastore.Datastore;
+import com.google.gcloud.datastore.DatastoreFactory;
+import com.google.gcloud.datastore.Entity;
+import com.google.gcloud.datastore.Key;
+import com.google.gcloud.datastore.KeyFactory;
+import com.google.gcloud.datastore.ProjectionEntity;
+import com.google.gcloud.datastore.Query;
+import com.google.gcloud.datastore.QueryResults;
+import com.google.gcloud.datastore.StructuredQuery;
+import com.google.gcloud.datastore.StructuredQuery.CompositeFilter;
+import com.google.gcloud.datastore.StructuredQuery.KeyQueryBuilder;
+import com.google.gcloud.datastore.StructuredQuery.Projection;
+import com.google.gcloud.datastore.StructuredQuery.ProjectionEntityQueryBuilder;
+import com.google.gcloud.datastore.StructuredQuery.PropertyFilter;
+
+/**
+ * GCloudSessionDataStore
+ *
+ *
+ */
+public class GCloudSessionDataStore extends AbstractSessionDataStore
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+
+    public  static final String ID = "id";
+    public  static final String CONTEXTPATH = "contextPath";
+    public  static final String VHOST = "vhost";
+    public  static final String ACCESSED = "accessed";
+    public  static final String LASTACCESSED = "lastAccessed";
+    public  static final String CREATETIME = "createTime";
+    public  static final  String COOKIESETTIME = "cookieSetTime";
+    public  static final String LASTNODE = "lastNode";
+    public  static final String EXPIRY = "expiry";
+    public  static final  String MAXINACTIVE = "maxInactive";
+    public  static final  String ATTRIBUTES = "attributes";
+
+    public static final String KIND = "GCloudSession";
+    public static final int DEFAULT_MAX_QUERY_RESULTS = 100;
+
+    private GCloudConfiguration _config;
+    private Datastore _datastore;
+    private KeyFactory _keyFactory;
+    private int _maxResults = DEFAULT_MAX_QUERY_RESULTS;
+    
+
+    
+    
+    
+    
+    
+    
+    
+    
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        
+        if (_config == null)
+            throw new IllegalStateException("No DataStore configuration");
+        
+        _datastore = DatastoreFactory.instance().get(_config.getDatastoreOptions());
+        _keyFactory = _datastore.newKeyFactory().kind(KIND);
+        
+        super.doStart();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+    }
+    
+    
+    /**
+     * @param cfg
+     */
+    public void setGCloudConfiguration (GCloudConfiguration cfg)
+    {
+        _config = cfg;
+    }
+    
+    /**
+     * @return
+     */
+    public GCloudConfiguration getGCloudConfiguration ()
+    {
+        return _config;
+    }
+
+    
+    /**
+     * @return
+     */
+    public int getMaxResults()
+    {
+        return _maxResults;
+    }
+
+
+    /**
+     * @param maxResults
+     */
+    public void setMaxResults(int maxResults)
+    {
+        if (_maxResults <= 0)
+            _maxResults = DEFAULT_MAX_QUERY_RESULTS;
+        else
+            _maxResults = maxResults;
+    }
+    
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#load(java.lang.String)
+     */
+    @Override
+    public SessionData load(String id) throws Exception
+    {
+        if (LOG.isDebugEnabled()) LOG.debug("Loading session {} from DataStore", id);
+
+        Entity entity = _datastore.get(makeKey(id, _context));
+        if (entity == null)
+        {
+            if (LOG.isDebugEnabled()) LOG.debug("No session {} in DataStore ", id);
+            return null;
+        }
+        else
+        {
+            SessionData data = sessionFromEntity(entity);
+            return data;
+        }
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#delete(java.lang.String)
+     */
+    @Override
+    public boolean delete(String id) throws Exception
+    {
+        if (LOG.isDebugEnabled()) LOG.debug("Removing session {} from DataStore", id);
+        _datastore.delete(makeKey(id, _context));
+        return true;
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set)
+     */
+    @Override
+    public Set<String> getExpired(Set<String> candidates)
+    {
+       long now = System.currentTimeMillis();
+       Set<String> expired = new HashSet<String>();
+        
+       //get up to maxResult number of sessions that have expired
+       ProjectionEntityQueryBuilder pbuilder = Query.projectionEntityQueryBuilder();
+       pbuilder.addProjection(Projection.property(ID));
+       pbuilder.filter(CompositeFilter.and(PropertyFilter.gt(EXPIRY, 0), PropertyFilter.le(EXPIRY, now)));
+       pbuilder.limit(_maxResults);
+       pbuilder.kind(KIND);
+       StructuredQuery<ProjectionEntity> pquery = pbuilder.build();
+       QueryResults<ProjectionEntity> presults = _datastore.run(pquery);
+       
+       while (presults.hasNext())
+       {
+           ProjectionEntity pe = presults.next();
+           String id = pe.getString(ID);
+           expired.add(id);
+       }
+      
+       //reconcile against ids that the SessionStore thinks are expired
+       Set<String> tmp = new HashSet<String>(candidates);
+       tmp.removeAll(expired);       
+       if (!tmp.isEmpty())
+       {
+           //sessionstore thinks these are expired, but they are either no
+           //longer in the db or not expired in the db, or we exceeded the
+           //number of records retrieved by the expiry query, so check them
+           //individually
+           for (String s:tmp)
+           {
+               try
+               {
+                   KeyQueryBuilder kbuilder = Query.keyQueryBuilder();
+                   kbuilder.filter(PropertyFilter.eq(ID, s));
+                   kbuilder.kind(KIND);
+                   StructuredQuery<Key> kq = kbuilder.build();
+                   QueryResults<Key> kresults = _datastore.run(kq);
+                   if (!kresults.hasNext())
+                       expired.add(s); //not in db, can be expired
+               }
+               catch (Exception e)
+               {
+                   LOG.warn(e);
+               }
+           }
+       }
+       
+       return expired;
+        
+    }
+
+ 
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(java.lang.String, org.eclipse.jetty.server.session.SessionData, boolean)
+     */
+    @Override
+    public void doStore(String id, SessionData data, boolean isNew) throws Exception
+    {
+        if (LOG.isDebugEnabled()) LOG.debug("Writing session {} to DataStore", data.getId());
+    
+        Entity entity = entityFromSession(data, makeKey(id, _context));
+        _datastore.put(entity);
+    }
+
+    /**
+     * Make a unique key for this session.
+     * As the same session id can be used across multiple contexts, to
+     * make it unique, the key must be composed of:
+     * <ol>
+     * <li>the id</li>
+     * <li>the context path</li>
+     * <li>the virtual hosts</li>
+     * </ol>
+     * 
+     *
+     * @param session
+     * @return
+     */
+    private Key makeKey (String id, SessionContext context)
+    {
+        String key = context.getCanonicalContextPath()+"_"+context.getVhost()+"_"+id;
+        return _keyFactory.newKey(key);
+    }
+    
+    
+    /**
+     * Generate a gcloud datastore Entity from SessionData
+     * @param session
+     * @param key
+     * @return
+     * @throws Exception
+     */
+    private Entity entityFromSession (SessionData session, Key key) throws Exception
+    {
+        if (session == null)
+            return null;
+        
+        Entity entity = null;
+        
+        //serialize the attribute map
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(session.getAllAttributes());
+        oos.flush();
+        
+        //turn a session into an entity
+        entity = Entity.builder(key)
+                .set(ID, session.getId())
+                .set(CONTEXTPATH, session.getContextPath())
+                .set(VHOST, session.getVhost())
+                .set(ACCESSED, session.getAccessed())
+                .set(LASTACCESSED, session.getLastAccessed())
+                .set(CREATETIME, session.getCreated())
+                .set(COOKIESETTIME, session.getCookieSet())
+                .set(LASTNODE,session.getLastNode())
+                .set(EXPIRY, session.getExpiry())
+                .set(MAXINACTIVE, session.getMaxInactiveMs())
+                .set(ATTRIBUTES, Blob.copyFrom(baos.toByteArray())).build();
+                 
+        return entity;
+    }
+    
+    /**
+     * Generate SessionData from an Entity retrieved from gcloud datastore.
+     * @param entity
+     * @return
+     * @throws Exception
+     */
+    private SessionData sessionFromEntity (Entity entity) throws Exception
+    {
+        if (entity == null)
+            return null;
+
+        final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
+        final AtomicReference<Exception> exception = new AtomicReference<Exception>();
+        Runnable load = new Runnable()
+        {
+            public void run ()
+            {
+                try
+                {
+                    //turn an Entity into a Session
+                    String id = entity.getString(ID);
+                    String contextPath = entity.getString(CONTEXTPATH);
+                    String vhost = entity.getString(VHOST);
+                    long accessed = entity.getLong(ACCESSED);
+                    long lastAccessed = entity.getLong(LASTACCESSED);
+                    long createTime = entity.getLong(CREATETIME);
+                    long cookieSet = entity.getLong(COOKIESETTIME);
+                    String lastNode = entity.getString(LASTNODE);
+                    long expiry = entity.getLong(EXPIRY);
+                    long maxInactive = entity.getLong(MAXINACTIVE);
+                    Blob blob = (Blob) entity.getBlob(ATTRIBUTES);
+
+                    SessionData session = newSessionData (id, createTime, accessed, lastAccessed, maxInactive);
+                    session.setLastNode(lastNode);
+                    session.setContextPath(contextPath);
+                    session.setVhost(vhost);
+                    session.setCookieSet(cookieSet);
+                    session.setLastNode(lastNode);
+                    session.setExpiry(expiry);
+                    try (ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(blob.asInputStream()))
+                    {
+                        Object o = ois.readObject();
+                        session.putAllAttributes((Map<String,Object>)o);
+                    }
+                    reference.set(session);
+                }
+                catch (Exception e)
+                {
+                    exception.set(e);
+                }
+            }
+        };
+        
+        //ensure this runs in the context classloader
+       _context.run(load);
+    
+        if (exception.get() != null)
+            throw exception.get();
+        
+        return reference.get();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
+     */
+    @Override
+    public boolean isPassivating()
+    {
+       return true;
+    }
+    
+    
+}
diff --git a/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java
index 7c2a81e..2f8da7a 100644
--- a/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java
+++ b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionIdManager.java
@@ -20,16 +20,9 @@
 
 import java.util.Random;
 
-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.AbstractSession;
 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.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
@@ -52,7 +45,6 @@
     private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
     public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2;
     public static final String KIND = "GCloudSessionId";
-    private Server _server;
     private Datastore _datastore;
     private KeyFactory _keyFactory;
     private GCloudConfiguration _config;
@@ -65,8 +57,7 @@
      */
     public GCloudSessionIdManager(Server server)
     {
-        super();
-        _server = server;
+        super(server);
     }
 
     /**
@@ -75,8 +66,7 @@
      */
     public GCloudSessionIdManager(Server server, Random random)
     {
-       super(random);
-       _server = server;
+       super(server,random);
     }
 
 
@@ -111,56 +101,7 @@
     }
 
     
-   
-
-    
-    /** 
-     * Check to see if the given session id is being
-     * used by a session in any context.
-     * 
-     * This method will consult the cluster.
-     * 
-     * @see org.eclipse.jetty.server.SessionIdManager#idInUse(java.lang.String)
-     */
-    @Override
-    public boolean idInUse(String id)
-    {
-        if (id == null)
-            return false;
-        
-        String clusterId = getClusterId(id);
-        
-        //ask the cluster - this should also tickle the idle expiration timer on the sessionid entry
-        //keeping it valid
-        try
-        {
-            return exists(clusterId);
-        }
-        catch (Exception e)
-        {
-            LOG.warn("Problem checking inUse for id="+clusterId, e);
-            return false;
-        }
-        
-    }
-
-    /** 
-     * Remember a new in-use session id.
-     * 
-     * This will save the in-use session id to the cluster.
-     * 
-     * @see org.eclipse.jetty.server.SessionIdManager#addSession(javax.servlet.http.HttpSession)
-     */
-    @Override
-    public void addSession(HttpSession session)
-    {
-        if (session == null)
-            return;
-
-        //insert into the store
-        insert (((AbstractSession)session).getClusterId());
-    }
-
+ 
   
 
     
@@ -174,91 +115,8 @@
         _config = config;
     }
 
-    
-    
-    /** 
-     * Remove a session id from the list of in-use ids.
-     * 
-     * This will remvove the corresponding session id from the cluster.
-     * 
-     * @see org.eclipse.jetty.server.SessionIdManager#removeSession(javax.servlet.http.HttpSession)
-     */
-    @Override
-    public void removeSession(HttpSession session)
-    {
-        if (session == null)
-            return;
+   
 
-        //delete from the cache
-        delete (((AbstractSession)session).getClusterId());
-    }
-
-    /** 
-     * Remove a session id. This compels all other contexts who have a session
-     * with the same id to also remove it.
-     * 
-     * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
-     */
-    @Override
-    public void invalidateAll(String id)
-    {
-        //delete the session id from list of in-use sessions
-        delete (id);
-
-
-        //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 GCloudSessionManager)
-                {
-                    ((GCloudSessionManager)manager).invalidateSession(id);
-                }
-            }
-        }
-
-    }
-
-    /** 
-     * Change a session id. 
-     * 
-     * Typically this occurs when a previously existing session has passed through authentication.
-     * 
-     * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#renewSessionId(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest)
-     */
-    @Override
-    public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request)
-    {
-        //generate a new id
-        String newClusterId = newSessionId(request.hashCode());
-
-        delete(oldClusterId);
-        insert(newClusterId);
-
-
-        //tell all contexts to update the id 
-        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 GCloudSessionManager)
-                {
-                    ((GCloudSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
-                }
-            }
-        }
-
-    }
 
     
     
@@ -300,12 +158,13 @@
      * 
      * @param id
      */
-    protected void delete (String id)
+    protected boolean delete (String id)
     {
         if (_datastore == null)
             throw new IllegalStateException ("No DataStore");
         
         _datastore.delete(makeKey(id));
+        return true; //gcloud does not distinguish between first and subsequent removes
     }
     
     
@@ -320,4 +179,53 @@
     {
         return _keyFactory.newKey(id);
     }
+
+    /** 
+     * @see org.eclipse.jetty.server.SessionIdManager#isIdInUse(java.lang.String)
+     */
+    @Override
+    public boolean isIdInUse(String id)
+    {
+        if (id == null)
+            return false;
+        
+        
+        //ask the cluster - this should also tickle the idle expiration timer on the sessionid entry
+        //keeping it valid
+        try
+        {
+            return exists(id);
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Problem checking inUse for id="+id, e);
+            return false;
+        }
+        
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.SessionIdManager#useId(org.eclipse.jetty.server.session.Session)
+     */
+    @Override
+    public void useId(Session session)
+    {
+        if (session == null)
+            return;
+
+        //insert into the store
+        insert (session.getId());
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.SessionIdManager#removeId(java.lang.String)
+     */
+    @Override
+    public boolean removeId(String id)
+    {
+       if (id == null)
+           return false;
+       
+       return delete(id);
+    }
 }
diff --git a/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java
index c38b2b8..dd50b94 100644
--- a/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java
+++ b/jetty-gcloud/gcloud-session-manager/src/main/java/org/eclipse/jetty/gcloud/session/GCloudSessionManager.java
@@ -18,43 +18,16 @@
 
 package org.eclipse.jetty.gcloud.session;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.ReentrantLock;
 
 import javax.servlet.http.HttpServletRequest;
 
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.server.handler.ContextHandler.Context;
-import org.eclipse.jetty.server.session.AbstractSession;
-import org.eclipse.jetty.server.session.AbstractSessionManager;
-import org.eclipse.jetty.server.session.MemSession;
-import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
+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.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.google.gcloud.datastore.Blob;
-import com.google.gcloud.datastore.Datastore;
-import com.google.gcloud.datastore.DatastoreFactory;
-import com.google.gcloud.datastore.Entity;
-import com.google.gcloud.datastore.GqlQuery;
-import com.google.gcloud.datastore.Key;
-import com.google.gcloud.datastore.KeyFactory;
-import com.google.gcloud.datastore.Query;
-import com.google.gcloud.datastore.Query.ResultType;
-import com.google.gcloud.datastore.QueryResults;
 
 
 
@@ -63,360 +36,50 @@
  * 
  * 
  */
-public class GCloudSessionManager extends AbstractSessionManager
+public class GCloudSessionManager extends SessionManager
 {
     private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
-    
-    
-    public static final String KIND = "GCloudSession";
-    public static final int DEFAULT_MAX_QUERY_RESULTS = 100;
-    public static final long DEFAULT_SCAVENGE_SEC = 600; 
-    
-    /**
-     * Sessions known to this node held in memory
-     */
-    private ConcurrentHashMap<String, GCloudSessionManager.Session> _sessions;
+
+  
+ 
+
+
+    private GCloudSessionDataStore _sessionDataStore = null;
+
+
 
     
-    /**
-     * The length of time a session can be in memory without being checked against
-     * the cluster. A value of 0 indicates that the session is never checked against
-     * the cluster - the current node is considered to be the master for the session.
-     *
-     */
-    private long _staleIntervalSec = 0;
+/*
     
-    protected Scheduler.Task _task; //scavenge task
-    protected Scheduler _scheduler;
-    protected Scavenger _scavenger;
-    protected long _scavengeIntervalMs = 1000L * DEFAULT_SCAVENGE_SEC; //10mins
-    protected boolean _ownScheduler;
-    
-    private Datastore _datastore;
-    private KeyFactory _keyFactory;
-
-
-    private SessionEntityConverter _converter;
-
-
-    private int _maxResults = DEFAULT_MAX_QUERY_RESULTS;
-
-
-    /**
-     * Scavenger
-     *
-     */
-    protected class Scavenger implements Runnable
-    {
-
-        @Override
-        public void run()
-        {
-           try
-           {
-               scavenge();
-           }
-           finally
-           {
-               if (_scheduler != null && _scheduler.isRunning())
-                   _task = _scheduler.schedule(this, _scavengeIntervalMs, TimeUnit.MILLISECONDS);
-           }
-        }
-    }
-
-    /**
-     * SessionEntityConverter
-     *
-     *
-     */
-    public class SessionEntityConverter
-    {
-        public  final String CLUSTERID = "clusterId";
-        public  final String CONTEXTPATH = "contextPath";
-        public  final String VHOST = "vhost";
-        public  final String ACCESSED = "accessed";
-        public  final String LASTACCESSED = "lastAccessed";
-        public  final String CREATETIME = "createTime";
-        public  final  String COOKIESETTIME = "cookieSetTime";
-        public  final String LASTNODE = "lastNode";
-        public  final String EXPIRY = "expiry";
-        public  final  String MAXINACTIVE = "maxInactive";
-        public  final  String ATTRIBUTES = "attributes";
-
-      
-        
-        public Entity entityFromSession (Session session, Key key) throws Exception
-        {
-            if (session == null)
-                return null;
-            
-            Entity entity = null;
-            
-            //serialize the attribute map
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            ObjectOutputStream oos = new ObjectOutputStream(baos);
-            oos.writeObject(session.getAttributeMap());
-            oos.flush();
-            
-            //turn a session into an entity
-            entity = Entity.builder(key)
-                    .set(CLUSTERID, session.getId())
-                    .set(CONTEXTPATH, session.getContextPath())
-                    .set(VHOST, session.getVHost())
-                    .set(ACCESSED, session.getAccessed())
-                    .set(LASTACCESSED, session.getLastAccessedTime())
-                    .set(CREATETIME, session.getCreationTime())
-                    .set(COOKIESETTIME, session.getCookieSetTime())
-                    .set(LASTNODE,session.getLastNode())
-                    .set(EXPIRY, session.getExpiry())
-                    .set(MAXINACTIVE, session.getMaxInactiveInterval())
-                    .set(ATTRIBUTES, Blob.copyFrom(baos.toByteArray())).build();
-                     
-            return entity;
-        }
-        
-        public Session sessionFromEntity (Entity entity) throws Exception
-        {
-            if (entity == null)
-                return null;
-
-            final AtomicReference<Session> reference = new AtomicReference<Session>();
-            final AtomicReference<Exception> exception = new AtomicReference<Exception>();
-            Runnable load = new Runnable()
-            {
-                public void run ()
-                {
-                    try
-                    {
-                        //turn an entity into a Session
-                        String clusterId = entity.getString(CLUSTERID);
-                        String contextPath = entity.getString(CONTEXTPATH);
-                        String vhost = entity.getString(VHOST);
-                        long accessed = entity.getLong(ACCESSED);
-                        long lastAccessed = entity.getLong(LASTACCESSED);
-                        long createTime = entity.getLong(CREATETIME);
-                        long cookieSetTime = entity.getLong(COOKIESETTIME);
-                        String lastNode = entity.getString(LASTNODE);
-                        long expiry = entity.getLong(EXPIRY);
-                        long maxInactive = entity.getLong(MAXINACTIVE);
-                        Blob blob = (Blob) entity.getBlob(ATTRIBUTES);
-
-                        Session session = new Session (clusterId, createTime, accessed, maxInactive);
-                        session.setLastNode(lastNode);
-                        session.setContextPath(contextPath);
-                        session.setVHost(vhost);
-                        session.setCookieSetTime(cookieSetTime);
-                        session.setLastAccessedTime(lastAccessed);
-                        session.setLastNode(lastNode);
-                        session.setExpiry(expiry);
-                        try (ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(blob.asInputStream()))
-                        {
-                            Object o = ois.readObject();
-                            session.addAttributes((Map<String,Object>)o);
-                        }
-                        reference.set(session);
-                    }
-                    catch (Exception e)
-                    {
-                        exception.set(e);
-                    }
-                }
-            };
-            
-            if (_context==null)
-                load.run();
-            else
-                _context.getContextHandler().handle(null,load);
-   
-           
-            if (exception.get() != null)
-            {
-                exception.get().printStackTrace();
-                throw exception.get();
-            }
-            
-            return reference.get();
-        }
-    }
-    
-    /*
-     * Every time a Session is put into the cache one of these objects
-     * is created to copy the data out of the in-memory session, and 
-     * every time an object is read from the cache one of these objects
-     * a fresh Session object is created based on the data held by this
-     * object.
-     */
-    public class SerializableSessionData implements Serializable
-    {
-        /**
-         * 
-         */
-        private static final long serialVersionUID = -7779120106058533486L;
-        String clusterId;
-        String contextPath;
-        String vhost;
-        long accessed;
-        long lastAccessed;
-        long createTime;
-        long cookieSetTime;
-        String lastNode;
-        long expiry;
-        long maxInactive;
-        Map<String, Object> attributes;
-
-        public SerializableSessionData()
-        {
-
-        }
-
-       
-       public SerializableSessionData(Session s)
-       {
-           clusterId = s.getClusterId();
-           contextPath = s.getContextPath();
-           vhost = s.getVHost();
-           accessed = s.getAccessed();
-           lastAccessed = s.getLastAccessedTime();
-           createTime = s.getCreationTime();
-           cookieSetTime = s.getCookieSetTime();
-           lastNode = s.getLastNode();
-           expiry = s.getExpiry();
-           maxInactive = s.getMaxInactiveInterval();
-           attributes = s.getAttributeMap(); // TODO pointer, not a copy
-       }
-        
-        private void writeObject(java.io.ObjectOutputStream out) throws IOException
-        {  
-            out.writeUTF(clusterId); //session id
-            out.writeUTF(contextPath); //context path
-            out.writeUTF(vhost); //first vhost
-
-            out.writeLong(accessed);//accessTime
-            out.writeLong(lastAccessed); //lastAccessTime
-            out.writeLong(createTime); //time created
-            out.writeLong(cookieSetTime);//time cookie was set
-            out.writeUTF(lastNode); //name of last node managing
-      
-            out.writeLong(expiry); 
-            out.writeLong(maxInactive);
-            out.writeObject(attributes);
-        }
-        
-        private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
-        {
-            clusterId = in.readUTF();
-            contextPath = in.readUTF();
-            vhost = in.readUTF();
-            
-            accessed = in.readLong();//accessTime
-            lastAccessed = in.readLong(); //lastAccessTime
-            createTime = in.readLong(); //time created
-            cookieSetTime = in.readLong();//time cookie was set
-            lastNode = in.readUTF(); //last managing node
-            expiry = in.readLong(); 
-            maxInactive = in.readLong();
-            attributes = (HashMap<String,Object>)in.readObject();
-        }
-        
-    }
-    
-
-    
-    /**
+    *//**
      * Session
      *
      * Representation of a session in local memory.
-     */
+     *//*
     public class Session extends MemSession
     {
         
         private ReentrantLock _lock = new ReentrantLock();
         
-        /**
-         * The (canonical) context path for with which this session is associated
-         */
-        private String _contextPath;
-        
-        
-        
-        /**
-         * The time in msec since the epoch at which this session should expire
-         */
-        private long _expiryTime; 
-        
-        
-        /**
-         * Time in msec since the epoch at which this session was last read from cluster
-         */
-        private long _lastSyncTime;
-        
-        
-        /**
-         * The workername of last node known to be managing the session
-         */
-        private String _lastNode;
-        
-        
-        /**
-         * If dirty, session needs to be (re)sent to cluster
-         */
-        protected boolean _dirty=false;
-        
-        
      
+        private long _lastSyncTime;
 
-        /**
-         * Any virtual hosts for the context with which this session is associated
-         */
-        private String _vhost;
-
-        
-        /**
-         * Count of how many threads are active in this session
-         */
         private AtomicInteger _activeThreads = new AtomicInteger(0);
         
         
-        
-        
-        /**
-         * A new session.
-         * 
-         * @param request
-         */
+       
         protected Session (HttpServletRequest request)
         {
-            super(GCloudSessionManager.this,request);
-            long maxInterval = getMaxInactiveInterval();
-            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
-            _lastNode = getSessionIdManager().getWorkerName();
-           setVHost(GCloudSessionManager.getVirtualHost(_context));
-           setContextPath(GCloudSessionManager.getContextPath(_context));
            _activeThreads.incrementAndGet(); //access will not be called on a freshly created session so increment here
         }
         
         
     
-        
-        /**
-         * A restored session.
-         * 
-         * @param sessionId
-         * @param created
-         * @param accessed
-         * @param maxInterval
-         */
-        protected Session (String sessionId, long created, long accessed, long maxInterval)
-        {
-            super(GCloudSessionManager.this, created, accessed, sessionId);
-            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
-        }
-        
-        /** 
+        *//** 
          * Called on entry to the session.
          * 
          * @see org.eclipse.jetty.server.session.AbstractSession#access(long)
-         */
+         *//*
         @Override
         protected boolean access(long time)
         {
@@ -459,10 +122,10 @@
         }
 
 
-        /**
+        *//**
          * Exit from session
          * @see org.eclipse.jetty.server.session.AbstractSession#complete()
-         */
+         *//*
         @Override
         protected void complete()
         {
@@ -511,46 +174,24 @@
             }
         }
         
-        /** Test if the session is stale
+        *//** Test if the session is stale
          * @param atTime
          * @return
-         */
+         *//*
         protected boolean isStale (long atTime)
         {
             return (getStaleIntervalSec() > 0) && (atTime - getLastSyncTime() >= (getStaleIntervalSec()*1000L));
         }
-        
-        
-        /** Test if the session is dirty
-         * @return
-         */
-        protected boolean isDirty ()
-        {
-            return _dirty;
-        }
-
-        /** 
-         * Expire the session.
-         * 
-         * @see org.eclipse.jetty.server.session.AbstractSession#timeout()
-         */
-        @Override
-        protected void timeout()
-        {
-            if (LOG.isDebugEnabled()) LOG.debug("Timing out session {}", getId());
-            super.timeout();
-        }
-        
       
         
-        /**
+        *//**
          * Reload the session from the cluster. If the node that
          * last managed the session from the cluster is ourself,
          * then the session does not need refreshing.
          * NOTE: this method MUST be called with sufficient locks
          * in place to prevent 2 or more concurrent threads from
          * simultaneously updating the session.
-         */
+         *//*
         private void refresh () 
         throws Exception
         {
@@ -612,25 +253,6 @@
         }
 
 
-        public void setExpiry (long expiry)
-        {
-            _expiryTime = expiry;
-        }
-        
-
-        public long getExpiry ()
-        {
-            return _expiryTime;
-        }
-        
-        public boolean isExpiredAt (long time)
-        {
-            if (_expiryTime <= 0)
-                return false; //never expires
-            
-            return  (_expiryTime <= time);
-        }
-        
         public void swapId (String newId, String newNodeId)
         {
             //TODO probably synchronize rather than use the access/complete lock?
@@ -639,69 +261,37 @@
             setNodeId(newNodeId);
             _lock.unlock();
         }
-        
-        @Override
-        public void setAttribute (String name, Object value)
-        {
-            Object old = changeAttribute(name, value);
-            if (value == null && old == null)
-                return; //if same as remove attribute but attribute was already removed, no change
-            
-           _dirty = true;
-        }
-        
-        
-        public String getContextPath()
-        {
-            return _contextPath;
-        }
 
 
-        public void setContextPath(String contextPath)
-        {
-            this._contextPath = contextPath;
-        }
+    }
 
+*/
 
-        public String getVHost()
-        {
-            return _vhost;
-        }
+    
+    /**
+     * 
+     */
+    public GCloudSessionManager()
+    {
+        _sessionDataStore = new GCloudSessionDataStore();
+        _sessionStore = new MemorySessionStore();
+    }
 
+    
+    
 
-        public void setVHost(String vhost)
-        {
-            this._vhost = vhost;
-        }
-        
-        public String getLastNode()
-        {
-            return _lastNode;
-        }
-
-
-        public void setLastNode(String lastNode)
-        {
-            _lastNode = lastNode;
-        }
-
-
-        public long getLastSyncTime()
-        {
-            return _lastSyncTime;
-        }
-
-
-        public void setLastSyncTime(long lastSyncTime)
-        {
-            _lastSyncTime = lastSyncTime;
-        }
-
+    /**
+     * @return
+     */
+    public GCloudSessionDataStore getSessionDataStore()
+    {
+        return _sessionDataStore;
     }
 
 
 
-    
+
+
     /**
      * Start the session manager.
      *
@@ -710,32 +300,7 @@
     @Override
     public void doStart() throws Exception
     {
-        if (_sessionIdManager == null)
-            throw new IllegalStateException("No session id manager defined");
-        
-        GCloudConfiguration config = ((GCloudSessionIdManager)_sessionIdManager).getConfig();
-        if (config == null)
-            throw new IllegalStateException("No gcloud configuration");
-        
-        
-        _datastore = DatastoreFactory.instance().get(config.getDatastoreOptions());
-        _keyFactory = _datastore.newKeyFactory().kind(KIND);
-        _converter = new SessionEntityConverter();       
-        _sessions = new ConcurrentHashMap<String, Session>();
-
-        //try and use a common scheduler, fallback to own
-        _scheduler = getSessionHandler().getServer().getBean(Scheduler.class);
-        if (_scheduler == null)
-        {
-            _scheduler = new ScheduledExecutorScheduler();
-            _ownScheduler = true;
-            _scheduler.start();
-        }
-        else if (!_scheduler.isStarted())
-            throw new IllegalStateException("Shared scheduler not started");
- 
-        setScavengeIntervalSec(getScavengeIntervalSec());
-        
+        ((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore);
         super.doStart();
     }
 
@@ -749,552 +314,16 @@
     public void doStop() throws Exception
     {
         super.doStop();
-
-        if (_task!=null)
-            _task.cancel();
-        _task=null;
-        if (_ownScheduler && _scheduler !=null)
-            _scheduler.stop();
-        _scheduler = null;
-
-        _sessions.clear();
-        _sessions = null;
     }
 
 
 
-    /**
-     * Look for sessions in local memory that have expired.
-     */
-    public void scavenge ()
-    {
-        try
-        {
-            //scavenge in the database every so often
-            scavengeGCloudDataStore();
-        }
-        catch (Exception e)
-        {
-            LOG.warn("Problem scavenging", e);
-        }
-    }
-
  
     
     protected void scavengeGCloudDataStore()
     throws Exception
     {
        
-        //query the datastore for sessions that have expired
-        long now = System.currentTimeMillis();
-        
-        //give a bit of leeway so we don't immediately something that has only just expired a nanosecond ago
-        now = now - (_scavengeIntervalMs/2);
-        
-        if (LOG.isDebugEnabled())
-            LOG.debug("Scavenging for sessions expired before "+now);
-
-
-        GqlQuery.Builder builder = Query.gqlQueryBuilder(ResultType.ENTITY, "select * from "+KIND+" where expiry < @1 limit "+_maxResults);
-        builder.allowLiteral(true);
-        builder.addBinding(now);
-        Query<Entity> query = builder.build();
-        QueryResults<Entity> results = _datastore.run(query);
-        
-        while (results.hasNext())
-        {          
-            Entity sessionEntity = results.next();
-            scavengeSession(sessionEntity);        
-        }
-
+     
     }
-
-    /**
-     * Scavenge a session that has expired
-     * @param e
-     * @throws Exception
-     */
-    protected void scavengeSession (Entity e)
-            throws Exception
-    {
-        long now = System.currentTimeMillis();
-        Session session = _converter.sessionFromEntity(e);
-        if (session == null)
-            return;
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("Scavenging session: {}",session.getId());
-        //if the session isn't in memory already, put it there so we can do a normal timeout call
-         Session memSession =  _sessions.putIfAbsent(session.getId(), session);
-         if (memSession == null)
-         {
-             memSession = session;
-             _sessionsStats.increment();
-         }
-
-        //final check
-        if (memSession.isExpiredAt(now))
-        {
-            if (LOG.isDebugEnabled()) LOG.debug("Session {} is definitely expired", memSession.getId());
-            memSession.timeout();   
-        }
-    }
-
-    public long getScavengeIntervalSec ()
-    {
-        return _scavengeIntervalMs/1000;
-    }
-
-    
-    
-    /**
-     * Set the interval between runs of the scavenger. It should not be run too
-     * often.
-     * 
-     * 
-     * @param sec
-     */
-    public void setScavengeIntervalSec (long sec)
-    {
-
-        long old_period=_scavengeIntervalMs;
-        long period=sec*1000L;
-
-        _scavengeIntervalMs=period;
-
-        if (_scavengeIntervalMs > 0)
-        {
-            //add a bit of variability into the scavenge time so that not all
-            //nodes with the same scavenge time sync up
-            long tenPercent = _scavengeIntervalMs/10;
-            if ((System.currentTimeMillis()%2) == 0)
-                _scavengeIntervalMs += tenPercent;
-            if (LOG.isDebugEnabled())
-                LOG.debug("Scavenging every "+_scavengeIntervalMs+" ms");
-        }
-        else
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Scavenging disabled"); 
-        }
-
- 
-        
-        synchronized (this)
-        {
-            if (_scheduler != null && (period!=old_period || _task==null))
-            {
-                //clean up any previously scheduled scavenger
-                if (_task!=null)
-                    _task.cancel();
-
-                //start a new one
-                if (_scavengeIntervalMs > 0)
-                {
-                    if (_scavenger == null)
-                        _scavenger = new Scavenger();
-
-                    _task = _scheduler.schedule(_scavenger,_scavengeIntervalMs,TimeUnit.MILLISECONDS);
-                }
-            }
-        }
-    }
-    
-    
-    public long getStaleIntervalSec()
-    {
-        return _staleIntervalSec;
-    }
-
-
-    public void setStaleIntervalSec(long staleIntervalSec)
-    {
-        _staleIntervalSec = staleIntervalSec;
-    }
-    
-    
-    public int getMaxResults()
-    {
-        return _maxResults;
-    }
-
-
-    public void setMaxResults(int maxResults)
-    {
-        if (_maxResults <= 0)
-            _maxResults = DEFAULT_MAX_QUERY_RESULTS;
-        else
-            _maxResults = maxResults;
-    }
-
-
-    /** 
-     * Add a new session for the context related to this session manager
-     * 
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
-     */
-    @Override
-    protected void addSession(AbstractSession session)
-    {
-        if (session==null)
-            return;
-        
-        if (LOG.isDebugEnabled()) LOG.debug("Adding session({}) to session manager for context {} on worker {}",session.getClusterId(), getContextPath(getContext()),getSessionIdManager().getWorkerName() + " with lastnode="+((Session)session).getLastNode());
-        _sessions.put(session.getClusterId(), (Session)session);
-        
-        try
-        {     
-                session.willPassivate();
-                save(((GCloudSessionManager.Session)session));
-                session.didActivate();
-            
-        }
-        catch (Exception e)
-        {
-            LOG.warn("Unable to store new session id="+session.getId() , e);
-        }
-    }
-
-    /** 
-     * Ask the cluster for the session.
-     * 
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
-     */
-    @Override
-    public AbstractSession getSession(String idInCluster)
-    {
-        Session session = null;
-
-        //try and find the session in this node's memory
-        Session memSession = (Session)_sessions.get(idInCluster);
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("getSession({}) {} in session map",idInCluster,(memSession==null?"not":""));
-
-        long now = System.currentTimeMillis();
-        try
-        {
-            //if the session is not in this node's memory, then load it from the datastore
-            if (memSession == null)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("getSession({}): loading session data from cluster", idInCluster);
-
-                session = load(makeKey(idInCluster, _context));
-                if (session != null)
-                {
-                    //Check that it wasn't expired
-                    if (session.getExpiry() > 0 && session.getExpiry() <= now)
-                    {
-                        if (LOG.isDebugEnabled()) LOG.debug("getSession ({}): Session expired", idInCluster);
-                        //ensure that the session id for the expired session is deleted so that a new session with the 
-                        //same id cannot be created (because the idInUse() test would succeed)
-                        ((GCloudSessionIdManager)getSessionIdManager()).removeSession(session);
-                        return null;  
-                    }
-
-                    //Update the last worker node to me
-                    session.setLastNode(getSessionIdManager().getWorkerName());                            
-                    //TODO consider saving session here if lastNode was not this node
-
-                    //Check that another thread hasn't loaded the same session
-                    Session existingSession = _sessions.putIfAbsent(idInCluster, session);
-                    if (existingSession != null)
-                    {
-                        //use the one that the other thread inserted
-                        session = existingSession;
-                        LOG.debug("getSession({}): using session loaded by another request thread ", idInCluster);
-                    }
-                    else
-                    {
-                        //indicate that the session was reinflated
-                        session.didActivate();
-                        _sessionsStats.increment();
-                        LOG.debug("getSession({}): loaded session from cluster", idInCluster);
-                    }
-                    return session;
-                }
-                else
-                {
-                    //The requested session does not exist anywhere in the cluster
-                    LOG.debug("getSession({}): No session in cluster matching",idInCluster);
-                    return null;
-                }
-            }
-            else
-            {
-               //The session exists in this node's memory
-               LOG.debug("getSession({}): returning session from local memory ", memSession.getClusterId());
-                return memSession;
-            }
-        }
-        catch (Exception e)
-        {
-            LOG.warn("Unable to load session="+idInCluster, e);
-            return null;
-        }
-    }
-    
-    
-
-    /** 
-     * The session manager is stopping.
-     * 
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#shutdownSessions()
-     */
-    @Override
-    protected void shutdownSessions() throws Exception
-    {
-        Set<String> keys = new HashSet<String>(_sessions.keySet());
-        for (String key:keys)
-        {
-            Session session = _sessions.remove(key); //take the session out of the session list
-            //If the session is dirty, then write it to the cluster.
-            //If the session is simply stale do NOT write it to the cluster, as some other node
-            //may have started managing that session - this means that the last accessed/expiry time
-            //will not be updated, meaning it may look like it can expire sooner than it should.
-            try
-            {
-                if (session.isDirty())
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Saving dirty session {} before exiting ", session.getId());
-                    save(session);
-                }
-            }
-            catch (Exception e)
-            {
-                LOG.warn(e);
-            }
-        }
-    }
-
-
-    @Override
-    protected AbstractSession newSession(HttpServletRequest request)
-    {
-        return new Session(request);
-    }
-
-    /** 
-     * Remove a session from local memory, and delete it from
-     * the cluster cache.
-     * 
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
-     */
-    @Override
-    protected boolean removeSession(String idInCluster)
-    {
-        Session session = (Session)_sessions.remove(idInCluster);
-        try
-        {
-            if (session != null)
-            {
-                delete(session);
-            }
-        }
-        catch (Exception e)
-        {
-            LOG.warn("Problem deleting session id="+idInCluster, e);
-        }
-        return session!=null;
-    }
-    
-    
-    
-    
-    @Override
-    public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
-    {
-        Session session = null;
-        try
-        {
-            //take the session with that id out of our managed list
-            session = (Session)_sessions.remove(oldClusterId);
-            if (session != null)
-            {
-                //TODO consider transactionality and ramifications if the session is live on another node
-                delete(session); //delete the old session from the cluster  
-                session.swapId(newClusterId, newNodeId); //update the session
-                _sessions.put(newClusterId, session); //put it into managed list under new key
-                save(session); //put the session under the new id into the cluster
-            }
-        }
-        catch (Exception e)
-        {
-            LOG.warn(e);
-        }
-
-        super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
-    }
-
-
-    /**
-     * Load a session from the clustered cache.
-     * 
-     * @param key
-     * @return
-     */
-    protected Session load (Key key)
-    throws Exception
-    {
-        if (_datastore == null)
-            throw new IllegalStateException("No DataStore");
-        
-        if (LOG.isDebugEnabled()) LOG.debug("Loading session {} from DataStore", key);
-
-        Entity entity = _datastore.get(key);
-        if (entity == null)
-        {
-            if (LOG.isDebugEnabled()) LOG.debug("No session {} in DataStore ",key);
-            return null;
-        }
-        else
-        {
-            Session session = _converter.sessionFromEntity(entity);
-            session.setLastSyncTime(System.currentTimeMillis());
-            return session;
-        }
-    }
-    
-    
-    
-    /**
-     * Save or update the session to the cluster cache
-     * 
-     * @param session
-     * @throws Exception
-     */
-    protected void save (GCloudSessionManager.Session session)
-    throws Exception
-    {
-        if (_datastore == null)
-            throw new IllegalStateException("No DataStore");
-        
-        if (LOG.isDebugEnabled()) LOG.debug("Writing session {} to DataStore", session.getId());
-    
-        Entity entity = _converter.entityFromSession(session, makeKey(session, _context));
-        _datastore.put(entity);
-        session.setLastSyncTime(System.currentTimeMillis());
-    }
-    
-    
-    
-    /**
-     * Remove the session from the cluster cache.
-     * 
-     * @param session
-     */
-    protected void delete (GCloudSessionManager.Session session)
-    {  
-        if (_datastore == null)
-            throw new IllegalStateException("No DataStore");
-        if (LOG.isDebugEnabled()) LOG.debug("Removing session {} from DataStore", session.getId());
-        _datastore.delete(makeKey(session, _context));
-    }
-
-    
-    /**
-     * Invalidate a session for this context with the given id
-     * 
-     * @param idInCluster
-     */
-    public void invalidateSession (String idInCluster)
-    {
-        Session session = (Session)_sessions.get(idInCluster);
-
-        if (session != null)
-        {
-            session.invalidate();
-        }
-    }
-
-    
-    /**
-     * Make a unique key for this session.
-     * As the same session id can be used across multiple contexts, to
-     * make it unique, the key must be composed of:
-     * <ol>
-     * <li>the id</li>
-     * <li>the context path</li>
-     * <li>the virtual hosts</li>
-     * </ol>
-     * 
-     *TODO consider the difference between getClusterId and getId
-     * @param session
-     * @return
-     */
-    private Key makeKey (Session session, Context context)
-    {
-       return makeKey(session.getId(), context);
-    }
-    
-    /**
-     * Make a unique key for this session.
-     * As the same session id can be used across multiple contexts, to
-     * make it unique, the key must be composed of:
-     * <ol>
-     * <li>the id</li>
-     * <li>the context path</li>
-     * <li>the virtual hosts</li>
-     * </ol>
-     * 
-     *TODO consider the difference between getClusterId and getId
-     * @param session
-     * @return
-     */
-    private Key makeKey (String id, Context context)
-    {
-        String key = getContextPath(context);
-        key = key + "_" + getVirtualHost(context);
-        key = key+"_"+id;
-        return _keyFactory.newKey(key);
-    }
-    
-    /**
-     * Turn the context path into an acceptable string
-     * 
-     * @param context
-     * @return
-     */
-    private static String getContextPath (ContextHandler.Context context)
-    {
-        return canonicalize (context.getContextPath());
-    }
-
-    /**
-     * Get the first virtual host for the context.
-     *
-     * Used to help identify the exact session/contextPath.
-     *
-     * @return 0.0.0.0 if no virtual host is defined
-     */
-    private static String getVirtualHost (ContextHandler.Context context)
-    {
-        String vhost = "0.0.0.0";
-
-        if (context==null)
-            return vhost;
-
-        String [] vhosts = context.getContextHandler().getVirtualHosts();
-        if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
-            return vhost;
-
-        return vhosts[0];
-    }
-
-    /**
-     * Make an acceptable name from a context path.
-     *
-     * @param path
-     * @return
-     */
-    private static String canonicalize (String path)
-    {
-        if (path==null)
-            return "";
-
-        return path.replace('/', '_').replace('.','_').replace('\\','_');
-    }
-
 }
diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java
new file mode 100644
index 0000000..61e8279
--- /dev/null
+++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java
@@ -0,0 +1,201 @@
+//
+//  ========================================================================
+//  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.session.infinispan;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.server.SessionIdManager;
+import org.eclipse.jetty.server.session.AbstractSessionDataStore;
+import org.eclipse.jetty.server.session.SessionContext;
+import org.eclipse.jetty.server.session.SessionData;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.infinispan.commons.api.BasicCache;
+
+/**
+ * InfinispanSessionDataStore
+ *
+ *
+ */
+public class InfinispanSessionDataStore extends AbstractSessionDataStore
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+
+    public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2;
+
+
+    /**
+     * Clustered cache of sessions
+     */
+    private BasicCache<String, Object> _cache;
+
+    private SessionIdManager _idMgr = null;
+    
+    
+    private int _idleExpiryMultiple = DEFAULT_IDLE_EXPIRY_MULTIPLE;
+    
+
+    /**
+     * Get the clustered cache instance.
+     * 
+     * @return
+     */
+    public BasicCache<String, Object> getCache() 
+    {
+        return _cache;
+    }
+
+    
+    
+    /**
+     * Set the clustered cache instance.
+     * 
+     * @param cache
+     */
+    public void setCache (BasicCache<String, Object> cache) 
+    {
+        this._cache = cache;
+    }
+
+    public void setSessionIdManager (SessionIdManager idMgr)
+    {
+        _idMgr = idMgr;
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#load(org.eclipse.jetty.server.session.SessionKey)
+     */
+    @Override
+    public SessionData load(String id) throws Exception
+    {  
+        final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
+        final AtomicReference<Exception> exception = new AtomicReference<Exception>();
+        
+        Runnable load = new Runnable()
+        {
+            public void run ()
+            {
+                try
+                {
+
+                    SessionData sd = (SessionData)_cache.get(getCacheKey(id, _context));
+                    reference.set(sd);
+                }
+                catch (Exception e)
+                {
+                    exception.set(e);
+                }
+            }
+        };
+        
+        //ensure the load runs in the context classloader scope
+        _context.run(load);
+        
+        if (exception.get() != null)
+            throw exception.get();
+        
+        return reference.get();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#delete(org.eclipse.jetty.server.session.SessionKey)
+     */
+    @Override
+    public boolean delete(String id) throws Exception
+    {
+        return (_cache.remove(getCacheKey(id, _context)) != null);
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set)
+     */
+    @Override
+    public Set<String> getExpired(Set<String> candidates)
+    {
+       if (candidates == null  || candidates.isEmpty())
+           return candidates;
+       
+       long now = System.currentTimeMillis();
+       
+       Set<String> expired = new HashSet<String>();
+       if (LOG.isDebugEnabled())
+           LOG.debug("Getting expired sessions " + now);
+       
+       for (String candidate:candidates)
+       {
+           try
+           {
+               SessionData sd = load(candidate);
+               if (sd == null || sd.isExpiredAt(now))
+                   expired.add(candidate);
+                   
+           }
+           catch (Exception e)
+           {
+               LOG.warn("Error checking if session {} is expired", candidate, e);
+           }
+       }
+       
+       return expired;
+    }
+
+    /** 
+     * @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
+    {
+        //Put an idle timeout on the cache entry if the session is not immortal - 
+        //if no requests arrive at any node before this timeout occurs, or no node 
+        //scavenges the session before this timeout occurs, the session will be removed.
+        //NOTE: that no session listeners can be called for this.
+        if (data.getMaxInactiveMs() > 0)
+            _cache.put(getCacheKey(id, _context), data, -1, TimeUnit.MILLISECONDS, (data.getMaxInactiveMs() * _idleExpiryMultiple), TimeUnit.MILLISECONDS);
+        else
+            _cache.put(getCacheKey(id, _context), data);
+        
+        //tickle the session id manager to keep the sessionid entry for this session up-to-date
+        if (_idMgr != null && _idMgr instanceof InfinispanSessionIdManager)
+        {
+            ((InfinispanSessionIdManager)_idMgr).touch(id);
+        }
+    }
+    
+    
+    public static String getCacheKey (String id, SessionContext context)
+    {
+        return context.getCanonicalContextPath()+"_"+context.getVhost()+"_"+id;
+    }
+
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
+     */
+    @Override
+    public boolean isPassivating()
+    {
+        return true;
+    }
+}
diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java
index 4dfd6c0..2ef60da 100644
--- a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java
+++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionIdManager.java
@@ -28,8 +28,8 @@
 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.AbstractSession;
 import org.eclipse.jetty.server.session.AbstractSessionIdManager;
+import org.eclipse.jetty.server.session.Session;
 import org.eclipse.jetty.server.session.SessionHandler;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
@@ -70,7 +70,6 @@
     public final static String ID_KEY = "__o.e.j.s.infinispanIdMgr__";
     public static final int DEFAULT_IDLE_EXPIRY_MULTIPLE = 2;
     protected BasicCache<String,Object> _cache;
-    private Server _server;
     private int _idleExpiryMultiple = DEFAULT_IDLE_EXPIRY_MULTIPLE;
 
     
@@ -79,14 +78,12 @@
     
     public InfinispanSessionIdManager(Server server)
     {
-        super();
-        _server = server;
+        super(server);
     }
 
     public InfinispanSessionIdManager(Server server, Random random)
     {
-       super(random);
-       _server = server;
+       super(server, random);
     }
 
     
@@ -123,15 +120,15 @@
      * 
      * This method will consult the cluster.
      * 
-     * @see org.eclipse.jetty.server.SessionIdManager#idInUse(java.lang.String)
+     * @see org.eclipse.jetty.server.SessionIdManager#isIdInUse(java.lang.String)
      */
     @Override
-    public boolean idInUse(String id)
+    public boolean isIdInUse(String id)
     {
         if (id == null)
             return false;
         
-        String clusterId = getClusterId(id);
+        String clusterId = getId(id);
         
         //ask the cluster - this should also tickle the idle expiration timer on the sessionid entry
         //keeping it valid
@@ -147,29 +144,6 @@
         
     }
 
-    /** 
-     * Remember a new in-use session id.
-     * 
-     * This will save the in-use session id to the cluster.
-     * 
-     * @see org.eclipse.jetty.server.SessionIdManager#addSession(javax.servlet.http.HttpSession)
-     */
-    @Override
-    public void addSession(HttpSession session)
-    {
-        if (session == null)
-            return;
-
-        //insert into the cache and set an idle expiry on the entry that
-        //is based off the max idle time configured for the session. If the
-        //session is immortal, then there is no idle expiry on the corresponding
-        //session id
-        if (session.getMaxInactiveInterval() == 0)
-            insert (((AbstractSession)session).getClusterId());
-        else
-            insert (((AbstractSession)session).getClusterId(), session.getMaxInactiveInterval() * getIdleExpiryMultiple());
-    }
-    
     
     public void setIdleExpiryMultiple (int multiplier)
     {
@@ -185,90 +159,8 @@
         return _idleExpiryMultiple;
     }
     
-    
-    /** 
-     * Remove a session id from the list of in-use ids.
-     * 
-     * This will remvove the corresponding session id from the cluster.
-     * 
-     * @see org.eclipse.jetty.server.SessionIdManager#removeSession(javax.servlet.http.HttpSession)
-     */
-    @Override
-    public void removeSession(HttpSession session)
-    {
-        if (session == null)
-            return;
+   
 
-        //delete from the cache
-        delete (((AbstractSession)session).getClusterId());
-    }
-
-    /** 
-     * Remove a session id. This compels all other contexts who have a session
-     * with the same id to also remove it.
-     * 
-     * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
-     */
-    @Override
-    public void invalidateAll(String id)
-    {
-        //delete the session id from list of in-use sessions
-        delete (id);
-
-
-        //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 InfinispanSessionManager)
-                {
-                    ((InfinispanSessionManager)manager).invalidateSession(id);
-                }
-            }
-        }
-
-    }
-
-    /** 
-     * Change a session id. 
-     * 
-     * Typically this occurs when a previously existing session has passed through authentication.
-     * 
-     * @see org.eclipse.jetty.server.session.AbstractSessionIdManager#renewSessionId(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest)
-     */
-    @Override
-    public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request)
-    {
-        //generate a new id
-        String newClusterId = newSessionId(request.hashCode());
-
-        delete(oldClusterId);
-        insert(newClusterId);
-
-
-        //tell all contexts to update the id 
-        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 InfinispanSessionManager)
-                {
-                    ((InfinispanSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
-                }
-            }
-        }
-
-    }
 
     /**
      * Get the cache.
@@ -351,12 +243,12 @@
      * 
      * @param id the session id
      */
-    protected void delete (String id)
+    protected boolean delete (String id)
     {
         if (_cache == null)
             throw new IllegalStateException ("No cache");
         
-        _cache.remove(makeKey(id));
+        return _cache.remove(makeKey(id)) != null;
     }
     
     
@@ -371,4 +263,28 @@
     {
         return ID_KEY+id;
     }
+
+    /** 
+     * @see org.eclipse.jetty.server.SessionIdManager#useId(java.lang.String)
+     */
+    @Override
+    public void useId(Session session)
+    {
+        if (session == null)
+            return;
+        
+      if (session.getMaxInactiveInterval() == 0)
+            insert (session.getId());
+        else
+            insert (session.getId(), session.getMaxInactiveInterval() * getIdleExpiryMultiple());
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.SessionIdManager#removeId(java.lang.String)
+     */
+    @Override
+    public boolean removeId(String id)
+    {
+       return delete (id);        
+    }
 }
diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java
index d247f28..4790096 100644
--- a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java
+++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionManager.java
@@ -18,573 +18,35 @@
 
 package org.eclipse.jetty.session.infinispan;
 
-import java.io.IOException;
-import java.io.ObjectStreamException;
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.ReentrantLock;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.server.handler.ContextHandler.Context;
-import org.eclipse.jetty.server.session.AbstractSession;
-import org.eclipse.jetty.server.session.AbstractSessionManager;
-import org.eclipse.jetty.server.session.MemSession;
+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.log.Log;
 import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
-import org.eclipse.jetty.util.thread.Scheduler;
-import org.infinispan.Cache;
-import org.infinispan.commons.api.BasicCache;
-import org.omg.CORBA._IDLTypeStub;
+
+
 
 /**
  * InfinispanSessionManager
  * 
- * The data for a session relevant to a particular context is stored in an Infinispan (clustered) cache:
- * <pre>
- * Key:   is the id of the session + the context path + the vhost for the context 
- * Value: is the data of the session
- * </pre>
- * 
- * The key is necessarily complex because the same session id can be in-use by more than one
- * context. In this case, the contents of the session will strictly be different for each
- * context, although the id will be the same.
- * 
- * Sessions are also kept in local memory when they are used by this session manager. This allows
- * multiple different request threads in the same context to call Request.getSession() and
- * obtain the same object.
- * 
- * This session manager support scavenging, which is only done over the set of sessions in its
- * local memory. This can result in some sessions being "stranded" in the cluster cache if no
- * session manager is currently managing it (eg the node managing the session crashed and it
- * was never requested on another node).
+ * Convenience class to create a MemorySessionStore and an InfinispanSessionDataStore.
  * 
  */
-public class InfinispanSessionManager extends AbstractSessionManager
+public class InfinispanSessionManager extends SessionManager
 {
     private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
     
-    /**
-     * Clustered cache of sessions
-     */
-    private BasicCache<String, Object> _cache;
-    
-    
-    /**
-     * Sessions known to this node held in memory
-     */
-    private ConcurrentHashMap<String, InfinispanSessionManager.Session> _sessions;
 
-    
-    /**
-     * The length of time a session can be in memory without being checked against
-     * the cluster. A value of 0 indicates that the session is never checked against
-     * the cluster - the current node is considered to be the master for the session.
-     *
-     */
-    private long _staleIntervalSec = 0;
-    
-    protected Scheduler.Task _task; //scavenge task
-    protected Scheduler _scheduler;
-    protected Scavenger _scavenger;
-    protected long _scavengeIntervalMs = 1000L * 60 * 10; //10mins
-    protected boolean _ownScheduler;
-    
-    
+    protected InfinispanSessionDataStore _sessionDataStore;
 
-    /**
-     * Scavenger
-     *
-     */
-    protected class Scavenger implements Runnable
+
+    public InfinispanSessionManager()
     {
-
-        @Override
-        public void run()
-        {
-           try
-           {
-               scavenge();
-           }
-           finally
-           {
-               if (_scheduler != null && _scheduler.isRunning())
-                   _task = _scheduler.schedule(this, _scavengeIntervalMs, TimeUnit.MILLISECONDS);
-           }
-        }
+        _sessionStore = new MemorySessionStore();
+        _sessionDataStore = new InfinispanSessionDataStore();
     }
     
     
-    /*
-     * Every time a Session is put into the cache one of these objects
-     * is created to copy the data out of the in-memory session, and 
-     * every time an object is read from the cache one of these objects
-     * a fresh Session object is created based on the data held by this
-     * object.
-     */
-    public class SerializableSessionData implements Serializable
-    {
-        /**
-         * 
-         */
-        private static final long serialVersionUID = -7779120106058533486L;
-        String clusterId;
-        String contextPath;
-        String vhost;
-        long accessed;
-        long lastAccessed;
-        long createTime;
-        long cookieSetTime;
-        String lastNode;
-        long expiry;
-        long maxInactive;
-        Map<String, Object> attributes;
-
-        public SerializableSessionData()
-        {
-
-        }
-
-       
-       public SerializableSessionData(Session s)
-       {
-           clusterId = s.getClusterId();
-           contextPath = s.getContextPath();
-           vhost = s.getVHost();
-           accessed = s.getAccessed();
-           lastAccessed = s.getLastAccessedTime();
-           createTime = s.getCreationTime();
-           cookieSetTime = s.getCookieSetTime();
-           lastNode = s.getLastNode();
-           expiry = s.getExpiry();
-           maxInactive = s.getMaxInactiveInterval();
-           attributes = s.getAttributeMap(); // TODO pointer, not a copy
-       }
-        
-        private void writeObject(java.io.ObjectOutputStream out) throws IOException
-        {  
-            out.writeUTF(clusterId); //session id
-            out.writeUTF(contextPath); //context path
-            out.writeUTF(vhost); //first vhost
-
-            out.writeLong(accessed);//accessTime
-            out.writeLong(lastAccessed); //lastAccessTime
-            out.writeLong(createTime); //time created
-            out.writeLong(cookieSetTime);//time cookie was set
-            out.writeUTF(lastNode); //name of last node managing
-      
-            out.writeLong(expiry); 
-            out.writeLong(maxInactive);
-            out.writeObject(attributes);
-        }
-        
-        private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
-        {
-            clusterId = in.readUTF();
-            contextPath = in.readUTF();
-            vhost = in.readUTF();
-            
-            accessed = in.readLong();//accessTime
-            lastAccessed = in.readLong(); //lastAccessTime
-            createTime = in.readLong(); //time created
-            cookieSetTime = in.readLong();//time cookie was set
-            lastNode = in.readUTF(); //last managing node
-            expiry = in.readLong(); 
-            maxInactive = in.readLong();
-            attributes = (HashMap<String,Object>)in.readObject();
-        }
-        
-    }
-    
- 
-    
-    
-    /**
-     * Session
-     *
-     * Representation of a session in local memory.
-     */
-    public class Session extends MemSession
-    {
-        
-        private ReentrantLock _lock = new ReentrantLock();
-        
-        /**
-         * The (canonical) context path for with which this session is associated
-         */
-        private String _contextPath;
-        
-        
-        
-        /**
-         * The time in msec since the epoch at which this session should expire
-         */
-        private long _expiryTime; 
-        
-        
-        /**
-         * Time in msec since the epoch at which this session was last read from cluster
-         */
-        private long _lastSyncTime;
-        
-        
-        /**
-         * The workername of last node known to be managing the session
-         */
-        private String _lastNode;
-        
-        
-        /**
-         * If dirty, session needs to be (re)sent to cluster
-         */
-        protected boolean _dirty=false;
-        
-        
-     
-
-        /**
-         * Any virtual hosts for the context with which this session is associated
-         */
-        private String _vhost;
-
-        
-        /**
-         * Count of how many threads are active in this session
-         */
-        private AtomicInteger _activeThreads = new AtomicInteger(0);
-        
-        
-        
-        
-        /**
-         * A new session.
-         * 
-         * @param request the request
-         */
-        protected Session (HttpServletRequest request)
-        {
-            super(InfinispanSessionManager.this,request);
-            long maxInterval = getMaxInactiveInterval();
-            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
-            _lastNode = getSessionIdManager().getWorkerName();
-           setVHost(InfinispanSessionManager.getVirtualHost(_context));
-           setContextPath(InfinispanSessionManager.getContextPath(_context));
-           _activeThreads.incrementAndGet(); //access will not be called on a freshly created session so increment here
-        }
-        
-        
-        protected Session (SerializableSessionData sd)
-        {
-            super(InfinispanSessionManager.this, sd.createTime, sd.accessed, sd.clusterId);
-            _expiryTime = (sd.maxInactive <= 0 ? 0 : (System.currentTimeMillis() + sd.maxInactive*1000L));
-            setLastNode(sd.lastNode);
-            setContextPath(sd.contextPath);
-            setVHost(sd.vhost);
-            addAttributes(sd.attributes);
-        }
-        
-        
-        /**
-         * A restored session.
-         * 
-         * @param sessionId the session id
-         * @param created time created
-         * @param accessed time last accessed
-         * @param maxInterval max expiry interval
-         */
-        protected Session (String sessionId, long created, long accessed, long maxInterval)
-        {
-            super(InfinispanSessionManager.this, created, accessed, sessionId);
-            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
-        }
-        
-        /** 
-         * Called on entry to the session.
-         * 
-         * @see org.eclipse.jetty.server.session.AbstractSession#access(long)
-         */
-        @Override
-        protected boolean access(long time)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Access session({}) for context {} on worker {}", getId(), getContextPath(), getSessionIdManager().getWorkerName());
-            try
-            {
-
-                long now = System.currentTimeMillis();
-                //lock so that no other thread can call access or complete until the first one has refreshed the session object if necessary
-                _lock.lock();
-                //a request thread is entering
-                if (_activeThreads.incrementAndGet() == 1)
-                {
-                    //if the first thread, check that the session in memory is not stale, if we're checking for stale sessions
-                    if (getStaleIntervalSec() > 0  && (now - getLastSyncTime()) >= (getStaleIntervalSec() * 1000L))
-                    {
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("Acess session({}) for context {} on worker {} stale session. Reloading.", getId(), getContextPath(), getSessionIdManager().getWorkerName());
-                        refresh();
-                    }
-                }
-            }
-            catch (Exception e)
-            {
-                LOG.warn(e);
-            }
-            finally
-            {            
-                _lock.unlock();
-            }
-
-            if (super.access(time))
-            {
-                int maxInterval=getMaxInactiveInterval();
-                _expiryTime = (maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
-                return true;
-            }
-            return false;
-        }
-
-
-        /**
-         * Exit from session
-         * @see org.eclipse.jetty.server.session.AbstractSession#complete()
-         */
-        @Override
-        protected void complete()
-        {
-            super.complete();
-
-            //lock so that no other thread that might be calling access can proceed until this complete is done
-            _lock.lock();
-
-            try
-            {
-                //if this is the last request thread to be in the session
-                if (_activeThreads.decrementAndGet() == 0)
-                {
-                    try
-                    {
-                        //an invalid session will already have been removed from the
-                        //local session map and deleted from the cluster. If its valid save
-                        //it to the cluster.
-                        //TODO consider doing only periodic saves if only the last access
-                        //time to the session changes
-                        if (isValid())
-                        {
-                            //if session still valid && its dirty or stale or never been synced, write it to the cluster
-                            //otherwise, we just keep the updated last access time in memory
-                            if (_dirty || getLastSyncTime() == 0 || isStale(System.currentTimeMillis()))
-                            {
-                                willPassivate();
-                                save(this);
-                                didActivate();
-                            }
-                        }
-                    }
-                    catch (Exception e)
-                    {
-                        LOG.warn("Problem saving session({})",getId(), e);
-                    } 
-                    finally
-                    {
-                        _dirty = false;
-                    }
-                }
-            }
-            finally
-            {
-                _lock.unlock();
-            }
-        }
-        
-        /** Test if the session is stale
-         * @param atTime time when stale
-         * @return true if stale
-         */
-        protected boolean isStale (long atTime)
-        {
-            return (getStaleIntervalSec() > 0) && (atTime - getLastSyncTime() >= (getStaleIntervalSec()*1000L));
-        }
-        
-        
-        /** Test if the session is dirty
-         * @return true if dirty
-         */
-        protected boolean isDirty ()
-        {
-            return _dirty;
-        }
-
-        /** 
-         * Expire the session.
-         * 
-         * @see org.eclipse.jetty.server.session.AbstractSession#timeout()
-         */
-        @Override
-        protected void timeout()
-        {
-            super.timeout();
-        }
-        
-      
-        
-        /**
-         * Reload the session from the cluster. If the node that
-         * last managed the session from the cluster is ourself,
-         * then the session does not need refreshing.
-         * NOTE: this method MUST be called with sufficient locks
-         * in place to prevent 2 or more concurrent threads from
-         * simultaneously updating the session.
-         */
-        private void refresh ()
-        {
-            //get fresh copy from the cluster
-            Session fresh = load(makeKey(getClusterId(), _context));
-
-            //if the session no longer exists, invalidate
-            if (fresh == null)
-            {
-                invalidate();
-                return;
-            }
-
-            //cluster copy assumed to be the same as we were the last
-            //node to manage it
-            if (fresh.getLastNode().equals(getLastNode()))
-                return;
-
-            setLastNode(getSessionIdManager().getWorkerName());
-            
-            //prepare for refresh
-            willPassivate();
-
-            //if fresh has no attributes, remove them
-            if (fresh.getAttributes() == 0)
-                this.clearAttributes();
-            else
-            {
-                //reconcile attributes
-                for (String key:fresh.getAttributeMap().keySet())
-                {
-                    Object freshvalue = fresh.getAttribute(key);
-
-                    //session does not already contain this attribute, so bind it
-                    if (getAttribute(key) == null)
-                    { 
-                        doPutOrRemove(key,freshvalue);
-                        bindValue(key,freshvalue);
-                    }
-                    else //session already contains this attribute, update its value
-                    {
-                        doPutOrRemove(key,freshvalue);
-                    }
-
-                }
-                // cleanup, remove values from session, that don't exist in data anymore:
-                for (String key : getNames())
-                {
-                    if (fresh.getAttribute(key) == null)
-                    {
-                        Object oldvalue = getAttribute(key);
-                        doPutOrRemove(key,null);
-                        unbindValue(key,oldvalue);
-                    }
-                }
-            }
-            //finish refresh
-            didActivate();
-        }
-
-
-        public void setExpiry (long expiry)
-        {
-            _expiryTime = expiry;
-        }
-        
-
-        public long getExpiry ()
-        {
-            return _expiryTime;
-        }
-        
-        public void swapId (String newId, String newNodeId)
-        {
-            //TODO probably synchronize rather than use the access/complete lock?
-            _lock.lock();
-            setClusterId(newId);
-            setNodeId(newNodeId);
-            _lock.unlock();
-        }
-        
-        @Override
-        public void setAttribute (String name, Object value)
-        {
-            Object old = changeAttribute(name, value);
-            if (value == null && old == null)
-                return; //if same as remove attribute but attribute was already removed, no change
-            
-           _dirty = true;
-        }
-        
-        
-        public String getContextPath()
-        {
-            return _contextPath;
-        }
-
-
-        public void setContextPath(String contextPath)
-        {
-            this._contextPath = contextPath;
-        }
-
-
-        public String getVHost()
-        {
-            return _vhost;
-        }
-
-
-        public void setVHost(String vhost)
-        {
-            this._vhost = vhost;
-        }
-        
-        public String getLastNode()
-        {
-            return _lastNode;
-        }
-
-
-        public void setLastNode(String lastNode)
-        {
-            _lastNode = lastNode;
-        }
-
-
-        public long getLastSyncTime()
-        {
-            return _lastSyncTime;
-        }
-
-
-        public void setLastSyncTime(long lastSyncTime)
-        {
-            _lastSyncTime = lastSyncTime;
-        }
-
-    }
-
-
-
     
     /**
      * Start the session manager.
@@ -597,24 +59,9 @@
         if (_sessionIdManager == null)
             throw new IllegalStateException("No session id manager defined");
         
-        if (_cache == null)
-            throw new IllegalStateException("No session cache defined");
-        
-        _sessions = new ConcurrentHashMap<String, Session>();
-
-        //try and use a common scheduler, fallback to own
-        _scheduler = getSessionHandler().getServer().getBean(Scheduler.class);
-        if (_scheduler == null)
-        {
-            _scheduler = new ScheduledExecutorScheduler();
-            _ownScheduler = true;
-            _scheduler.start();
-        }
-        else if (!_scheduler.isStarted())
-            throw new IllegalStateException("Shared scheduler not started");
- 
-        setScavengeInterval(getScavengeInterval());
-        
+        ((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore);
+        _sessionDataStore.setSessionIdManager(_sessionIdManager);
+       
         super.doStart();
     }
 
@@ -629,544 +76,16 @@
     {
         super.doStop();
 
-        if (_task!=null)
-            _task.cancel();
-        _task=null;
-        if (_ownScheduler && _scheduler !=null)
-            _scheduler.stop();
-        _scheduler = null;
-        
-        _sessions.clear();
-        _sessions = null;
-    }
-    
-    
-    
-    /**
-     * Look for sessions in local memory that have expired.
-     */
-    /**
-     * 
-     */
-    public void scavenge ()
-    {
-        Set<String> candidateIds = new HashSet<String>();
-        long now = System.currentTimeMillis();
-        
-        LOG.info("SessionManager for context {} scavenging at {} ", getContextPath(getContext()), now);
-        for (Map.Entry<String, Session> entry:_sessions.entrySet())
-        {
-            long expiry = entry.getValue().getExpiry();
-            if (expiry > 0 && expiry < now)
-                candidateIds.add(entry.getKey());
-        }
-
-        for (String candidateId:candidateIds)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Session {} candidate for expiry", candidateId);
-            
-            Session candidateSession = _sessions.get(candidateId);
-            if (candidateSession != null)
-            {
-                //double check the state of the session in the cache, as the
-                //session may have migrated to another node. This leaves a window
-                //where the cached session may have been changed by another node
-                Session cachedSession = load(makeKey(candidateId, _context));
-                if (cachedSession == null)
-                {
-                   if (LOG.isDebugEnabled()) LOG.debug("Locally expired session({}) does not exist in cluster ",candidateId);
-                    //the session no longer exists, do a full invalidation
-                    candidateSession.timeout();
-                }
-                else if (getSessionIdManager().getWorkerName().equals(cachedSession.getLastNode()))
-                {
-                    if (LOG.isDebugEnabled()) LOG.debug("Expiring session({}) local to session manager",candidateId);
-                    //if I am the master of the session then it can be timed out
-                    candidateSession.timeout();
-                }
-                else
-                {
-                    //some other node is the master of the session, simply remove it from my memory
-                    if (LOG.isDebugEnabled()) LOG.debug("Session({}) not local to this session manager, removing from local memory", candidateId);
-                    candidateSession.willPassivate();
-                    _sessions.remove(candidateSession.getClusterId());
-                    _sessionsStats.decrement();
-                }
-
-            }
-        }
-    }
-    
-    
-
-    public long getScavengeInterval ()
-    {
-        return _scavengeIntervalMs/1000;
     }
 
-    
-    
-    /**
-     * Set the interval between runs of the scavenger. It should not be run too
-     * often.
-     * 
-     * 
-     * @param sec scavenge interval in seconds
-     */
-    public void setScavengeInterval (long sec)
-    {
-        if (sec<=0)
-            sec=60;
-
-        long old_period=_scavengeIntervalMs;
-        long period=sec*1000L;
-
-        _scavengeIntervalMs=period;
-
-        //add a bit of variability into the scavenge time so that not all
-        //nodes with the same scavenge time sync up
-        long tenPercent = _scavengeIntervalMs/10;
-        if ((System.currentTimeMillis()%2) == 0)
-            _scavengeIntervalMs += tenPercent;
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("Scavenging every "+_scavengeIntervalMs+" ms");
-        
-        synchronized (this)
-        {
-            if (_scheduler != null && (period!=old_period || _task==null))
-            {
-                if (_task!=null)
-                    _task.cancel();
-                if (_scavenger == null)
-                    _scavenger = new Scavenger();
-                
-                _task = _scheduler.schedule(_scavenger,_scavengeIntervalMs,TimeUnit.MILLISECONDS);
-            }
-        }
-    }
-    
-    
-    
-
-    /**
-     * Get the clustered cache instance.
-     * 
-     * @return the cache
-     */
-    public BasicCache<String, Object> getCache() 
-    {
-        return _cache;
-    }
-
-    
-    
-    /**
-     * Set the clustered cache instance.
-     * 
-     * @param cache the cache
-     */
-    public void setCache (BasicCache<String, Object> cache) 
-    {
-        this._cache = cache;
-    }
-
-
-    
-    
-    
-    public long getStaleIntervalSec()
-    {
-        return _staleIntervalSec;
-    }
-
-
-    public void setStaleIntervalSec(long staleIntervalSec)
-    {
-        _staleIntervalSec = staleIntervalSec;
-    }
-
-
-    /** 
-     * Add a new session for the context related to this session manager
-     * 
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
-     */
-    @Override
-    protected void addSession(AbstractSession session)
-    {
-        if (session==null)
-            return;
-        
-        if (LOG.isDebugEnabled()) LOG.debug("Adding session({}) to session manager for context {} on worker {}",session.getClusterId(), getContextPath(getContext()),getSessionIdManager().getWorkerName() + " with lastnode="+((Session)session).getLastNode());
-        _sessions.put(session.getClusterId(), (Session)session);
-        
-        try
-        {     
-                session.willPassivate();
-                save(((InfinispanSessionManager.Session)session));
-                session.didActivate();
-            
-        }
-        catch (Exception e)
-        {
-            LOG.warn("Unable to store new session id="+session.getId() , e);
-        }
-    }
-
-    /** 
-     * Ask the cluster for the session.
-     * 
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
-     */
-    @Override
-    public AbstractSession getSession(String idInCluster)
-    {
-        Session session = null;
-
-        //try and find the session in this node's memory
-        Session memSession = (Session)_sessions.get(idInCluster);
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("getSession({}) {} in session map",idInCluster,(memSession==null?"not":""));
-
-        long now = System.currentTimeMillis();
-        try
-        {
-            //if the session is not in this node's memory, then load it from the cluster cache
-            if (memSession == null)
-            {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("getSession({}): loading session data from cluster", idInCluster);
-
-                session = load(makeKey(idInCluster, _context));
-                if (session != null)
-                {
-                    //We retrieved a session with the same key from the database
-
-                    //Check that it wasn't expired
-                    if (session.getExpiry() > 0 && session.getExpiry() <= now)
-                    {
-                        if (LOG.isDebugEnabled()) LOG.debug("getSession ({}): Session expired", idInCluster);
-                        //ensure that the session id for the expired session is deleted so that a new session with the 
-                        //same id cannot be created (because the idInUse() test would succeed)
-                        ((InfinispanSessionIdManager)getSessionIdManager()).removeSession(session);
-                        return null;  
-                    }
-
-                    //Update the last worker node to me
-                    session.setLastNode(getSessionIdManager().getWorkerName());                            
-                    //TODO consider saving session here if lastNode was not this node
-
-                    //Check that another thread hasn't loaded the same session
-                    Session existingSession = _sessions.putIfAbsent(idInCluster, session);
-                    if (existingSession != null)
-                    {
-                        //use the one that the other thread inserted
-                        session = existingSession;
-                        LOG.debug("getSession({}): using session loaded by another request thread ", idInCluster);
-                    }
-                    else
-                    {
-                        //indicate that the session was reinflated
-                        session.didActivate();
-                        _sessionsStats.increment();
-                        LOG.debug("getSession({}): loaded session from cluster", idInCluster);
-                    }
-                    return session;
-                }
-                else
-                {
-                    //The requested session does not exist anywhere in the cluster
-                    LOG.debug("getSession({}): No session in cluster matching",idInCluster);
-                    return null;
-                }
-            }
-            else
-            {
-               //The session exists in this node's memory
-               LOG.debug("getSession({}): returning session from local memory ", memSession.getClusterId());
-                return memSession;
-            }
-        }
-        catch (Exception e)
-        {
-            LOG.warn("Unable to load session="+idInCluster, e);
-            return null;
-        }
-    }
-    
-    
-
-    /** 
-     * The session manager is stopping.
-     * 
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#shutdownSessions()
-     */
-    @Override
-    protected void shutdownSessions() throws Exception
-    {
-        Set<String> keys = new HashSet<String>(_sessions.keySet());
-        for (String key:keys)
-        {
-            Session session = _sessions.remove(key); //take the session out of the session list
-            //If the session is dirty, then write it to the cluster.
-            //If the session is simply stale do NOT write it to the cluster, as some other node
-            //may have started managing that session - this means that the last accessed/expiry time
-            //will not be updated, meaning it may look like it can expire sooner than it should.
-            try
-            {
-                if (session.isDirty())
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("Saving dirty session {} before exiting ", session.getId());
-                    save(session);
-                }
-            }
-            catch (Exception e)
-            {
-                LOG.warn(e);
-            }
-        }
-    }
-
-
-    @Override
-    protected AbstractSession newSession(HttpServletRequest request)
-    {
-        return new Session(request);
-    }
-
-    /** 
-     * Remove a session from local memory, and delete it from
-     * the cluster cache.
-     * 
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
-     */
-    @Override
-    protected boolean removeSession(String idInCluster)
-    {
-        Session session = (Session)_sessions.remove(idInCluster);
-        try
-        {
-            if (session != null)
-                delete(session);
-        }
-        catch (Exception e)
-        {
-            LOG.warn("Problem deleting session id="+idInCluster, e);
-        }
-        return session!=null;
-    }
-    
-    
-    
-    
-    @Override
-    public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
-    {
-        Session session = null;
-        try
-        {
-            //take the session with that id out of our managed list
-            session = (Session)_sessions.remove(oldClusterId);
-            if (session != null)
-            {
-                //TODO consider transactionality and ramifications if the session is live on another node
-                delete(session); //delete the old session from the cluster  
-                session.swapId(newClusterId, newNodeId); //update the session
-                _sessions.put(newClusterId, session); //put it into managed list under new key
-                save(session); //put the session under the new id into the cluster
-            }
-        }
-        catch (Exception e)
-        {
-            LOG.warn(e);
-        }
-
-        super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
-    }
 
 
     /**
-     * Load a session from the clustered cache.
-     * 
-     * @param key the session key
-     * @return the session
-     */
-    protected Session load (String key)
-    {
-        if (_cache == null)
-            throw new IllegalStateException("No cache");
-        
-        if (LOG.isDebugEnabled()) LOG.debug("Loading session {} from cluster", key);
-
-        SerializableSessionData storableSession = (SerializableSessionData)_cache.get(key);
-        if (storableSession == null)
-        {
-            if (LOG.isDebugEnabled()) LOG.debug("No session {} in cluster ",key);
-            return null;
-        }
-        else
-        {
-            Session session = new Session (storableSession);
-            session.setLastSyncTime(System.currentTimeMillis());
-            return session;
-        }
-    }
-    
-    
-    
-    /**
-     * Save or update the session to the cluster cache
-     * 
-     * @param session the session
-     * @throws Exception if unable to save
-     */
-    protected void save (InfinispanSessionManager.Session session)
-    throws Exception
-    {
-        if (_cache == null)
-            throw new IllegalStateException("No cache");
-        
-        if (LOG.isDebugEnabled()) LOG.debug("Writing session {} to cluster", session.getId());
-    
-        SerializableSessionData storableSession = new SerializableSessionData(session);
-
-        //Put an idle timeout on the cache entry if the session is not immortal - 
-        //if no requests arrive at any node before this timeout occurs, or no node 
-        //scavenges the session before this timeout occurs, the session will be removed.
-        //NOTE: that no session listeners can be called for this.
-        InfinispanSessionIdManager sessionIdManager = (InfinispanSessionIdManager)getSessionIdManager();
-        if (storableSession.maxInactive > 0)
-            _cache.put(makeKey(session, _context), storableSession, -1, TimeUnit.SECONDS, storableSession.maxInactive*sessionIdManager.getIdleExpiryMultiple(), TimeUnit.SECONDS);
-        else
-            _cache.put(makeKey(session, _context), storableSession);
-        
-        //tickle the session id manager to keep the sessionid entry for this session up-to-date
-        sessionIdManager.touch(session.getClusterId());
-        
-        session.setLastSyncTime(System.currentTimeMillis());
-    }
-    
-    
-    
-    /**
-     * Remove the session from the cluster cache.
-     * 
-     * @param session the session
-     */
-    protected void delete (InfinispanSessionManager.Session session)
-    {  
-        if (_cache == null)
-            throw new IllegalStateException("No cache");
-        if (LOG.isDebugEnabled()) LOG.debug("Removing session {} from cluster", session.getId());
-        _cache.remove(makeKey(session, _context));
-    }
-
-    
-    /**
-     * Invalidate a session for this context with the given id
-     * 
-     * @param idInCluster session id in cluster
-     */
-    public void invalidateSession (String idInCluster)
-    {
-        Session session = (Session)_sessions.get(idInCluster);
-
-        if (session != null)
-        {
-            session.invalidate();
-        }
-    }
-
-    
-    /**
-     * Make a unique key for this session.
-     * As the same session id can be used across multiple contexts, to
-     * make it unique, the key must be composed of:
-     * <ol>
-     * <li>the id</li>
-     * <li>the context path</li>
-     * <li>the virtual hosts</li>
-     * </ol>
-     * 
-     *TODO consider the difference between getClusterId and getId
-     * @param session
      * @return
      */
-    private String makeKey (Session session, Context context)
+    public InfinispanSessionDataStore getSessionDataStore()
     {
-       return makeKey(session.getId(), context);
-    }
-    
-    /**
-     * Make a unique key for this session.
-     * As the same session id can be used across multiple contexts, to
-     * make it unique, the key must be composed of:
-     * <ol>
-     * <li>the id</li>
-     * <li>the context path</li>
-     * <li>the virtual hosts</li>
-     * </ol>
-     * 
-     *TODO consider the difference between getClusterId and getId
-     * @param session
-     * @return
-     */
-    private String makeKey (String id, Context context)
-    {
-        String key = getContextPath(context);
-        key = key + "_" + getVirtualHost(context);
-        key = key+"_"+id;
-        return key;
-    }
-    
-    /**
-     * Turn the context path into an acceptable string
-     * 
-     * @param context
-     * @return
-     */
-    private static String getContextPath (ContextHandler.Context context)
-    {
-        return canonicalize (context.getContextPath());
-    }
-
-    /**
-     * Get the first virtual host for the context.
-     *
-     * Used to help identify the exact session/contextPath.
-     *
-     * @return 0.0.0.0 if no virtual host is defined
-     */
-    private static String getVirtualHost (ContextHandler.Context context)
-    {
-        String vhost = "0.0.0.0";
-
-        if (context==null)
-            return vhost;
-
-        String [] vhosts = context.getContextHandler().getVirtualHosts();
-        if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
-            return vhost;
-
-        return vhosts[0];
-    }
-
-    /**
-     * Make an acceptable name from a context path.
-     *
-     * @param path
-     * @return
-     */
-    private static String canonicalize (String path)
-    {
-        if (path==null)
-            return "";
-
-        return path.replace('/', '_').replace('.','_').replace('\\','_');
+        return _sessionDataStore;
     }
 
 }
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 afdd9ed..0000000
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSession.java
+++ /dev/null
@@ -1,224 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2016 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 0000000..aa296a7
--- /dev/null
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionDataStore.java
@@ -0,0 +1,98 @@
+//
+//  ========================================================================
+//  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;
+
+
+/**
+ * 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, _context.getCanonicalContextPath(), _context.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 b82f9f2..0000000
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/NoSqlSessionManager.java
+++ /dev/null
@@ -1,435 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2016 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);
-                    _sessionsStats.increment();
-                }
-                
-                //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 0000000..d977c06
--- /dev/null
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java
@@ -0,0 +1,643 @@
+//
+//  ========================================================================
+//  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 java.util.concurrent.atomic.AtomicReference;
+
+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;
+import com.mongodb.WriteResult;
+
+/**
+ * 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;
+    }
+    
+    
+    /**
+     * @return
+     */
+    public DBCollection getDBCollection ()
+    {
+        return _dbSessions;
+    }
+    
+    /**
+     * @return
+     */
+    public int getGracePeriodSec ()
+    {
+        return (int)(_gracePeriodMs == 0L? 0 : _gracePeriodMs/1000L);
+    }
+    
+    /**
+     * @param sec
+     */
+    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
+    {
+        final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
+        final AtomicReference<Exception> exception = new AtomicReference<Exception>();
+        Runnable r = new Runnable()
+        {
+            public void run ()
+            {
+                try
+                {
+                    DBObject sessionDocument = _dbSessions.findOne(new BasicDBObject(__ID, id));
+
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("id={} loaded={}", id, sessionDocument);
+                    
+                    if (sessionDocument == null)
+                        return;
+
+                    Boolean valid = (Boolean)sessionDocument.get(__VALID);   
+
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("id={} valid={}", id, valid);
+                    if (valid == null || !valid)
+                        return;
+
+
+                    Object version = getNestedValue(sessionDocument, 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, _context);
+
+                        //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(_context.getCanonicalContextPath());
+                        data.setVhost(_context.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, _context);        
+                    }
+
+                    reference.set(data);
+                }
+                catch (Exception e)
+                {
+                    exception.set(e);
+                }
+            }
+        };
+        
+        _context.run(r);
+        
+        if (exception.get() != null)
+            throw exception.get();
+        
+        return reference.get();
+    }
+
+    /** 
+     * @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, _context);
+
+        /*
+         * 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)
+        {
+            DBObject c = (DBObject)getNestedValue(sessionDocument, __CONTEXT);
+            if (c == null)
+            {
+                //delete whole doc
+                _dbSessions.remove(mongoKey);
+                return false;
+            }
+
+            Set<String> contexts = c.keySet();
+            if (contexts.isEmpty())
+            {
+                //delete whole doc
+                _dbSessions.remove(mongoKey);
+                return false;
+            }
+
+            if (contexts.size() == 1 && contexts.iterator().next().equals(getCanonicalContextId()))
+            {
+                //delete whole doc
+                 _dbSessions.remove(mongoKey);
+                return true;
+            }
+            
+            //just remove entry for my context
+            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 {}", _context,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 {}", _context, 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
+    {
+        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());
+            nsqd.setVersion(version);
+        }
+        else
+        {
+            version = new Long(((Number)version).longValue() + 1);
+            nsqd.setVersion(version);
+            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)
+            {
+                Long currentMaxIdle = (Long)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 + "." + getCanonicalContextId();
+    }
+    
+   
+    private String getCanonicalContextId ()
+    {
+        return canonicalizeVHost(_context.getVhost()) + ":" + _context.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]);
+    }
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
+     */
+    @Override
+    public boolean isPassivating()
+    {
+        return true;
+    }
+
+}
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 004af6d..9cad234 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 @@
 
 
 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 DBObject __expiry = new BasicDBObject(MongoSessionDataStore.__EXPIRY, 1);
 
-    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 @@
      */
     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);
-            }
-        }
-    }
-    
+   
     
     
 
@@ -177,9 +74,8 @@
     /* ------------------------------------------------------------ */
     public MongoSessionIdManager(Server server, DBCollection sessions)
     {
-        super(new Random());
+        super(server, new Random());
         
-        _server = server;
         _sessions = sessions;
 
         _sessions.ensureIndex(
@@ -193,187 +89,10 @@
         // 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(getWorkerName()+":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(getWorkerName()+":SessionIdManager:scavenge: {} expiring session {}", atTime,(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,192 +101,20 @@
         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");
-
-
-        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");
-        }
+        if (LOG.isDebugEnabled()) LOG.debug("MongoSessionIdManager:starting");
+        super.doStart();
     }
 
     /* ------------------------------------------------------------ */
     @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;
-            }
-        }
+        if (LOG.isDebugEnabled()) LOG.debug("MongoSessionIdManager:stopping");
         super.doStop();
     }
 
@@ -576,20 +123,26 @@
      * Searches database to find if the session id known to mongo, and is it valid
      */
     @Override
-    public boolean idInUse(String sessionId)
+    public boolean isIdInUse(String sessionId)
     {        
         /*
-         * optimize this query to only return the valid variable
+         * optimize this query to only return the valid and expiry
          */
-        DBObject o = _sessions.findOne(new BasicDBObject("id",sessionId), __valid_true);
+        DBObject fields = new BasicDBObject();
+        fields.put(MongoSessionDataStore.__VALID, new Long(1));
+        fields.put(MongoSessionDataStore.__EXPIRY, new Long(1));
+        
+        DBObject o = _sessions.findOne(new BasicDBObject(MongoSessionDataStore.__ID,sessionId), fields);
         
         if ( o != null )
         {                    
-            Boolean valid = (Boolean)o.get(MongoSessionManager.__VALID);
+            Boolean valid = (Boolean)o.get(MongoSessionDataStore.__VALID);
             if ( valid == null )
-            {
+                return false;            
+            
+            Long expiry = (Long)o.get(MongoSessionDataStore.__EXPIRY);
+            if (expiry < System.currentTimeMillis())
                 return false;
-            }            
             
             return valid;
         }
@@ -599,119 +152,27 @@
 
     /* ------------------------------------------------------------ */
     @Override
-    public void addSession(HttpSession session)
+    public void useId(Session session)
     {
         if (session == null)
-        {
             return;
-        }
         
         /*
          * already a part of the index in mongo...
          */
         
-        __log.debug("MongoSessionIdManager:addSession {}", session.getId());
-        
-        _sessionsIds.add(session.getId());
-        
+        LOG.debug("MongoSessionIdManager:addSession {}", session.getId());
     }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public void removeSession(HttpSession session)
-    {
-        if (session == null)
-        {
-            return;
-        }
-        
-        _sessionsIds.remove(session.getId());
-    }
-
-    /* ------------------------------------------------------------ */
-    /** Remove the session id from the list of in-use sessions.
-     * Inform all other known contexts that sessions with the same id should be
-     * invalidated.
-     * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
-     */
-    @Override
-    public void invalidateAll(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);
-                }
-            }
-        }
-    }      
  
 
     /* ------------------------------------------------------------ */
-    /**
-     * 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)
+    public boolean removeId(String id)
     {
-        //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++)
-        {
-            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, getNodeId(newClusterId, request));
-                }
-            }
-        }
+       //The corresponding session document will be marked as expired or invalid?
+        return true; //can't distinguish first remove vs subsequent removes
     }
 
+  
+
 }
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 06b8077..4e813b1 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
@@ -18,31 +18,20 @@
 
 package org.eclipse.jetty.nosql.mongodb;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
 import java.net.UnknownHostException;
-import java.util.Date;
-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.SessionDataStore;
+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;
 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.DBObject;
 import com.mongodb.MongoException;
-import com.mongodb.WriteConcern;
 
 
 /**
@@ -93,71 +82,10 @@
  *  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__";
+    private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
 
-    
-    /**
-     * 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";
-
-    /**
-    * the context id is only set when this class has been started
-    */
-    private String _contextId = null;
 
     
     /**
@@ -166,16 +94,14 @@
     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 +109,10 @@
     /*------------------------------------------------------------ */
     @Override
     public void doStart() throws Exception
-    {
+    {    
+        ((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore);
+        _sessionDataStore.setDBCollection(_dbSessions);
         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,491 +128,15 @@
         
     }
 
-    /* ------------------------------------------------------------ */
-    @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)
+    public MongoSessionDataStore getSessionDataStore()
     {
-        __log.debug("MongoSessionManager:remove:session {} for context {}",session.getClusterId(), getContextKey());
+        return _sessionDataStore;
+    }
+  
 
-        /*
-         * 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
@@ -712,81 +150,5 @@
     {
         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);
-            }
-        }
-    }
-
 
 }
diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/jmx/MongoSessionManagerMBean.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/jmx/MongoSessionManagerMBean.java
index 01e4f65..c444a27 100644
--- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/jmx/MongoSessionManagerMBean.java
+++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/jmx/MongoSessionManagerMBean.java
@@ -22,11 +22,11 @@
 import org.eclipse.jetty.server.handler.AbstractHandlerContainer;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.session.SessionHandler;
-import org.eclipse.jetty.server.session.jmx.AbstractSessionManagerMBean;
+import org.eclipse.jetty.server.session.jmx.SessionManagerMBean;
 import org.eclipse.jetty.util.annotation.ManagedObject;
 
 @ManagedObject("Mongo Session Manager MBean")
-public class MongoSessionManagerMBean extends AbstractSessionManagerMBean
+public class MongoSessionManagerMBean extends SessionManagerMBean
 {
 
     public MongoSessionManagerMBean(Object managedObject)
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java
index 91f6f55..1d0ae2d 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java
@@ -99,7 +99,7 @@
 
         if (nodeName != null)
         {
-            AbstractSessionIdManager sessionIdManager = new HashSessionIdManager();
+            AbstractSessionIdManager sessionIdManager = new HashSessionIdManager(server);
             sessionIdManager.setWorkerName(nodeName);
             server.setSessionIdManager(sessionIdManager);
         }
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java
index 8518695..f44439a 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java
@@ -29,7 +29,7 @@
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.server.UserIdentity;
-import org.eclipse.jetty.server.session.AbstractSession;
+import org.eclipse.jetty.server.session.Session;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
@@ -109,17 +109,17 @@
             {
                 //if we should renew sessions, and there is an existing session that may have been seen by non-authenticated users
                 //(indicated by SESSION_SECURED not being set on the session) then we should change id
-                if (httpSession.getAttribute(AbstractSession.SESSION_CREATED_SECURE)!=Boolean.TRUE)
+                if (httpSession.getAttribute(Session.SESSION_CREATED_SECURE)!=Boolean.TRUE)
                 {
-                    if (httpSession instanceof AbstractSession)
+                    if (httpSession instanceof Session)
                     {
-                        AbstractSession abstractSession = (AbstractSession)httpSession;
-                        String oldId = abstractSession.getId();
-                        abstractSession.renewId(request);
-                        abstractSession.setAttribute(AbstractSession.SESSION_CREATED_SECURE, Boolean.TRUE);
-                        if (abstractSession.isIdChanged() && response != null && (response instanceof Response))
-                            ((Response)response).addCookie(abstractSession.getSessionManager().getSessionCookie(abstractSession, request.getContextPath(), request.isSecure()));
-                        LOG.debug("renew {}->{}",oldId,abstractSession.getId());
+                        Session s = (Session)httpSession;
+                        String oldId = s.getId();
+                        s.renewId(request);
+                        s.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE);
+                        if (s.isIdChanged() && response != null && (response instanceof Response))
+                            ((Response)response).addCookie(s.getSessionManager().getSessionCookie(s, request.getContextPath(), request.isSecure()));
+                        LOG.debug("renew {}->{}",oldId,s.getId());
                     }
                     else
                         LOG.warn("Unable to renew session "+httpSession);
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
index 9155bf3..373bd93 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
@@ -33,7 +33,7 @@
 import org.eclipse.jetty.security.LoginService;
 import org.eclipse.jetty.security.SecurityHandler;
 import org.eclipse.jetty.server.UserIdentity;
-import org.eclipse.jetty.server.session.AbstractSession;
+import org.eclipse.jetty.server.session.Session;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
@@ -89,7 +89,7 @@
         if (security!=null)
             security.logout(this);
         if (_session!=null)
-            _session.removeAttribute(AbstractSession.SESSION_CREATED_SECURE);
+            _session.removeAttribute(Session.SESSION_CREATED_SECURE);
     }
 
     @Override
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
index 8e7b07f..dff6eb6 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
@@ -76,7 +76,7 @@
 import org.eclipse.jetty.http.MimeTypes;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.ContextHandler.Context;
-import org.eclipse.jetty.server.session.AbstractSession;
+import org.eclipse.jetty.server.session.Session;
 import org.eclipse.jetty.util.Attributes;
 import org.eclipse.jetty.util.AttributesMap;
 import org.eclipse.jetty.util.IO;
@@ -1491,23 +1491,24 @@
     }
 
     /* ------------------------------------------------------------ */
-    /*
-     * Add @override when 3.1 api is available
+    /** 
+     * @see javax.servlet.http.HttpServletRequest#changeSessionId()
      */
+    @Override
     public String changeSessionId()
     {
         HttpSession session = getSession(false);
         if (session == null)
             throw new IllegalStateException("No session");
 
-        if (session instanceof AbstractSession)
+        if (session instanceof Session)
         {
-            AbstractSession abstractSession =  ((AbstractSession)session);
-            abstractSession.renewId(this);
+            Session s =  ((Session)session);
+            s.renewId(this);
             if (getRemoteUser() != null)
-                abstractSession.setAttribute(AbstractSession.SESSION_CREATED_SECURE, Boolean.TRUE);
-            if (abstractSession.isIdChanged())
-                _channel.getResponse().addCookie(_sessionManager.getSessionCookie(abstractSession, getContextPath(), isSecure()));
+                s.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE);
+            if (s.isIdChanged())
+                _channel.getResponse().addCookie(_sessionManager.getSessionCookie(s, getContextPath(), isSecure()));
         }
 
         return session.getId();
@@ -1711,7 +1712,7 @@
             return false;
 
         HttpSession session = getSession(false);
-        return (session != null && _sessionManager.getSessionIdManager().getClusterId(_requestedSessionId).equals(_sessionManager.getClusterId(session)));
+        return (session != null && _sessionManager.getSessionIdManager().getId(_requestedSessionId).equals(_sessionManager.getId(session)));
     }
 
     /* ------------------------------------------------------------ */
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
index d8cdad9..fdd4cb3 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
@@ -448,7 +448,7 @@
         if (!sessionManager.isValid(session))
             return url;
 
-        String id = sessionManager.getNodeId(session);
+        String id = sessionManager.getExtendedId(session);
 
         if (uri == null)
             uri = new HttpURI(url);
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java
index 80946c6..68bcb34 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionIdManager.java
@@ -19,8 +19,8 @@
 package org.eclipse.jetty.server;
 
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
 
+import org.eclipse.jetty.server.session.Session;
 import org.eclipse.jetty.util.component.LifeCycle;
 
 /** Session ID Manager.
@@ -29,28 +29,31 @@
 public interface SessionIdManager extends LifeCycle
 {
     /**
-     * @param id The session ID without any cluster node extension
+     * @param id The plain session ID (ie no workername extension)
      * @return True if the session ID is in use by at least one context.
      */
-    public boolean idInUse(String id);
+    public boolean isIdInUse(String id);
+    
     
     /**
-     * Add a session to the list of known sessions for a given ID.
-     * @param session The session
+     * Notify the sessionid manager that a particular session id is in use
+     * @param the session whose id is being used
      */
-    public void addSession(HttpSession session);
+    public void useId (Session session);
     
     /**
-     * Remove session from the list of known sessions for a given ID.
-     * @param session the session to remove
+     * Remove id
+     * @param id the plain session id (no workername extension) of the session to remove
+     * @return true if the id was removed, false otherwise
      */
-    public void removeSession(HttpSession session);
+    public boolean removeId (String id);
     
     /**
-     * Call {@link HttpSession#invalidate()} on all known sessions for the given id.
+     * Invalidate all sessions on all contexts that share the same id.
+     * 
      * @param id The session ID without any cluster node extension
      */
-    public void invalidateAll(String id);
+    public void expireAll(String id);
     
     /**
      * Create a new Session ID.
@@ -67,30 +70,36 @@
     
     
     /* ------------------------------------------------------------ */
-    /** Get a cluster ID from a node ID.
+    /** Get just the session id from an id that includes the worker name
+     * as a suffix.
+     * 
      * Strip node identifier from a located session ID.
-     * @param nodeId the node id
+     * @param qualifiedId the session id including the worker name
      * @return the cluster id
      */
-    public String getClusterId(String nodeId);
+    public String getId(String qualifiedId);
+    
+    
     
     /* ------------------------------------------------------------ */
-    /** Get a node ID from a cluster ID and a request
-     * @param clusterId The ID of the session
+    /** Get an extended id for a session. An extended id contains
+     * the workername as a suffix.
+     * 
+     * @param id The id of the session
      * @param request The request that for the session (or null)
-     * @return The session ID qualified with the node ID.
+     * @return The session id qualified with the worker name
      */
-    public String getNodeId(String clusterId,HttpServletRequest request);
+    public String getExtendedId(String id,HttpServletRequest request);
     
     
     /* ------------------------------------------------------------ */
     /** Change the existing session id.
     * 
-    * @param oldClusterId the old cluster id
-    * @param oldNodeId the old node id
+    * @param oldId the old plain session id
+    * @param oldExtendedId the old fully qualified id
     * @param request the request containing the session
     */
-    public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request);    
+    public void renewSessionId(String oldId, String oldExtendedId, HttpServletRequest request);    
 
     
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
index d2a10df..1b5cfee 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
@@ -194,13 +194,7 @@
      */
     public SessionIdManager getSessionIdManager();
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @return the cross context session id manager.
-     * @deprecated use {@link #getSessionIdManager()}
-     */
-    @Deprecated
-    public SessionIdManager getMetaManager();
+
 
     /* ------------------------------------------------------------ */
     /**
@@ -222,17 +216,17 @@
     /**
      * @param session the session object
      * @return the unique id of the session within the cluster, extended with an optional node id.
-     * @see #getClusterId(HttpSession)
+     * @see #getId(HttpSession)
      */
-    public String getNodeId(HttpSession session);
+    public String getExtendedId(HttpSession session);
 
     /* ------------------------------------------------------------ */
     /**
      * @param session the session object
      * @return the unique id of the session within the cluster (without a node id extension)
-     * @see #getNodeId(HttpSession)
+     * @see #getExtendedId(HttpSession)
      */
-    public String getClusterId(HttpSession session);
+    public String getId(HttpSession session);
 
     /* ------------------------------------------------------------ */
     /**
@@ -308,10 +302,10 @@
     /* ------------------------------------------------------------ */
     /** Change the existing session id.
     * 
-    * @param oldClusterId the old cluster id
-    * @param oldNodeId the old node id
-    * @param newClusterId the new cluster id
-    * @param newNodeId the new node id
+    * @param oldId the old session id
+    * @param oldExtendedId the session id including worker suffix
+    * @param newId the new session id
+    * @param newExtendedId the new session id including worker suffix
     */
-    public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId);  
+    public void renewSessionId(String oldId, String oldExtendedId, String newId, String newExtendedId);  
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java
deleted file mode 100644
index 7fbc25e..0000000
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java
+++ /dev/null
@@ -1,664 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.server.session;
-
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSessionActivationListener;
-import javax.servlet.http.HttpSessionBindingEvent;
-import javax.servlet.http.HttpSessionBindingListener;
-import javax.servlet.http.HttpSessionContext;
-import javax.servlet.http.HttpSessionEvent;
-
-import org.eclipse.jetty.server.SessionManager;
-import org.eclipse.jetty.util.log.Logger;
-
-/**
- *
- * <p>
- * Implements {@link javax.servlet.http.HttpSession} from the <code>javax.servlet</code> package.
- * </p>
- *
- */
-@SuppressWarnings("deprecation")
-public abstract class AbstractSession implements AbstractSessionManager.SessionIf
-{
-    final static Logger LOG = SessionHandler.LOG;
-    public final static String SESSION_CREATED_SECURE="org.eclipse.jetty.security.sessionCreatedSecure";
-    private  String _clusterId; // ID without any node (ie "worker") id appended
-    private  String _nodeId;    // ID of session with node(ie "worker") id appended
-    private final AbstractSessionManager _manager;
-    private boolean _idChanged;
-    private final long _created;
-    private long _cookieSet;
-    private long _accessed;         // the time of the last access
-    private long _lastAccessed;     // the time of the last access excluding this one
-    private boolean _invalid;
-    private boolean _doInvalidate;
-    private long _maxIdleMs;
-    private boolean _newSession;
-    private int _requests;
-
-
-
-    /* ------------------------------------------------------------- */
-    protected AbstractSession(AbstractSessionManager abstractSessionManager, HttpServletRequest request)
-    {
-        _manager = abstractSessionManager;
-
-        _newSession=true;
-        _created=System.currentTimeMillis();
-        _clusterId=_manager._sessionIdManager.newSessionId(request,_created);
-        _nodeId=_manager._sessionIdManager.getNodeId(_clusterId,request);
-        _accessed=_created;
-        _lastAccessed=_created;
-        _requests=1;
-        _maxIdleMs=_manager._dftMaxIdleSecs>0?_manager._dftMaxIdleSecs*1000L:-1;
-        if (LOG.isDebugEnabled())
-            LOG.debug("new session & id "+_nodeId+" "+_clusterId);
-    }
-
-    /* ------------------------------------------------------------- */
-    protected AbstractSession(AbstractSessionManager abstractSessionManager, long created, long accessed, String clusterId)
-    {
-        _manager = abstractSessionManager;
-        _created=created;
-        _clusterId=clusterId;
-        _nodeId=_manager._sessionIdManager.getNodeId(_clusterId,null);
-        _accessed=accessed;
-        _lastAccessed=accessed;
-        _requests=1;
-        _maxIdleMs=_manager._dftMaxIdleSecs>0?_manager._dftMaxIdleSecs*1000L:-1;
-        if (LOG.isDebugEnabled())
-            LOG.debug("new session "+_nodeId+" "+_clusterId);
-    }
-
-    /* ------------------------------------------------------------- */
-    /**
-     * asserts that the session is valid
-     * @throws IllegalStateException if the sesion is invalid
-     */
-    protected void checkValid() throws IllegalStateException
-    {
-        if (_invalid)
-            throw new IllegalStateException();
-    }
-    
-    /* ------------------------------------------------------------- */
-    /** Check to see if session has expired as at the time given.
-     * @param time the time in milliseconds
-     * @return true if expired
-     */
-    protected boolean checkExpiry(long time)
-    {
-        if (_maxIdleMs>0 && _lastAccessed>0 && _lastAccessed + _maxIdleMs < time)
-            return true;
-        return false;
-    }
-
-    /* ------------------------------------------------------------- */
-    @Override
-    public AbstractSession getSession()
-    {
-        return this;
-    }
-
-    /* ------------------------------------------------------------- */
-    public long getAccessed()
-    {
-        synchronized (this)
-        {
-            return _accessed;
-        }
-    }
-
-    /* ------------------------------------------------------------- */
-    public abstract Map<String,Object> getAttributeMap();
-
-
- 
-    
-
-    /* ------------------------------------------------------------ */
-    public abstract int getAttributes();
-  
-
- 
-
-    /* ------------------------------------------------------------ */
-    public abstract Set<String> getNames();
-  
-
-    /* ------------------------------------------------------------- */
-    public long getCookieSetTime()
-    {
-        return _cookieSet;
-    }
-    
-    /* ------------------------------------------------------------- */
-    public void setCookieSetTime(long time)
-    {
-        _cookieSet = time;
-    }
-
-    /* ------------------------------------------------------------- */
-    @Override
-    public long getCreationTime() throws IllegalStateException
-    {
-        checkValid();
-        return _created;
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public String getId() throws IllegalStateException
-    {
-        return _manager._nodeIdInSessionId?_nodeId:_clusterId;
-    }
-
-    /* ------------------------------------------------------------- */
-    public String getNodeId()
-    {
-        return _nodeId;
-    }
-
-    /* ------------------------------------------------------------- */
-    public String getClusterId()
-    {
-        return _clusterId;
-    }
-
-    /* ------------------------------------------------------------- */
-    @Override
-    public long getLastAccessedTime() throws IllegalStateException
-    {
-        checkValid();
-        return _lastAccessed;
-    }
-    
-    /* ------------------------------------------------------------- */
-    public void setLastAccessedTime(long time)
-    {
-        _lastAccessed = time;
-    }
-
-    /* ------------------------------------------------------------- */
-    @Override
-    public int getMaxInactiveInterval()
-    {
-        return (int)(_maxIdleMs/1000);
-    }
-
-    /* ------------------------------------------------------------ */
-    /*
-     * @see javax.servlet.http.HttpSession#getServletContext()
-     */
-    @Override
-    public ServletContext getServletContext()
-    {
-        return _manager._context;
-    }
-
-    /* ------------------------------------------------------------- */
-    @Deprecated
-    @Override
-    public HttpSessionContext getSessionContext() throws IllegalStateException
-    {
-        checkValid();
-        return AbstractSessionManager.__nullSessionContext;
-    }
-
-    /* ------------------------------------------------------------- */
-    /**
-     * @deprecated As of Version 2.2, this method is replaced by
-     *             {@link #getAttribute}
-     */
-    @Deprecated
-    @Override
-    public Object getValue(String name) throws IllegalStateException
-    {
-        return getAttribute(name);
-    }
-
- 
-
-    /* ------------------------------------------------------------ */
-    public void renewId(HttpServletRequest request)
-    {
-        _manager._sessionIdManager.renewSessionId(getClusterId(), getNodeId(), request); 
-        setIdChanged(true);
-    }
-       
-    /* ------------------------------------------------------------- */
-    public SessionManager getSessionManager()
-    {
-        return _manager;
-    }
-
-    /* ------------------------------------------------------------ */
-    protected void setClusterId (String clusterId)
-    {
-        _clusterId = clusterId;
-    }
-    
-    /* ------------------------------------------------------------ */
-    protected void setNodeId (String nodeId)
-    {
-        _nodeId = nodeId;
-    }
-    
-
-    /* ------------------------------------------------------------ */
-    protected boolean access(long time)
-    {
-        synchronized(this)
-        {
-            if (_invalid)
-                return false;
-            _newSession=false;
-            _lastAccessed=_accessed;
-            _accessed=time;
-
-            if (checkExpiry(time))
-            {
-                invalidate();
-                return false;
-            }
-            _requests++;
-            return true;
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    protected void complete()
-    {
-        synchronized(this)
-        {
-            _requests--;
-            if (_doInvalidate && _requests<=0  )
-                doInvalidate();
-        }
-    }
-
-
-    /* ------------------------------------------------------------- */
-    protected void timeout() throws IllegalStateException
-    {
-        // remove session from context and invalidate other sessions with same ID.
-        _manager.removeSession(this,true);
-
-        // Notify listeners and unbind values
-        boolean do_invalidate=false;
-        synchronized (this)
-        {
-            if (!_invalid)
-            {
-                if (_requests<=0)
-                    do_invalidate=true;
-                else
-                    _doInvalidate=true;
-            }
-        }
-        if (do_invalidate)
-            doInvalidate();
-    }
-
-    /* ------------------------------------------------------------- */
-    @Override
-    public void invalidate() throws IllegalStateException
-    {
-        checkValid();
-        // remove session from context and invalidate other sessions with same ID.
-        _manager.removeSession(this,true);
-        doInvalidate();
-    }
-
-    /* ------------------------------------------------------------- */
-    protected void doInvalidate() throws IllegalStateException
-    {
-        try
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("invalidate {}",_clusterId);
-            if (isValid())
-                clearAttributes();
-        }
-        finally
-        {
-            synchronized (this)
-            {
-                // mark as invalid
-                _invalid=true;
-            }
-        }
-    }
-
-    /* ------------------------------------------------------------- */
-    public abstract void clearAttributes();
-   
-
-    /* ------------------------------------------------------------- */
-    public boolean isIdChanged()
-    {
-        return _idChanged;
-    }
-
-    /* ------------------------------------------------------------- */
-    @Override
-    public boolean isNew() throws IllegalStateException
-    {
-        checkValid();
-        return _newSession;
-    }
-
-    /* ------------------------------------------------------------- */
-    /**
-     * @deprecated As of Version 2.2, this method is replaced by
-     *             {@link #setAttribute}
-     */
-    @Deprecated
-    @Override
-    public void putValue(java.lang.String name, java.lang.Object value) throws IllegalStateException
-    {
-        changeAttribute(name,value);
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public void removeAttribute(String name)
-    {
-        setAttribute(name,null);
-    }
-
-    /* ------------------------------------------------------------- */
-    /**
-     * @deprecated As of Version 2.2, this method is replaced by
-     *             {@link #removeAttribute}
-     */
-    @Deprecated
-    @Override
-    public void removeValue(java.lang.String name) throws IllegalStateException
-    {
-        removeAttribute(name);
-    }
-    
-    /* ------------------------------------------------------------ */
-    @Override
-    public Enumeration<String> getAttributeNames()
-    {
-        synchronized (this)
-        {
-            checkValid();
-            return doGetAttributeNames();
-        }
-    }
-    
-    /* ------------------------------------------------------------- */
-    /**
-     * @deprecated As of Version 2.2, this method is replaced by
-     *             {@link #getAttributeNames}
-     */
-    @Deprecated
-    @Override
-    public String[] getValueNames() throws IllegalStateException
-    {
-        synchronized(this)
-        {
-            checkValid();
-            Enumeration<String> anames = doGetAttributeNames();
-            if (anames == null)
-                return new String[0];
-            ArrayList<String> names = new ArrayList<String>();
-            while (anames.hasMoreElements())
-                names.add(anames.nextElement());
-            return names.toArray(new String[names.size()]);
-        }
-    }
-    
-
-    /* ------------------------------------------------------------ */
-    public abstract Object doPutOrRemove(String name, Object value);
- 
-
-    /* ------------------------------------------------------------ */
-    public abstract Object doGet(String name);
-    
-    
-    /* ------------------------------------------------------------ */
-    public abstract Enumeration<String> doGetAttributeNames();
-    
-    
-    /* ------------------------------------------------------------ */
-    @Override
-    public Object getAttribute(String name)
-    {
-        synchronized (this)
-        {
-            checkValid();
-            return doGet(name);
-        }
-    }
-   
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public void setAttribute(String name, Object value)
-    {
-        changeAttribute(name,value);
-    }
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * @param name the name of the attribute
-     * @param value the value of the attribute
-     * @return true if attribute changed
-     * @deprecated use changeAttribute(String,Object) instead
-     */
-    @Deprecated
-    protected boolean updateAttribute (String name, Object value)
-    {
-        Object old=null;
-        synchronized (this)
-        {
-            checkValid();
-            old=doPutOrRemove(name,value);
-        }
-
-        if (value==null || !value.equals(old))
-        {
-            if (old!=null)
-                unbindValue(name,old);
-            if (value!=null)
-                bindValue(name,value);
-
-            _manager.doSessionAttributeListeners(this,name,old,value);
-            return true;
-        }
-        return false;
-    }
-    
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * Either set (perhaps replace) or remove the value of the attribute
-     * in the session. The appropriate session attribute listeners are
-     * also called.
-     * 
-     * @param name the name of the attribute
-     * @param value the value of the attribute
-     * @return the old value for the attribute
-     */
-    protected Object changeAttribute (String name, Object value)
-    {
-        Object old=null;
-        synchronized (this)
-        {
-            checkValid();
-            old=doPutOrRemove(name,value);
-        }
-
-        callSessionAttributeListeners(name, value, old);
-
-        return old;
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Call binding and attribute listeners based on the new and old
-     * values of the attribute.
-     * 
-     * @param name name of the attribute
-     * @param newValue  new value of the attribute
-     * @param oldValue previous value of the attribute
-     */
-    protected void callSessionAttributeListeners (String name, Object newValue, Object oldValue)
-    {
-        if (newValue==null || !newValue.equals(oldValue))
-        {
-            if (oldValue!=null)
-                unbindValue(name,oldValue);
-            if (newValue!=null)
-                bindValue(name,newValue);
-
-            _manager.doSessionAttributeListeners(this,name,oldValue,newValue);
-        }
-    }
-  
-
-    /* ------------------------------------------------------------- */
-    public void setIdChanged(boolean changed)
-    {
-        _idChanged=changed;
-    }
-
-    /* ------------------------------------------------------------- */
-    @Override
-    public void setMaxInactiveInterval(int secs)
-    {
-        _maxIdleMs=(long)secs*1000L;
-    }
-
-    /* ------------------------------------------------------------- */
-    @Override
-    public String toString()
-    {
-        return this.getClass().getName()+":"+getId()+"@"+hashCode();
-    }
-
-    /* ------------------------------------------------------------- */
-    /** 
-     * Bind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)}) 
-     * @param name the name with which the object is bound or unbound  
-     * @param value the bound value
-     */
-    public void bindValue(java.lang.String name, Object value)
-    {
-        if (value!=null&&value instanceof HttpSessionBindingListener)
-            ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this,name));
-    }
-
-    /* ------------------------------------------------------------ */
-    public boolean isValid()
-    {
-        return !_invalid;
-    }
-
-    /* ------------------------------------------------------------- */
-    protected void cookieSet()
-    {
-        synchronized (this)
-        {
-            _cookieSet=_accessed;
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    public int getRequests()
-    {
-        synchronized (this)
-        {
-            return _requests;
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    public void setRequests(int requests)
-    {
-        synchronized (this)
-        {
-            _requests=requests;
-        }
-    }
-
-    /* ------------------------------------------------------------- */
-    /**
-     * Unbind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)}) 
-     * @param name the name with which the object is bound or unbound  
-     * @param value the bound value
-     */
-    public void unbindValue(java.lang.String name, Object value)
-    {
-        if (value!=null&&value instanceof HttpSessionBindingListener)
-            ((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this,name));
-    }
-
-    /* ------------------------------------------------------------- */
-    public void willPassivate()
-    {
-        synchronized(this)
-        {
-            HttpSessionEvent event = new HttpSessionEvent(this);
-            for (Iterator<Object> iter = getAttributeMap().values().iterator(); iter.hasNext();)
-            {
-                Object value = iter.next();
-                if (value instanceof HttpSessionActivationListener)
-                {
-                    HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
-                    listener.sessionWillPassivate(event);
-                }
-            }
-        }
-    }
-
-    /* ------------------------------------------------------------- */
-    public void didActivate()
-    {
-        synchronized(this)
-        {
-            HttpSessionEvent event = new HttpSessionEvent(this);
-            for (Iterator<Object> iter = getAttributeMap().values().iterator(); iter.hasNext();)
-            {
-                Object value = iter.next();
-                if (value instanceof HttpSessionActivationListener)
-                {
-                    HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
-                    listener.sessionDidActivate(event);
-                }
-            }
-        }
-    }
-
-
-}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java
new file mode 100644
index 0000000..3ea77d9
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java
@@ -0,0 +1,101 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+
+/**
+ * AbstractSessionDataStore
+ *
+ *
+ */
+public abstract class AbstractSessionDataStore extends AbstractLifeCycle implements SessionDataStore
+{
+    protected SessionContext _context; //context associated with this session data store
+
+
+    public abstract void doStore(String id, SessionData data, boolean isNew) throws Exception;
+
+   
+
+    
+    public void initialize (SessionContext context)
+    {
+        if (isStarted())
+            throw new IllegalStateException("Context set after SessionDataStore started");
+        _context = context;
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#store(java.lang.String, org.eclipse.jetty.server.session.SessionData)
+     */
+    @Override
+    public void store(String id, SessionData data) throws Exception
+    {
+        long lastSave = data.getLastSaved();
+        
+        data.setLastSaved(System.currentTimeMillis());
+        try
+        {
+            doStore(id, data, (lastSave<=0));
+        }
+        catch (Exception e)
+        {
+            //reset last save time
+            data.setLastSaved(lastSave);
+        }
+        finally
+        {
+            data.setDirty(false);
+        }
+    }
+    
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#newSessionData(org.eclipse.jetty.server.session.SessionKey, long, long, long, long)
+     */
+    @Override
+    public SessionData newSessionData(String id, long created, long accessed, long lastAccessed, long maxInactiveMs)
+    {
+        return new SessionData(id, _context.getCanonicalContextPath(), _context.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
+    }
+ 
+    protected void checkStarted () throws IllegalStateException
+    {
+        if (isStarted())
+            throw new IllegalStateException("Already started");
+    }
+
+
+
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (_context == null)
+            throw new IllegalStateException ("No SessionContext");
+        
+        super.doStart();
+    }
+    
+    
+    
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java
index 07a6f7f..d8cca44 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionIdManager.java
@@ -19,19 +19,34 @@
 package org.eclipse.jetty.server.session;
 
 import java.security.SecureRandom;
+import java.util.HashSet;
 import java.util.Random;
+import java.util.Set;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.SessionIdManager;
+import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.util.component.AbstractLifeCycle;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 
+
+
+/**
+ * AbstractSessionIdManager
+ * 
+ * Manages session ids to ensure each session id within a context is unique, and that
+ * session ids can be shared across contexts (but not session contents).
+ * 
+ * There is only 1 session id manager per Server instance.
+ */
 public abstract class AbstractSessionIdManager extends AbstractLifeCycle implements SessionIdManager
 {
-    private static final Logger LOG = Log.getLogger(AbstractSessionIdManager.class);
-
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
     private final static String __NEW_SESSION_ID="org.eclipse.jetty.server.newSessionId";
 
     protected Random _random;
@@ -39,18 +54,49 @@
     protected String _workerName;
     protected String _workerAttr;
     protected long _reseed=100000L;
+    protected Server _server;
+    protected SessionScavenger _scavenger;
 
     /* ------------------------------------------------------------ */
-    public AbstractSessionIdManager()
+    public AbstractSessionIdManager(Server server)
     {
+        _server = server;
     }
 
     /* ------------------------------------------------------------ */
-    public AbstractSessionIdManager(Random random)
+    public AbstractSessionIdManager(Server server, Random random)
     {
+        this(server);
         _random=random;
     }
 
+    /* ------------------------------------------------------------ */
+    public void setServer (Server server)
+    {
+        _server = server;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+
+    public Server getServer ()
+    {
+        return _server;
+    }
+
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param scavenger
+     */
+    public void setSessionScavenger (SessionScavenger scavenger)
+    {
+        _scavenger = scavenger;
+        _scavenger.setSessionIdManager(this);
+    }
+   
+    
 
     /* ------------------------------------------------------------ */
     /**
@@ -65,6 +111,8 @@
         return _workerName;
     }
 
+    
+    
     /* ------------------------------------------------------------ */
     /**
      * Set the workername. If set, the workername is dot appended to the session
@@ -133,14 +181,14 @@
             String requested_id=request.getRequestedSessionId();
             if (requested_id!=null)
             {
-                String cluster_id=getClusterId(requested_id);
-                if (idInUse(cluster_id))
+                String cluster_id=getId(requested_id);
+                if (isIdInUse(cluster_id))
                     return cluster_id;
             }
 
             // Else reuse any new session ID already defined for this request.
             String new_id=(String)request.getAttribute(__NEW_SESSION_ID);
-            if (new_id!=null&&idInUse(new_id))
+            if (new_id!=null&&isIdInUse(new_id))
                 return new_id;
 
             // pick a new unique ID!
@@ -156,7 +204,7 @@
     {
         // pick a new unique ID!
         String id=null;
-        while (id==null||id.length()==0||idInUse(id))
+        while (id==null||id.length()==0||isIdInUse(id))
         {
             long r0=_weakRandom
                     ?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^((seedTerm)<<32))
@@ -198,23 +246,32 @@
     }
 
 
-    /* ------------------------------------------------------------ */
-    @Override
-    public abstract void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request);
 
     
     /* ------------------------------------------------------------ */
     @Override
     protected void doStart() throws Exception
     {
+        if (_server == null)
+            throw new IllegalStateException("No Server for SessionIdManager");
        initRandom();
        _workerAttr=(_workerName!=null && _workerName.startsWith("$"))?_workerName.substring(1):null;
+       
+       if (_scavenger == null)
+       {
+           LOG.warn("No SessionScavenger set, using defaults");
+           _scavenger = new SessionScavenger();
+           _scavenger.setSessionIdManager(this);
+       }
+       
+       _scavenger.start();
     }
 
     /* ------------------------------------------------------------ */
     @Override
     protected void doStop() throws Exception
     {
+        _scavenger.stop();
     }
 
     /* ------------------------------------------------------------ */
@@ -242,6 +299,8 @@
             _random.setSeed(_random.nextLong()^System.currentTimeMillis()^hashCode()^Runtime.getRuntime().freeMemory());
     }
 
+    
+    /* ------------------------------------------------------------ */
     /** Get the session ID with any worker ID.
      *
      * @param clusterId the cluster id
@@ -249,7 +308,7 @@
      * @return sessionId plus any worker ID.
      */
     @Override
-    public String getNodeId(String clusterId, HttpServletRequest request)
+    public String getExtendedId(String clusterId, HttpServletRequest request)
     {
         if (_workerName!=null)
         {
@@ -264,17 +323,106 @@
         return clusterId;
     }
 
+    
+    /* ------------------------------------------------------------ */
     /** Get the session ID without any worker ID.
      *
-     * @param nodeId the node id
+     * @param extendedId the session id with the worker extension
      * @return sessionId without any worker ID.
      */
     @Override
-    public String getClusterId(String nodeId)
+    public String getId(String extendedId)
     {
-        int dot=nodeId.lastIndexOf('.');
-        return (dot>0)?nodeId.substring(0,dot):nodeId;
+        int dot=extendedId.lastIndexOf('.');
+        return (dot>0)?extendedId.substring(0,dot):extendedId;
     }
 
+    
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * Remove an id from use by telling all contexts to remove a session with this id.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#expireAll(java.lang.String)
+     */
+    @Override
+    public void expireAll(String id)
+    {
+        //take the id out of the list of known sessionids for this node
+        if (removeId(id))
+        {
+            //tell all contexts that may have a session object with this id to
+            //get rid of them
+            for (SessionManager manager:getSessionManagers())
+            {
+                manager.invalidate(id);
+            }
+        }
+    }
 
+    /* ------------------------------------------------------------ */
+    /**
+     * @param id
+     */
+    public void invalidateAll (String id)
+    {
+        //take the id out of the list of known sessionids for this node
+        if (removeId(id))
+        {
+            //tell all contexts that may have a session object with this id to
+            //get rid of them
+            for (SessionManager manager:getSessionManagers())
+            {         
+                manager.invalidate(id);
+            } 
+        }
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Generate a new id for a session and update across
+     * all SessionManagers.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#renewSessionId(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest)
+     */
+    @Override
+    public void renewSessionId (String oldClusterId, String oldNodeId, HttpServletRequest request)
+    { 
+        //generate a new id
+        String newClusterId = newSessionId(request.hashCode());
+        
+        removeId(oldClusterId);//remove the old one from the list
+
+        //tell all contexts to update the id 
+        for (SessionManager manager:getSessionManagers())
+        {
+            manager.renewSessionId(oldClusterId, oldNodeId, newClusterId, getExtendedId(newClusterId, request));
+        }
+    }
+    
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Get SessionManager for every context.
+     * 
+     * @return
+     */
+    protected Set<SessionManager> getSessionManagers()
+    {
+        Set<SessionManager> managers = new HashSet<>();
+
+        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 = (SessionManager)sessionHandler.getSessionManager();
+
+                if (manager != null)
+                    managers.add(manager);
+            }
+        }
+        return managers;
+    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionStore.java
new file mode 100644
index 0000000..cffb7e7
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionStore.java
@@ -0,0 +1,328 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Locker.Lock;
+
+/**
+ * AbstractSessionStore
+ *
+ * Basic behaviour for maintaining an in-memory store of Session objects and 
+ * making sure that any backing SessionDataStore is kept in sync.
+ */
+public abstract class AbstractSessionStore extends AbstractLifeCycle implements SessionStore
+{
+    final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
+    protected SessionDataStore _sessionDataStore;
+    protected StalenessStrategy _staleStrategy;
+    protected SessionManager _manager;
+    protected SessionContext _context;
+
+
+    
+
+    /**
+     * Create a new Session object from session data
+     * @param data
+     * @return
+     */
+    public abstract Session newSession (SessionData data);
+
+    
+    
+    /**
+     * Get the session matching the key
+     * @param id session id
+     * @return
+     */
+    public abstract Session doGet(String id);
+    
+    
+    
+    /**
+     * Put the session into the map if it wasn't already there
+     * 
+     * @param id the identity of the session
+     * @param session the session object
+     * @return null if the session wasn't already in the map, or the existing entry otherwise
+     */
+    public abstract Session doPutIfAbsent (String id, Session session);
+    
+    
+    
+    /**
+     * Check to see if the session exists in the store
+     * @param id
+     * @return
+     */
+    public abstract boolean doExists (String id);
+    
+    
+    
+    /**
+     * Remove the session with this identity from the store
+     * @param id
+     * @return true if removed false otherwise
+     */
+    public abstract Session doDelete (String id);
+    
+    
+    
+    
+    /**
+     * Get a list of keys for sessions that the store thinks has expired
+     * @return
+     */
+    public abstract Set<String> doGetExpiredCandidates();
+    
+    
+    
+    
+    /**
+     * 
+     */
+    public AbstractSessionStore ()
+    {
+    }
+    
+    
+    public void setSessionManager (SessionManager manager)
+    {
+        _manager = manager;
+    }
+    
+    public SessionManager getSessionManager()
+    {
+        return _manager;
+    }
+    
+    
+
+    public void initialize (SessionContext context)
+    {
+        if (isStarted())
+            throw new IllegalStateException("Context set after session store started");
+        _context = context;
+    }
+    
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (_sessionDataStore == null)
+            throw new IllegalStateException ("No session data store configured");
+        
+        if (_manager == null)
+            throw new IllegalStateException ("No session manager");
+        
+        if (_context == null)
+            throw new IllegalStateException ("No ContextId");
+        
+        _sessionDataStore.initialize(_context);
+        _sessionDataStore.start();
+        
+        super.doStart();
+    }
+
+    @Override
+    protected void doStop() throws Exception
+    {
+        _sessionDataStore.stop();
+        super.doStop();
+    }
+
+    public SessionDataStore getSessionDataStore()
+    {
+        return _sessionDataStore;
+    }
+
+    public void setSessionDataStore(SessionDataStore sessionDataStore)
+    {
+        _sessionDataStore = sessionDataStore;
+    }
+    
+    public StalenessStrategy getStaleStrategy()
+    {
+        return _staleStrategy;
+    }
+
+    public void setStaleStrategy(StalenessStrategy staleStrategy)
+    {
+        _staleStrategy = staleStrategy;
+    }
+
+    /** 
+     * Get a session object.
+     * 
+     * If the session object is not in this session store, try getting
+     * the data for it from a SessionDataStore associated with the 
+     * session manager.
+     * 
+     * @see org.eclipse.jetty.server.session.SessionStore#get(java.lang.String)
+     */
+    @Override
+    public Session get(String id, boolean staleCheck) throws Exception
+    {
+        //look locally
+        Session session = doGet(id);
+        
+        
+        if (staleCheck && isStale(session))
+        {
+            //delete from session store so should reload from session data store
+            doDelete(id);
+            session = null;
+        }
+        
+        //not in session store, load the data for the session if possible
+        if (session == null && _sessionDataStore != null)
+        {
+            SessionData data = _sessionDataStore.load(id);
+            if (data != null)
+            {
+                session = newSession(data);
+                session.setSessionManager(_manager);
+                Session existing = doPutIfAbsent(id, session);
+                if (existing != null)
+                {
+                    //some other thread has got in first and added the session
+                    //so use it
+                    session = existing;
+                }
+            }
+        }
+        return session;
+    }
+
+    /** 
+     * Put the Session object into the session store. 
+     * If the session manager supports a session data store, write the
+     * session data through to the session data store.
+     * 
+     * @see org.eclipse.jetty.server.session.SessionStore#put(java.lang.String, org.eclipse.jetty.server.session.Session)
+     */
+    @Override
+    public void put(String id, Session session) throws Exception
+    {
+        if (id == null || session == null)
+            throw new IllegalArgumentException ("Put key="+id+" session="+(session==null?"null":session.getId()));
+        
+        session.setSessionManager(_manager);
+
+        //if the session is new, the data has changed, or the cache is considered stale, write it to any backing store
+        try (Lock lock = session.lock())
+        {
+            if ((session.isNew() || session.getSessionData().isDirty() || isStale(session)) && _sessionDataStore != null)
+            {
+                if (_sessionDataStore.isPassivating())
+                {
+                    session.willPassivate();
+                    try
+                    {
+                        _sessionDataStore.store(id, session.getSessionData());
+                    }
+                    finally
+                    {
+                        session.didActivate();
+                    }
+                }
+                else
+                    _sessionDataStore.store(id, session.getSessionData());
+            }
+
+        }
+
+        doPutIfAbsent(id,session);
+    }
+
+    /** 
+     * Check to see if the session object exists in this store.
+     * 
+     * @see org.eclipse.jetty.server.session.SessionStore#exists(java.lang.String)
+     */
+    @Override
+    public boolean exists(String id)
+    {
+        return doExists(id);
+    }
+
+
+    /** 
+     * Remove a session object from this store and from any backing store.
+     * 
+     * @see org.eclipse.jetty.server.session.SessionStore#delete(java.lang.String)
+     */
+    @Override
+    public Session delete(String id) throws Exception
+    {
+        if (_sessionDataStore != null)
+        {
+            boolean dsdel = _sessionDataStore.delete(id);
+            if (LOG.isDebugEnabled()) LOG.debug("Session {} deleted in db {}",id, dsdel);                   
+        }
+        return doDelete(id);
+    }
+
+    
+    
+    /**
+     * @param session
+     * @return
+     */
+    public boolean isStale (Session session)
+    {
+        if (_staleStrategy != null)
+            return _staleStrategy.isStale(session);
+        return false;
+    }
+    
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionStore#getExpired()
+     */
+    @Override
+    public Set<String> getExpired()
+    {
+       if (!isStarted())
+           return Collections.emptySet();
+       Set<String> candidates = doGetExpiredCandidates();
+       return _sessionDataStore.getExpired(candidates);
+    }
+
+
+
+
+
+    @Override
+    public Session newSession(HttpServletRequest request, String id, long time, long maxInactiveMs)
+    {
+        return null;
+    }
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AlwaysStaleStrategy.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AlwaysStaleStrategy.java
new file mode 100644
index 0000000..264ea64
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AlwaysStaleStrategy.java
@@ -0,0 +1,39 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+/**
+ * AlwaysStale
+ *
+ *
+ */
+public class AlwaysStaleStrategy implements StalenessStrategy
+{
+
+    /** 
+     * @see org.eclipse.jetty.server.session.StalenessStrategy#isStale(org.eclipse.jetty.server.session.Session)
+     */
+    @Override
+    public boolean isStale(Session session)
+    {
+       return true;
+    }
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/CachingSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/CachingSessionDataStore.java
new file mode 100644
index 0000000..400834a
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/CachingSessionDataStore.java
@@ -0,0 +1,170 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.util.Set;
+
+/**
+ * CachingSessionDataStore
+ *
+ * A SessionDataStore is a mechanism for (persistently) storing data associated with sessions.
+ * This implementation delegates to a pluggable SessionDataStore for actually storing the 
+ * session data. It also uses a pluggable JCache implementation in front of the 
+ * delegate SessionDataStore to improve performance: accessing most persistent store
+ * technology can be expensive time-wise, so introducing a fronting cache 
+ * can increase performance. The cache implementation can either be a local cache, 
+ * a remote cache, or a clustered cache.
+ */
+public class CachingSessionDataStore extends AbstractSessionDataStore
+{
+
+    public interface SessionDataCache
+    {
+        public SessionData get (String id); //get mapped value
+        public boolean putIfAbsent (String id, SessionData data); //only insert if no mapping for key already
+        public boolean remove (String id); //remove the mapping for key, returns false if no mapping
+        public void put (String id, SessionData data); //overwrite or add the mapping
+        public void initialize(SessionContext context);
+    }
+    
+    
+    protected SessionDataStore _delegateDataStore;
+    protected SessionDataCache _cache;
+    
+    
+    public void setSessionDataStore (SessionDataStore store)
+    {
+        checkStarted();
+        _delegateDataStore = store;
+    }
+    
+    public SessionDataStore getSessionDataStore()
+    {
+        return _delegateDataStore;
+    }
+    
+    
+    public void setSessionDataCache (SessionDataCache cache)
+    {
+        checkStarted();
+        _cache = cache;
+    }
+    
+    public SessionDataCache getSessionDataCache ()
+    {
+        return _cache;
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#load(org.eclipse.jetty.server.session.SessionKey)
+     */
+    @Override
+    public SessionData load(String id) throws Exception
+    {
+        //check to see if the session data is already in our cache
+        SessionData d = _cache.get(id);
+        if (d == null)
+        {
+            //not in the cache, go get it from the store
+           d =  _delegateDataStore.load(id);
+           
+           //put it into the cache, unless another thread/node has put it into the cache
+          boolean inserted = _cache.putIfAbsent(id, d);
+          if (!inserted)
+          {
+              //some other thread/node put this data into the cache, so get it from there
+              SessionData d2 = _cache.get(id);
+              
+              if (d2 != null)
+                  d = d2;
+             //else: The cache either timed out the entry, or maybe the session data was being removed, and we're about to resurrect it!
+          }
+        }
+        return d;
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#delete(org.eclipse.jetty.server.session.SessionKey)
+     */
+    @Override
+    public boolean delete(String id) throws Exception
+    {
+        //delete from the store and from the cache
+        _delegateDataStore.delete(id);
+        _cache.remove(id);
+        //TODO need to check removal at each level?
+        return false;
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(java.util.Set)
+     */
+    @Override
+    public Set<String> getExpired(Set<String> candidates)
+    {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    /** 
+     * @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
+    {
+        //write to the SessionDataStore first
+        if (_delegateDataStore instanceof AbstractSessionDataStore)
+            ((AbstractSessionDataStore)_delegateDataStore).doStore(id, data, isNew);
+
+        //else??????
+        
+        //then update the cache with written data
+        _cache.put(id,data);
+
+    }
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        _cache.initialize(_context);
+        _delegateDataStore.initialize(_context);
+        super.doStart();
+    }
+
+    @Override
+    protected void doStop() throws Exception
+    {
+        // TODO Auto-generated method stub
+        super.doStop();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
+     */
+    @Override
+    public boolean isPassivating()
+    {
+       return true;
+    }
+
+    
+    
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/DatabaseAdaptor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/DatabaseAdaptor.java
new file mode 100644
index 0000000..4c2cb7c
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/DatabaseAdaptor.java
@@ -0,0 +1,270 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Locale;
+
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.sql.DataSource;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * DatabaseAdaptor
+ *
+ * Handles differences between databases.
+ *
+ * Postgres uses the getBytes and setBinaryStream methods to access
+ * a "bytea" datatype, which can be up to 1Gb of binary data. MySQL
+ * is happy to use the "blob" type and getBlob() methods instead.
+ *
+ */
+public class DatabaseAdaptor
+{
+    final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
+    String _dbName;
+    boolean _isLower;
+    boolean _isUpper;
+    
+    protected String _blobType; //if not set, is deduced from the type of the database at runtime
+    protected String _longType; //if not set, is deduced from the type of the database at runtime
+    private String _driverClassName;
+    private String _connectionUrl;
+    private Driver _driver;
+    private DataSource _datasource;
+    private String _jndiName;
+
+
+    public DatabaseAdaptor ()
+    {           
+    }
+    
+    
+    public void adaptTo(DatabaseMetaData dbMeta)  
+    throws SQLException
+    {
+        _dbName = dbMeta.getDatabaseProductName().toLowerCase(Locale.ENGLISH);
+        if (LOG.isDebugEnabled())
+            LOG.debug ("Using database {}",_dbName);
+        _isLower = dbMeta.storesLowerCaseIdentifiers();
+        _isUpper = dbMeta.storesUpperCaseIdentifiers(); 
+    }
+    
+
+    public void setBlobType(String blobType)
+    {
+        _blobType = blobType;
+    }
+    
+    public String getBlobType ()
+    {
+        if (_blobType != null)
+            return _blobType;
+
+        if (_dbName.startsWith("postgres"))
+            return "bytea";
+
+        return "blob";
+    }
+    
+
+    public void setLongType(String longType)
+    {
+        _longType = longType;
+    }
+    
+
+    public String getLongType ()
+    {
+        if (_longType != null)
+            return _longType;
+
+        if (_dbName == null)
+            throw new IllegalStateException ("DbAdaptor missing metadata");
+        
+        if (_dbName.startsWith("oracle"))
+            return "number(20)";
+
+        return "bigint";
+    }
+    
+
+    /**
+     * Convert a camel case identifier into either upper or lower
+     * depending on the way the db stores identifiers.
+     *
+     * @param identifier the raw identifier
+     * @return the converted identifier
+     */
+    public String convertIdentifier (String identifier)
+    {
+        if (_dbName == null)
+            throw new IllegalStateException ("DbAdaptor missing metadata");
+        
+        if (_isLower)
+            return identifier.toLowerCase(Locale.ENGLISH);
+        if (_isUpper)
+            return identifier.toUpperCase(Locale.ENGLISH);
+
+        return identifier;
+    }
+
+    
+    public String getDBName ()
+    {
+        return _dbName;
+    }
+
+
+    public InputStream getBlobInputStream (ResultSet result, String columnName)
+    throws SQLException
+    {
+        if (_dbName == null)
+            throw new IllegalStateException ("DbAdaptor missing metadata");
+        
+        if (_dbName.startsWith("postgres"))
+        {
+            byte[] bytes = result.getBytes(columnName);
+            return new ByteArrayInputStream(bytes);
+        }
+
+        Blob blob = result.getBlob(columnName);
+        return blob.getBinaryStream();
+    }
+
+
+    public boolean isEmptyStringNull ()
+    {
+        if (_dbName == null)
+            throw new IllegalStateException ("DbAdaptor missing metadata");
+        
+        return (_dbName.startsWith("oracle"));
+    }
+    
+    /**
+     * rowId is a reserved word for Oracle, so change the name of this column
+     * @return true if db in use is oracle
+     */
+    public boolean isRowIdReserved ()
+    {
+        if (_dbName == null)
+            throw new IllegalStateException ("DbAdaptor missing metadata");
+        
+        return (_dbName != null && _dbName.startsWith("oracle"));
+    }
+
+    /**
+     * Configure jdbc connection information via a jdbc Driver
+     *
+     * @param driverClassName the driver classname
+     * @param connectionUrl the driver connection url
+     */
+    public void setDriverInfo (String driverClassName, String connectionUrl)
+    {
+        _driverClassName=driverClassName;
+        _connectionUrl=connectionUrl;
+    }
+
+    /**
+     * Configure jdbc connection information via a jdbc Driver
+     *
+     * @param driverClass the driver class
+     * @param connectionUrl the driver connection url
+     */
+    public void setDriverInfo (Driver driverClass, String connectionUrl)
+    {
+        _driver=driverClass;
+        _connectionUrl=connectionUrl;
+    }
+
+
+    public void setDatasource (DataSource ds)
+    {
+        _datasource = ds;
+    }
+    
+    public void setDatasourceName (String jndi)
+    {
+        _jndiName=jndi;
+    }
+
+    public void initialize ()
+    throws Exception
+    {
+        if (_datasource != null)
+            return; //already set up
+        
+        if (_jndiName!=null)
+        {
+            InitialContext ic = new InitialContext();
+            _datasource = (DataSource)ic.lookup(_jndiName);
+        }
+        else if ( _driver != null && _connectionUrl != null )
+        {
+            DriverManager.registerDriver(_driver);
+        }
+        else if (_driverClassName != null && _connectionUrl != null)
+        {
+            Class.forName(_driverClassName);
+        }
+        else
+        {
+            try
+            {
+                InitialContext ic = new InitialContext();
+                _datasource = (DataSource)ic.lookup("jdbc/sessions"); //last ditch effort
+            }
+            catch (NamingException e)
+            {
+                throw new IllegalStateException("No database configured for sessions");
+            }
+        }
+    }
+    
+   
+    
+    /**
+     * Get a connection from the driver or datasource.
+     *
+     * @return the connection for the datasource
+     * @throws SQLException if unable to get the connection
+     */
+    protected Connection getConnection ()
+    throws SQLException
+    {
+        if (_datasource != null)
+            return _datasource.getConnection();
+        else
+            return DriverManager.getConnection(_connectionUrl);
+    }
+    
+}
\ No newline at end of file
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionDataStore.java
new file mode 100644
index 0000000..30fb64d
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionDataStore.java
@@ -0,0 +1,312 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * FileSessionDataStore
+ *
+ * A file-based store of session data.
+ */
+public class FileSessionDataStore extends AbstractSessionDataStore
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    private File _storeDir;
+    private boolean _deleteUnrestorableFiles = false;
+    
+
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        initializeStore();
+        super.doStart();
+    }
+
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+    }
+
+    public File getStoreDir()
+    {
+        return _storeDir;
+    }
+
+    public void setStoreDir(File storeDir)
+    {
+        checkStarted();
+        _storeDir = storeDir;
+    }
+
+    public boolean isDeleteUnrestorableFiles()
+    {
+        return _deleteUnrestorableFiles;
+    }
+
+    public void setDeleteUnrestorableFiles(boolean deleteUnrestorableFiles)
+    {
+        checkStarted();
+        _deleteUnrestorableFiles = deleteUnrestorableFiles;
+    }
+
+ 
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#delete(org.eclipse.jetty.server.session.SessionKey)
+     */
+    @Override
+    public boolean delete(String id) throws Exception
+    {   
+        File file = null;
+        if (_storeDir != null)
+        {
+            file = new File(_storeDir, getFileName(id));
+            if (file.exists() && file.getParentFile().equals(_storeDir))
+            {
+                file.delete();
+                return true;
+            }
+        }
+         
+        return false;
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired()
+     */
+    @Override
+    public Set<String> getExpired(Set<String> candidates)
+    {
+        //we don't want to open up each file and check, so just leave it up to the SessionStore
+        return candidates;
+    }
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#load(org.eclipse.jetty.server.session.SessionKey)
+     */
+    @Override
+    public SessionData load(String id) throws Exception
+    {  
+        final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
+        final AtomicReference<Exception> exception = new AtomicReference<Exception>();
+        Runnable r = new Runnable()
+        {
+            public void run ()
+            {
+                File file = new File(_storeDir,getFileName(id));
+
+                if (!file.exists())
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("No file: {}",file);
+                    return;
+                }
+
+                try (FileInputStream in = new FileInputStream(file))
+                {
+                    SessionData data = load(in);
+                    //delete restored file
+                    file.delete();
+                    reference.set(data);
+                }
+                catch (UnreadableSessionDataException e)
+                {
+                    if (isDeleteUnrestorableFiles() && file.exists() && file.getParentFile().equals(_storeDir));
+                    {
+                        file.delete();
+                        LOG.warn("Deleted unrestorable file for session {}", id);
+                    }
+                    exception.set(e);
+                }
+                catch (Exception e)
+                {
+                    exception.set(e);
+                }
+            }
+        };
+        //ensure this runs with the context classloader set
+        _context.run(r);
+        
+        if (exception.get() != null)
+            throw exception.get();
+        
+        return reference.get();
+    }
+    
+        
+
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore(org.eclipse.jetty.server.session.SessionKey, org.eclipse.jetty.server.session.SessionData)
+     */
+    @Override
+    public void doStore(String id, SessionData data, boolean isNew) throws Exception
+    {
+        File file = null;
+        if (_storeDir != null)
+        {
+            file = new File(_storeDir, getFileName(id));
+            if (file.exists())
+                file.delete();
+
+            try(FileOutputStream fos = new FileOutputStream(file,false))
+            {
+                save(fos, id, data);
+            }
+            catch (Exception e)
+            { 
+                if (file != null) 
+                    file.delete(); // No point keeping the file if we didn't save the whole session
+                throw new UnwriteableSessionDataException(id, _context,e);             
+            }
+        }
+    }
+    
+    public void initializeStore ()
+    {
+        if (_storeDir == null)
+            throw new IllegalStateException("No file store specified");
+
+        if (!_storeDir.exists())
+            _storeDir.mkdirs();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
+     */
+    @Override
+    public boolean isPassivating()
+    {
+        return true;
+    }
+    
+    /* ------------------------------------------------------------ */
+    private void save(OutputStream os, String id, SessionData data)  throws IOException
+    {    
+        DataOutputStream out = new DataOutputStream(os);
+        out.writeUTF(id);
+        out.writeUTF(_context.getCanonicalContextPath());
+        out.writeUTF(_context.getVhost());
+        out.writeUTF(data.getLastNode());
+        out.writeLong(data.getCreated());
+        out.writeLong(data.getAccessed());
+        out.writeLong(data.getLastAccessed());
+        out.writeLong(data.getCookieSet());
+        out.writeLong(data.getExpiry());
+        out.writeLong(data.getMaxInactiveMs());
+        
+        List<String> keys = new ArrayList<String>(data.getKeys());
+        out.writeInt(keys.size());
+        ObjectOutputStream oos = new ObjectOutputStream(out);
+        for (String name:keys)
+        {
+            oos.writeUTF(name);
+            oos.writeObject(data.getAttribute(name));
+        }
+    }
+
+    private String getFileName (String id)
+    {
+        return _context.getCanonicalContextPath()+"_"+_context.getVhost()+"_"+id;
+    }
+
+
+    private SessionData load (InputStream is)
+            throws Exception
+    {
+        String id = null;
+
+        try
+        {
+            SessionData data = null;
+            DataInputStream di = new DataInputStream(is);
+
+            id = di.readUTF();
+            String contextPath = di.readUTF();
+            String vhost = di.readUTF();
+            String lastNode = di.readUTF();
+            long created = di.readLong();
+            long accessed = di.readLong();
+            long lastAccessed = di.readLong();
+            long cookieSet = di.readLong();
+            long expiry = di.readLong();
+            long maxIdle = di.readLong();
+
+            data = newSessionData(id, created, accessed, lastAccessed, maxIdle); 
+            data.setContextPath(contextPath);
+            data.setVhost(vhost);
+            data.setLastNode(lastNode);
+            data.setCookieSet(cookieSet);
+            data.setExpiry(expiry);
+            data.setMaxInactiveMs(maxIdle);
+
+            // Attributes
+            restoreAttributes(di, di.readInt(), data);
+
+            return data;        
+        }
+        catch (Exception e)
+        {
+            throw new UnreadableSessionDataException(id, _context, e);
+        }
+    }
+
+    private void restoreAttributes (InputStream is, int size, SessionData data)
+            throws Exception
+    {
+        if (size>0)
+        {
+            // input stream should not be closed here
+            Map<String,Object> attributes = new HashMap<String,Object>();
+            ClassLoadingObjectInputStream ois =  new ClassLoadingObjectInputStream(is);
+            for (int i=0; i<size;i++)
+            {
+                String key = ois.readUTF();
+                Object value = ois.readObject();
+                attributes.put(key,value);
+            }
+            data.putAllAttributes(attributes);
+        }
+    }
+
+
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionManager.java
new file mode 100644
index 0000000..f107957
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionManager.java
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+/**
+ * FileHashSessionManager
+ *
+ * Session manager that stores its sessions in files on disk
+ * 
+ */
+public class FileSessionManager extends SessionManager
+{
+    protected FileSessionDataStore _sessionDataStore = new FileSessionDataStore();
+
+
+    @Override
+    public void doStart() throws Exception
+    {
+        _sessionStore = new MemorySessionStore();
+        ((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore);
+        
+        super.doStart();
+    }
+
+    @Override
+    public void doStop() throws Exception
+    {
+        super.doStop();
+    }
+    
+    /**
+     * Get the SessionDataStore to configure it
+     * @return
+     */
+    public FileSessionDataStore getSessionDataStore()
+    {
+        return _sessionDataStore;
+    }
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java
index 7f2a489..ca63282 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionIdManager.java
@@ -16,217 +16,63 @@
 //  ========================================================================
 //
 
+
 package org.eclipse.jetty.server.session;
 
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Random;
 import java.util.Set;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.ConcurrentHashSet;
 
-import org.eclipse.jetty.server.SessionIdManager;
-
-/* ------------------------------------------------------------ */
 /**
- * HashSessionIdManager. An in-memory implementation of the session ID manager.
+ * HashSessionIdManager
+ *
+ *
  */
 public class HashSessionIdManager extends AbstractSessionIdManager
 {
-    private final Map<String, Set<WeakReference<HttpSession>>> _sessions = new HashMap<String, Set<WeakReference<HttpSession>>>();
-
-    /* ------------------------------------------------------------ */
-    public HashSessionIdManager()
-    {
-    }
-
-    /* ------------------------------------------------------------ */
-    public HashSessionIdManager(Random random)
-    {
-        super(random);
-    }
-
-    /* ------------------------------------------------------------ */
     /**
-     * @return Collection of String session IDs
+     * @param server
      */
-    public Collection<String> getSessions()
+    public HashSessionIdManager(Server server)
     {
-        return Collections.unmodifiableCollection(_sessions.keySet());
+        super(server);
     }
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @param id the id of the session
-     * @return Collection of Sessions for the passed session ID
-     */
-    public Collection<HttpSession> getSession(String id)
-    {
-        ArrayList<HttpSession> sessions = new ArrayList<HttpSession>();
-        Set<WeakReference<HttpSession>> refs =_sessions.get(id);
-        if (refs!=null)
-        {
-            for (WeakReference<HttpSession> ref: refs)
-            {
-                HttpSession session = ref.get();
-                if (session!=null)
-                    sessions.add(session);
-            }
-        }
-        return sessions;
-    }
-    
-    /* ------------------------------------------------------------ */
-    @Override
-    protected void doStart() throws Exception
-    {
-        super.doStart();
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    protected void doStop() throws Exception
-    {
-        _sessions.clear();
-        super.doStop();
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see SessionIdManager#idInUse(String)
-     */
-    @Override
-    public boolean idInUse(String id)
-    {
-        synchronized (this)
-        {
-            return _sessions.containsKey(id);
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see SessionIdManager#addSession(HttpSession)
-     */
-    @Override
-    public void addSession(HttpSession session)
-    {
-        String id = getClusterId(session.getId());
-        WeakReference<HttpSession> ref = new WeakReference<HttpSession>(session);
-
-        synchronized (this)
-        {
-            Set<WeakReference<HttpSession>> sessions = _sessions.get(id);
-            if (sessions==null)
-            {
-                sessions=new HashSet<WeakReference<HttpSession>>();
-                _sessions.put(id,sessions);
-            }
-            sessions.add(ref);
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see SessionIdManager#removeSession(HttpSession)
-     */
-    @Override
-    public void removeSession(HttpSession session)
-    {
-        String id = getClusterId(session.getId());
-
-        synchronized (this)
-        {
-            Collection<WeakReference<HttpSession>> sessions = _sessions.get(id);
-            if (sessions!=null)
-            {
-                for (Iterator<WeakReference<HttpSession>> iter = sessions.iterator(); iter.hasNext();)
-                {
-                    WeakReference<HttpSession> ref = iter.next();
-                    HttpSession s=ref.get();
-                    if (s==null)
-                    {
-                        iter.remove();
-                        continue;
-                    }
-                    if (s==session)
-                    {
-                        iter.remove();
-                        break;
-                    }
-                }
-                if (sessions.isEmpty())
-                    _sessions.remove(id);
-            }
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see SessionIdManager#invalidateAll(String)
-     */
-    @Override
-    public void invalidateAll(String id)
-    {
-        Collection<WeakReference<HttpSession>> sessions;
-        synchronized (this)
-        {
-            sessions = _sessions.remove(id);
-        }
-
-        if (sessions!=null)
-        {
-            for (WeakReference<HttpSession> ref: sessions)
-            {
-                AbstractSession session=(AbstractSession)ref.get();
-                if (session!=null && session.isValid())
-                    session.invalidate();
-            }
-            sessions.clear();
-        }
-    }
+    private final Set<String> _ids = new ConcurrentHashSet<String>();
     
     
-    /* ------------------------------------------------------------ */
+    /** 
+     * @see org.eclipse.jetty.server.SessionIdManager#isIdInUse(java.lang.String)
+     */
     @Override
-    public void renewSessionId (String oldClusterId, String oldNodeId, HttpServletRequest request)
+    public boolean isIdInUse(String id)
     {
-        //generate a new id
-        String newClusterId = newSessionId(request.hashCode());
+         return _ids.contains(id);
+    }
 
 
-        synchronized (this)
-        {
-            Set<WeakReference<HttpSession>> sessions = _sessions.remove(oldClusterId); //get the list of sessions with same id from other contexts
-            if (sessions!=null)
-            {
-                for (Iterator<WeakReference<HttpSession>> iter = sessions.iterator(); iter.hasNext();)
-                {
-                    WeakReference<HttpSession> ref = iter.next();
-                    HttpSession s = ref.get();
-                    if (s == null)
-                    {
-                        continue;
-                    }
-                    else
-                    {
-                        if (s instanceof AbstractSession)
-                        {
-                            AbstractSession abstractSession = (AbstractSession)s;
-                            abstractSession.getSessionManager().renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
-                        }
-                    }
-                }
-                _sessions.put(newClusterId, sessions);
-            }
-        }
+    
+    /** 
+     * @see org.eclipse.jetty.server.SessionIdManager#useId(java.lang.String)
+     * @param session the session whose id to use
+     */
+    @Override
+    public void useId(Session session)
+    {
+        if (session == null)
+            return;
+        
+       _ids.add(session.getId());
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.SessionIdManager#removeId(java.lang.String)
+     */
+    @Override
+    public boolean removeId(String id)
+    {
+       return _ids.remove(id);
     }
 
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
index 68e5227..6cea7fe 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
@@ -16,681 +16,38 @@
 //  ========================================================================
 //
 
+
 package org.eclipse.jetty.server.session;
 
-import java.io.DataInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.TimeUnit;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
-import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
-import org.eclipse.jetty.util.thread.Scheduler;
-
-
-/* ------------------------------------------------------------ */
-/** 
+/**
  * HashSessionManager
- * 
- * An in-memory implementation of SessionManager.
- * <p>
- * This manager supports saving sessions to disk, either periodically or at shutdown.
- * Sessions can also have their content idle saved to disk to reduce the memory overheads of large idle sessions.
- * <p>
- * This manager will create it's own Timer instance to scavenge threads, unless it discovers a shared Timer instance
- * set as the "org.eclipse.jetty.server.session.timer" attribute of the ContextHandler.
  *
+ * In memory-only session manager.
+ * 
  */
-public class HashSessionManager extends AbstractSessionManager
+public class HashSessionManager extends SessionManager
 {
-    final static Logger LOG = SessionHandler.LOG;
-
-    protected final ConcurrentMap<String,HashedSession> _sessions=new ConcurrentHashMap<String,HashedSession>();
-    private Scheduler _timer;
-    private Scheduler.Task _task;
-    long _scavengePeriodMs=30000;
-    long _savePeriodMs=0; //don't do period saves by default
-    long _idleSavePeriodMs = 0; // don't idle save sessions by default.
-    private Scheduler.Task _saveTask;
-    File _storeDir;
-    private boolean _lazyLoad=false;
-    private volatile boolean _sessionsLoaded=false;
-    private boolean _deleteUnrestorableSessions=false;
+    protected NullSessionDataStore _sessionDataStore = new NullSessionDataStore();
 
 
-    /**
-     * Scavenger
-     *
-     */
-    protected class Scavenger implements Runnable
-    {
-        @Override
-        public void run()
-        {
-            try
-            {
-                scavenge();
-            }
-            finally
-            {
-                if (_timer != null && _timer.isRunning()) {
-                    _task = _timer.schedule(this, _scavengePeriodMs, TimeUnit.MILLISECONDS);
-                }
-            }
-        }
-    }
-
-    /**
-     * Saver
-     *
-     */
-    protected class Saver implements Runnable
-    {
-        @Override
-        public void run()
-        {
-            try
-            {
-                saveSessions(true);
-            }
-            catch (Exception e)
-            {       
-                LOG.warn(e);
-            }
-            finally
-            {
-                if (_timer != null && _timer.isRunning())
-                    _saveTask = _timer.schedule(this, _savePeriodMs, TimeUnit.MILLISECONDS);
-            }
-        }        
-    }
-
-
-    /* ------------------------------------------------------------ */
-    public HashSessionManager()
-    {
-        super();
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @see AbstractSessionManager#doStart()
-     */
     @Override
     public void doStart() throws Exception
     {
-        //try shared scheduler from Server first
-        _timer = getSessionHandler().getServer().getBean(Scheduler.class);
-        if (_timer == null)
-        {
-            //try one passed into the context
-            ServletContext context = ContextHandler.getCurrentContext();
-            if (context!=null)
-                _timer = (Scheduler)context.getAttribute("org.eclipse.jetty.server.session.timer");   
-        }    
-      
-        if (_timer == null)
-        {
-            //make a scheduler if none useable
-            _timer=new ScheduledExecutorScheduler(toString()+"Timer",true);
-            addBean(_timer,true);
-        }
-        else
-            addBean(_timer,false);
+        _sessionStore = new MemorySessionStore();
+        ((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore);
         
         super.doStart();
-
-        setScavengePeriod(getScavengePeriod());
-
-        if (_storeDir!=null)
-        {
-            if (!_storeDir.exists())
-                _storeDir.mkdirs();
-
-            if (!_lazyLoad)
-                restoreSessions();
-        }
-
-        setSavePeriod(getSavePeriod());
     }
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @see AbstractSessionManager#doStop()
-     */
     @Override
     public void doStop() throws Exception
     {
-        // stop the scavengers
-        synchronized(this)
-        {
-            if (_saveTask!=null)
-                _saveTask.cancel();
-
-            _saveTask=null;
-            if (_task!=null)
-                _task.cancel();
-            
-            _task=null;
             
             //if we're managing our own timer, remove it
             if (isManaged(_timer))
                removeBean(_timer);
 
-            _timer=null;
-        }
-       
-
-        // This will callback invalidate sessions - where we decide if we will save
         super.doStop();
-
-        _sessions.clear();
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @return the period in seconds at which a check is made for sessions to be invalidated.
-     */
-    public int getScavengePeriod()
-    {
-        return (int)(_scavengePeriodMs/1000);
-    }
-
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public int getSessions()
-    {
-        int sessions=super.getSessions();
-        if (LOG.isDebugEnabled())
-        {
-            if (_sessions.size()!=sessions)
-                LOG.warn("sessions: "+_sessions.size()+"!="+sessions);
-        }
-        return sessions;
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @return seconds Idle period after which a session is saved
-     */
-    public int getIdleSavePeriod()
-    {
-      if (_idleSavePeriodMs <= 0)
-        return 0;
-
-      return (int)(_idleSavePeriodMs / 1000);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Configures the period in seconds after which a session is deemed idle and saved
-     * to save on session memory.
-     *
-     * The session is persisted, the values attribute map is cleared and the session set to idled.
-     *
-     * @param seconds Idle period after which a session is saved
-     */
-    public void setIdleSavePeriod(int seconds)
-    {
-      _idleSavePeriodMs = seconds * 1000L;
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public void setMaxInactiveInterval(int seconds)
-    {
-        super.setMaxInactiveInterval(seconds);
-        if (_dftMaxIdleSecs>0&&_scavengePeriodMs>_dftMaxIdleSecs*1000L)
-            setScavengePeriod((_dftMaxIdleSecs+9)/10);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @param seconds the period is seconds at which sessions are periodically saved to disk
-     */
-    public void setSavePeriod (int seconds)
-    {
-        long period = (seconds * 1000L);
-        if (period < 0)
-            period=0;
-        _savePeriodMs=period;
-
-        if (_timer!=null)
-        {
-            synchronized (this)
-            {
-                if (_saveTask!=null)
-                    _saveTask.cancel();
-                _saveTask = null;
-                if (_savePeriodMs > 0 && _storeDir!=null) //only save if we have a directory configured
-                {
-                    _saveTask = _timer.schedule(new Saver(),_savePeriodMs,TimeUnit.MILLISECONDS);
-                }
-            }
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @return the period in seconds at which sessions are periodically saved to disk
-     */
-    public int getSavePeriod ()
-    {
-        if (_savePeriodMs<=0)
-            return 0;
-
-        return (int)(_savePeriodMs/1000);
-    }
-
-    /* ------------------------------------------------------------ */
-    /**
-     * @param seconds the period in seconds at which a check is made for sessions to be invalidated.
-     */
-    public void setScavengePeriod(int seconds)
-    { 
-        if (seconds==0)
-            seconds=60;
-
-        long old_period=_scavengePeriodMs;
-        long period=seconds*1000L;
-        if (period>60000)
-            period=60000;
-        if (period<1000)
-            period=1000;
-
-        _scavengePeriodMs=period;
-    
-        synchronized (this)
-        {
-            if (_timer!=null && (period!=old_period || _task==null))
-            {
-                if (_task!=null)
-                {
-                    _task.cancel();
-                    _task = null;
-                }
-
-                _task = _timer.schedule(new Scavenger(),_scavengePeriodMs, TimeUnit.MILLISECONDS);
-            }
-        }
-    }
-
-    /* -------------------------------------------------------------- */
-    /**
-     * Find sessions that have timed out and invalidate them. This runs in the
-     * SessionScavenger thread.
-     */
-    protected void scavenge()
-    {
-        //don't attempt to scavenge if we are shutting down
-        if (isStopping() || isStopped())
-            return;
-
-        Thread thread=Thread.currentThread();
-        ClassLoader old_loader=thread.getContextClassLoader();
-        try
-        {      
-            if (_loader!=null)
-                thread.setContextClassLoader(_loader);
-
-            // For each session
-            long now=System.currentTimeMillis();
-            __log.debug("Scavenging sessions at {}", now); 
-            
-            for (Iterator<HashedSession> i=_sessions.values().iterator(); i.hasNext();)
-            {
-                HashedSession session=i.next();
-                long idleTime=session.getMaxInactiveInterval()*1000L; 
-                if (idleTime>0&&session.getAccessed()+idleTime<now)
-                {
-                    // Found a stale session, add it to the list
-                    try
-                    {
-                        session.timeout();
-                    }
-                    catch (Exception e)
-                    {
-                        __log.warn("Problem scavenging sessions", e);
-                    }
-                }
-                else if (_idleSavePeriodMs > 0 && session.getAccessed()+_idleSavePeriodMs < now)
-                {
-                    try
-                    {
-                        session.idle();
-                    }
-                    catch (Exception e)
-                    {
-                        __log.warn("Problem idling session "+ session.getId(), e);
-                    }
-                }
-            }
-        }       
-        finally
-        {
-            thread.setContextClassLoader(old_loader);
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    protected void addSession(AbstractSession session)
-    {
-        if (isRunning())
-            _sessions.put(session.getClusterId(),(HashedSession)session);
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public AbstractSession getSession(String idInCluster)
-    {
-        if ( _lazyLoad && !_sessionsLoaded)
-        {
-            try
-            {
-                restoreSessions();
-            }
-            catch(Exception e)
-            {
-                LOG.warn(e);
-            }
-        }
-
-        Map<String,HashedSession> sessions=_sessions;
-        if (sessions==null)
-            return null;
-
-        HashedSession session = sessions.get(idInCluster);
-
-        if (session == null && _lazyLoad)
-            session=restoreSession(idInCluster);
-        if (session == null)
-            return null;
-
-        if (_idleSavePeriodMs!=0)
-            session.deIdle();
-
-        return session;
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    protected void shutdownSessions() throws Exception
-    {   
-        // Invalidate all sessions to cause unbind events
-        ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values());
-        int loop=100;
-        while (sessions.size()>0 && loop-->0)
-        {
-            // If we are called from doStop
-            if (isStopping() && _storeDir != null && _storeDir.exists() && _storeDir.canWrite())
-            {
-                // Then we only save and remove the session from memory- it is not invalidated.
-                for (HashedSession session : sessions)
-                {
-                    session.save(false);
-                    _sessions.remove(session.getClusterId());
-                }
-            }
-            else
-            {
-                for (HashedSession session : sessions)
-                    session.invalidate();
-            }
-
-            // check that no new sessions were created while we were iterating
-            sessions=new ArrayList<HashedSession>(_sessions.values());
-        }
     }
     
-    
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * @see org.eclipse.jetty.server.SessionManager#renewSessionId(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
-     */
-    @Override
-    public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
-    {
-        try
-        {
-            Map<String,HashedSession> sessions=_sessions;
-            if (sessions == null)
-                return;
-
-            HashedSession session = sessions.remove(oldClusterId);
-            if (session == null)
-                return;
-
-            session.remove(); //delete any previously saved session
-            session.setClusterId(newClusterId); //update ids
-            session.setNodeId(newNodeId);
-            session.save(); //save updated session: TODO consider only saving file if idled
-            sessions.put(newClusterId, session);
-            
-            super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
-        }
-        catch (Exception e)
-        {
-            LOG.warn(e);
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    protected AbstractSession newSession(HttpServletRequest request)
-    {
-        return new HashedSession(this, request);
-    }
-
-    /* ------------------------------------------------------------ */
-    protected AbstractSession newSession(long created, long accessed, String clusterId)
-    {
-        return new HashedSession(this, created,accessed, clusterId);
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    protected boolean removeSession(String clusterId)
-    {
-        return _sessions.remove(clusterId)!=null;
-    }
-
-    /* ------------------------------------------------------------ */
-    public void setStoreDirectory (File dir) throws IOException
-    { 
-        // CanonicalFile is used to capture the base store directory in a way that will
-        // work on Windows.  Case differences may through off later checks using this directory.
-        _storeDir=dir.getCanonicalFile();
-    }
-
-    /* ------------------------------------------------------------ */
-    public File getStoreDirectory ()
-    {
-        return _storeDir;
-    }
-
-    /* ------------------------------------------------------------ */
-    public void setLazyLoad(boolean lazyLoad)
-    {
-        _lazyLoad = lazyLoad;
-    }
-
-    /* ------------------------------------------------------------ */
-    public boolean isLazyLoad()
-    {
-        return _lazyLoad;
-    }
-
-    /* ------------------------------------------------------------ */
-    public boolean isDeleteUnrestorableSessions()
-    {
-        return _deleteUnrestorableSessions;
-    }
-
-    /* ------------------------------------------------------------ */
-    public void setDeleteUnrestorableSessions(boolean deleteUnrestorableSessions)
-    {
-        _deleteUnrestorableSessions = deleteUnrestorableSessions;
-    }
-
-    /* ------------------------------------------------------------ */
-    public void restoreSessions () throws Exception
-    {
-        _sessionsLoaded = true;
-
-        if (_storeDir==null || !_storeDir.exists())
-        {
-            return;
-        }
-
-        if (!_storeDir.canRead())
-        {
-            LOG.warn ("Unable to restore Sessions: Cannot read from Session storage directory "+_storeDir.getAbsolutePath());
-            return;
-        }
-
-        String[] files = _storeDir.list();
-        for (int i=0;files!=null&&i<files.length;i++)
-        {
-            restoreSession(files[i]);
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    protected synchronized HashedSession restoreSession(String idInCuster)
-    {        
-        File file = new File(_storeDir,idInCuster);
-
-        Exception error = null;
-        if (!file.exists())
-        {
-            if (LOG.isDebugEnabled())
-            {
-                LOG.debug("Not loading: {}",file);
-            }
-            return null;
-        }
-        
-        try (FileInputStream in = new FileInputStream(file))
-        {
-            HashedSession session = restoreSession(in,null);
-            addSession(session,false);
-            session.didActivate();
-            return session;
-        }
-        catch (Exception e)
-        {
-           error = e;
-        }
-        finally
-        {
-            if (error != null)
-            {
-                if (isDeleteUnrestorableSessions() && file.exists() && file.getParentFile().equals(_storeDir) )
-                {
-                    file.delete();
-                    LOG.warn("Deleting file for unrestorable session {} {}",idInCuster,error);
-                    __log.debug(error);
-                }
-                else
-                {
-                    __log.warn("Problem restoring session {} {}",idInCuster, error);
-                    __log.debug(error);
-                }
-            }
-            else
-            {
-                // delete successfully restored file
-                file.delete();
-            }
-        }
-        return null;
-    }
-
-    /* ------------------------------------------------------------ */
-    public void saveSessions(boolean reactivate) throws Exception
-    {
-        if (_storeDir==null || !_storeDir.exists())
-        {
-            return;
-        }
-
-        if (!_storeDir.canWrite())
-        {
-            LOG.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable");
-            return;
-        }
-
-        for (HashedSession session : _sessions.values())
-            session.save(reactivate);
-    }
-    
-
-    /* ------------------------------------------------------------ */
-    public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception
-    {
-        DataInputStream di = new DataInputStream(is);
-
-        String clusterId = di.readUTF();
-        di.readUTF(); // nodeId
-
-        long created = di.readLong();
-        long accessed = di.readLong();
-        int requests = di.readInt();
-
-        if (session == null)
-            session = (HashedSession)newSession(created, accessed, clusterId);
-        
-        session.setRequests(requests);
-
-        // Attributes
-        int size = di.readInt();
-
-        restoreSessionAttributes(di, size, session);
-
-        try
-        {
-            int maxIdle = di.readInt();
-            session.setMaxInactiveInterval(maxIdle);
-        }
-        catch (IOException e)
-        {
-            LOG.debug("No maxInactiveInterval persisted for session "+clusterId);
-            LOG.ignore(e);
-        }
-
-        return session;
-    }
-
-
-    @SuppressWarnings("resource")
-    private void restoreSessionAttributes (InputStream is, int size, HashedSession session)
-    throws Exception
-    {
-        if (size>0)
-        {
-            // input stream should not be closed here
-            ClassLoadingObjectInputStream ois =  new ClassLoadingObjectInputStream(is);
-            for (int i=0; i<size;i++)
-            {
-                String key = ois.readUTF();
-                Object value = ois.readObject();
-                session.setAttribute(key,value);
-            }
-        }
-    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java
deleted file mode 100644
index 9a289fd..0000000
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java
+++ /dev/null
@@ -1,286 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.server.session;
-
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.util.Enumeration;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
-public class HashedSession extends MemSession
-{
-    private static final Logger LOG = Log.getLogger(HashedSession.class);
-
-    private final HashSessionManager _hashSessionManager;
-
-    /** Whether the session has been saved because it has been deemed idle;
-     * in which case its attribute map will have been saved and cleared. */
-    private transient boolean _idled = false;
-
-    /** Whether there has already been an attempt to save this session
-     * which has failed.  If there has, there will be no more save attempts
-     * for this session.  This is to stop the logs being flooded with errors
-     * due to serialization failures that are most likely caused by user
-     * data stored in the session that is not serializable. */
-    private transient boolean _saveFailed = false;
-    
-    /**
-     * True if an attempt has been made to de-idle a session and it failed. Once
-     * true, the session will not be attempted to be de-idled again.
-     */
-    private transient boolean _deIdleFailed = false;
-
-    /* ------------------------------------------------------------- */
-    protected HashedSession(HashSessionManager hashSessionManager, HttpServletRequest request)
-    {
-        super(hashSessionManager,request);
-        _hashSessionManager = hashSessionManager;
-    }
-
-    /* ------------------------------------------------------------- */
-    protected HashedSession(HashSessionManager hashSessionManager, long created, long accessed, String clusterId)
-    {
-        super(hashSessionManager,created, accessed, clusterId);
-        _hashSessionManager = hashSessionManager;
-    }
-
-    /* ------------------------------------------------------------- */
-    protected void checkValid()
-    {
-        if (!_deIdleFailed && _hashSessionManager._idleSavePeriodMs!=0)
-            deIdle();
-        super.checkValid();
-    }
-
-    /* ------------------------------------------------------------- */
-    @Override
-    public void setMaxInactiveInterval(int secs)
-    {
-        super.setMaxInactiveInterval(secs);
-        if (getMaxInactiveInterval()>0&&(getMaxInactiveInterval()*1000L/10)<_hashSessionManager._scavengePeriodMs)
-            _hashSessionManager.setScavengePeriod((secs+9)/10);
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    protected void doInvalidate()
-    throws IllegalStateException
-    {
-        super.doInvalidate();
-        remove();
-    }
-    
-    
-    /* ------------------------------------------------------------ */
-    /**
-     * Remove from the disk
-     */
-    synchronized void remove ()
-    {
-        if (_hashSessionManager._storeDir!=null && getId()!=null)
-        {
-            String id=getId();
-            File f = new File(_hashSessionManager._storeDir, id);
-            f.delete();
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    synchronized void save(boolean reactivate)
-    throws Exception
-    {
-        // Only idle the session if not already idled and no previous save/idle has failed
-        if (!isIdled() && !_saveFailed)
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Saving {} {}",super.getId(),reactivate);
-
-            try
-            {
-                willPassivate();
-                save();
-                if (reactivate)
-                    didActivate();
-                else
-                    clearAttributes();
-            }
-            catch (Exception e)
-            {       
-                LOG.warn("Problem saving session " + super.getId(), e);
-                _idled=false; // assume problem was before _values.clear();
-            }
-        }
-    }
-    
-    
-    
-    synchronized void save ()
-    throws Exception
-    {   
-        File file = null;
-        if (!_saveFailed && _hashSessionManager._storeDir != null)
-        {
-            file = new File(_hashSessionManager._storeDir, super.getId());
-            if (file.exists())
-            {
-                file.delete();
-            }
-
-            try(FileOutputStream fos = new FileOutputStream(file,false))
-            {
-                save(fos);
-            }
-            catch (Exception e)
-            {
-                saveFailed(); // We won't try again for this session
-                if (file != null) 
-                    file.delete(); // No point keeping the file if we didn't save the whole session
-                throw e;             
-            }
-        }
-    }
-    
-    
-    /* ------------------------------------------------------------ */
-    public synchronized void save(OutputStream os)  throws IOException
-    {
-        DataOutputStream out = new DataOutputStream(os);
-        out.writeUTF(getClusterId());
-        out.writeUTF(getNodeId());
-        out.writeLong(getCreationTime());
-        out.writeLong(getAccessed());
-
-        /* Don't write these out, as they don't make sense to store because they
-         * either they cannot be true or their value will be restored in the
-         * Session constructor.
-         */
-        //out.writeBoolean(_invalid);
-        //out.writeBoolean(_doInvalidate);
-        //out.writeBoolean( _newSession);
-        out.writeInt(getRequests());
-        out.writeInt(getAttributes());
-        ObjectOutputStream oos = new ObjectOutputStream(out);
-        Enumeration<String> e=getAttributeNames();
-        while(e.hasMoreElements())
-        {
-            String key=e.nextElement();
-            oos.writeUTF(key);
-            oos.writeObject(doGet(key));
-        }
-        
-        out.writeInt(getMaxInactiveInterval());
-    }
-
-    /* ------------------------------------------------------------ */
-    public synchronized void deIdle()
-    {
-        if (isIdled() && !_deIdleFailed)
-        {
-            // Access now to prevent race with idling period
-            access(System.currentTimeMillis());
-
-            if (LOG.isDebugEnabled())
-                LOG.debug("De-idling " + super.getId());
-
-            FileInputStream fis = null;
-
-            try
-            {
-                File file = new File(_hashSessionManager._storeDir, super.getId());
-                if (!file.exists() || !file.canRead())
-                    throw new FileNotFoundException(file.getName());
-
-                fis = new FileInputStream(file);
-                _idled = false;
-                _hashSessionManager.restoreSession(fis, this);
-                IO.close(fis); 
-                
-                didActivate();
-
-                // If we are doing period saves, then there is no point deleting at this point 
-                if (_hashSessionManager._savePeriodMs == 0)
-                    file.delete();
-            }
-            catch (Exception e)
-            {
-                deIdleFailed();
-                LOG.warn("Problem de-idling session " + super.getId(), e);
-                if (fis != null) IO.close(fis);//Must ensure closed before invalidate
-                invalidate();
-            }
-        }
-    }
-
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Idle the session to reduce session memory footprint.
-     *
-     * The session is idled by persisting it, then clearing the session values attribute map and finally setting
-     * it to an idled state.
-     * @throws Exception if unable to save session
-     */
-    public synchronized void idle()
-    throws Exception
-    {
-        save(false);
-        _idled = true;
-    }
-
-    /* ------------------------------------------------------------ */
-    public synchronized boolean isIdled()
-    {
-      return _idled;
-    }
-
-    /* ------------------------------------------------------------ */
-    public synchronized boolean isSaveFailed()
-    {
-        return _saveFailed;
-    }
-
-    /* ------------------------------------------------------------ */
-    public synchronized void saveFailed()
-    {
-        _saveFailed = true;
-    }
-
-    /* ------------------------------------------------------------ */
-    public synchronized void deIdleFailed()
-    {
-        _deIdleFailed = true;
-    }
-    
-    /* ------------------------------------------------------------ */
-    public synchronized boolean isDeIdleFailed()
-    {
-        return _deIdleFailed;
-    }
-}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java
new file mode 100644
index 0000000..d810c19
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java
@@ -0,0 +1,1067 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.ObjectOutputStream;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * JDBCSessionDataStore
+ *
+ * Session data stored in database
+ */
+public class JDBCSessionDataStore extends AbstractSessionDataStore
+{
+    final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
+    protected boolean _initialized = false;
+    protected Map<String, AtomicInteger> _unloadables = new ConcurrentHashMap<>();
+
+    private DatabaseAdaptor _dbAdaptor;
+    private SessionTableSchema _sessionTableSchema;
+
+    private int _attempts = -1; // <= 0 means unlimited attempts to load a session
+    private boolean _deleteUnloadables = false; //true means if attempts exhausted delete the session
+    private long _gracePeriodMs = 1000L * 60 * 60; //default grace period is 1hr
+
+    /**
+     * SessionTableSchema
+     *
+     */
+    public static class SessionTableSchema
+    {      
+        public final static int MAX_INTERVAL_NOT_SET = -999;
+        
+        protected DatabaseAdaptor _dbAdaptor;
+        protected String _tableName = "JettySessions";
+        protected String _idColumn = "sessionId";
+        protected String _contextPathColumn = "contextPath";
+        protected String _virtualHostColumn = "virtualHost"; 
+        protected String _lastNodeColumn = "lastNode";
+        protected String _accessTimeColumn = "accessTime"; 
+        protected String _lastAccessTimeColumn = "lastAccessTime";
+        protected String _createTimeColumn = "createTime";
+        protected String _cookieTimeColumn = "cookieTime";
+        protected String _lastSavedTimeColumn = "lastSavedTime";
+        protected String _expiryTimeColumn = "expiryTime";
+        protected String _maxIntervalColumn = "maxInterval";
+        protected String _mapColumn = "map";
+
+        
+        
+        protected void setDatabaseAdaptor(DatabaseAdaptor dbadaptor)
+        {
+            _dbAdaptor = dbadaptor;
+        }
+        
+        
+        public String getTableName()
+        {
+            return _tableName;
+        }
+        public void setTableName(String tableName)
+        {
+            checkNotNull(tableName);
+            _tableName = tableName;
+        }
+     
+        public String getIdColumn()
+        {
+            return _idColumn;
+        }
+        public void setIdColumn(String idColumn)
+        {
+            checkNotNull(idColumn);
+            _idColumn = idColumn;
+        }
+        public String getContextPathColumn()
+        {
+            return _contextPathColumn;
+        }
+        public void setContextPathColumn(String contextPathColumn)
+        {
+            checkNotNull(contextPathColumn);
+            _contextPathColumn = contextPathColumn;
+        }
+        public String getVirtualHostColumn()
+        {
+            return _virtualHostColumn;
+        }
+        public void setVirtualHostColumn(String virtualHostColumn)
+        {
+            checkNotNull(virtualHostColumn);
+            _virtualHostColumn = virtualHostColumn;
+        }
+        public String getLastNodeColumn()
+        {
+            return _lastNodeColumn;
+        }
+        public void setLastNodeColumn(String lastNodeColumn)
+        {
+            checkNotNull(lastNodeColumn);
+            _lastNodeColumn = lastNodeColumn;
+        }
+        public String getAccessTimeColumn()
+        {
+            return _accessTimeColumn;
+        }
+        public void setAccessTimeColumn(String accessTimeColumn)
+        {
+            checkNotNull(accessTimeColumn);
+            _accessTimeColumn = accessTimeColumn;
+        }
+        public String getLastAccessTimeColumn()
+        {
+            return _lastAccessTimeColumn;
+        }
+        public void setLastAccessTimeColumn(String lastAccessTimeColumn)
+        {
+            checkNotNull(lastAccessTimeColumn);
+            _lastAccessTimeColumn = lastAccessTimeColumn;
+        }
+        public String getCreateTimeColumn()
+        {
+            return _createTimeColumn;
+        }
+        public void setCreateTimeColumn(String createTimeColumn)
+        {
+            checkNotNull(createTimeColumn);
+            _createTimeColumn = createTimeColumn;
+        }
+        public String getCookieTimeColumn()
+        {
+            return _cookieTimeColumn;
+        }
+        public void setCookieTimeColumn(String cookieTimeColumn)
+        {
+            checkNotNull(cookieTimeColumn);
+            _cookieTimeColumn = cookieTimeColumn;
+        }
+        public String getLastSavedTimeColumn()
+        {
+            return _lastSavedTimeColumn;
+        }
+        public void setLastSavedTimeColumn(String lastSavedTimeColumn)
+        {
+            checkNotNull(lastSavedTimeColumn);
+            _lastSavedTimeColumn = lastSavedTimeColumn;
+        }
+        public String getExpiryTimeColumn()
+        {
+            return _expiryTimeColumn;
+        }
+        public void setExpiryTimeColumn(String expiryTimeColumn)
+        {
+            checkNotNull(expiryTimeColumn);
+            _expiryTimeColumn = expiryTimeColumn;
+        }
+        public String getMaxIntervalColumn()
+        {
+            return _maxIntervalColumn;
+        }
+        public void setMaxIntervalColumn(String maxIntervalColumn)
+        {
+            checkNotNull(maxIntervalColumn);
+            _maxIntervalColumn = maxIntervalColumn;
+        }
+        public String getMapColumn()
+        {
+            return _mapColumn;
+        }
+        public void setMapColumn(String mapColumn)
+        {
+            checkNotNull(mapColumn);
+            _mapColumn = mapColumn;
+        }
+        
+        public String getCreateStatementAsString ()
+        {
+            if (_dbAdaptor == null)
+                throw new IllegalStateException ("No DBAdaptor");
+            
+            String blobType = _dbAdaptor.getBlobType();
+            String longType = _dbAdaptor.getLongType();
+            
+            return "create table "+_tableName+" ("+_idColumn+" varchar(120), "+
+                    _contextPathColumn+" varchar(60), "+_virtualHostColumn+" varchar(60), "+_lastNodeColumn+" varchar(60), "+_accessTimeColumn+" "+longType+", "+
+                    _lastAccessTimeColumn+" "+longType+", "+_createTimeColumn+" "+longType+", "+_cookieTimeColumn+" "+longType+", "+
+                    _lastSavedTimeColumn+" "+longType+", "+_expiryTimeColumn+" "+longType+", "+_maxIntervalColumn+" "+longType+", "+
+                    _mapColumn+" "+blobType+", primary key("+_idColumn+", "+_contextPathColumn+","+_virtualHostColumn+"))";
+        }
+        
+        public String getCreateIndexOverExpiryStatementAsString (String indexName)
+        {
+            return "create index "+indexName+" on "+getTableName()+" ("+getExpiryTimeColumn()+")";
+        }
+        
+        public String getCreateIndexOverSessionStatementAsString (String indexName)
+        {
+            return "create index "+indexName+" on "+getTableName()+" ("+getIdColumn()+", "+getContextPathColumn()+")";
+        }
+        
+        public String getAlterTableForMaxIntervalAsString ()
+        {
+            if (_dbAdaptor == null)
+                throw new IllegalStateException ("No DBAdaptor");
+            String longType = _dbAdaptor.getLongType();
+            String stem = "alter table "+getTableName()+" add "+getMaxIntervalColumn()+" "+longType;
+            if (_dbAdaptor.getDBName().contains("oracle"))
+                return stem + " default "+ MAX_INTERVAL_NOT_SET + " not null";
+            else
+                return stem +" not null default "+ MAX_INTERVAL_NOT_SET;
+        }
+        
+        private void checkNotNull(String s)
+        {
+            if (s == null)
+                throw new IllegalArgumentException(s);
+        }
+        public String getInsertSessionStatementAsString()
+        {
+           return "insert into "+getTableName()+
+            " ("+getIdColumn()+", "+getContextPathColumn()+", "+getVirtualHostColumn()+", "+getLastNodeColumn()+
+            ", "+getAccessTimeColumn()+", "+getLastAccessTimeColumn()+", "+getCreateTimeColumn()+", "+getCookieTimeColumn()+
+            ", "+getLastSavedTimeColumn()+", "+getExpiryTimeColumn()+", "+getMaxIntervalColumn()+", "+getMapColumn()+") "+
+            " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+        }
+
+        public PreparedStatement getUpdateSessionStatement(Connection connection, String canonicalContextPath)
+                throws SQLException
+        {
+            String s =  "update "+getTableName()+
+                    " set "+getLastNodeColumn()+" = ?, "+getAccessTimeColumn()+" = ?, "+
+                    getLastAccessTimeColumn()+" = ?, "+getLastSavedTimeColumn()+" = ?, "+getExpiryTimeColumn()+" = ?, "+
+                    getMaxIntervalColumn()+" = ?, "+getMapColumn()+" = ? where ";
+
+            if (canonicalContextPath == null || "".equals(canonicalContextPath))
+            {
+                if (_dbAdaptor.isEmptyStringNull())
+                {
+                    s = s+getIdColumn()+" = ? and "+
+                            getContextPathColumn()+" is null and "+
+                            getVirtualHostColumn()+" = ?";
+                    return connection.prepareStatement(s);
+                }
+            }
+
+            return connection.prepareStatement(s+getIdColumn()+" = ? and "+getContextPathColumn()+
+                                               " = ? and "+getVirtualHostColumn()+" = ?");
+        }
+
+      
+        public PreparedStatement getMyExpiredSessionsStatement (Connection connection, String canonicalContextPath, String vhost, long expiry)
+        throws SQLException
+        {
+            if (_dbAdaptor == null)
+                throw new IllegalStateException("No DB adaptor");
+
+
+            if (canonicalContextPath == null || "".equals(canonicalContextPath))
+            {
+                if (_dbAdaptor.isEmptyStringNull())
+                {
+                    PreparedStatement statement = connection.prepareStatement("select "+getIdColumn()+", "+getExpiryTimeColumn()+
+                                                                              " from "+getTableName()+" where "+
+                                                                              getContextPathColumn()+" is null and "+
+                                                                              getVirtualHostColumn()+" = ? and "+getExpiryTimeColumn()+" >0 and "+getExpiryTimeColumn()+" <= ?");
+                    statement.setString(1, vhost);
+                    statement.setLong(2, expiry);
+                    return statement;
+                }
+            }
+
+            PreparedStatement statement = connection.prepareStatement("select "+getIdColumn()+", "+getExpiryTimeColumn()+
+                                                                      " from "+getTableName()+" where "+getContextPathColumn()+" = ? and "+
+                                                                      getVirtualHostColumn()+" = ? and "+
+                                                                      getExpiryTimeColumn()+" >0 and "+getExpiryTimeColumn()+" <= ?");
+
+            statement.setString(1, canonicalContextPath);
+            statement.setString(2, vhost);
+            statement.setLong(3, expiry);
+            return statement;
+        }
+      
+    
+        
+        public PreparedStatement getAllAncientExpiredSessionsStatement (Connection connection)
+        throws SQLException
+        {
+            if (_dbAdaptor == null)
+                throw new IllegalStateException("No DB adaptor");
+
+            PreparedStatement statement = connection.prepareStatement("select "+getIdColumn()+", "+getContextPathColumn()+", "+getVirtualHostColumn()+
+                                                                      " from "+getTableName()+
+                                                                      " where "+getExpiryTimeColumn()+" >0 and "+getExpiryTimeColumn()+" <= ?");
+            return statement;
+        }
+     
+     
+        public PreparedStatement getCheckSessionExistsStatement (Connection connection, String canonicalContextPath)
+        throws SQLException
+        {
+            if (_dbAdaptor == null)
+                throw new IllegalStateException("No DB adaptor");
+
+
+            if (canonicalContextPath == null || "".equals(canonicalContextPath))
+            {
+                if (_dbAdaptor.isEmptyStringNull())
+                {
+                    PreparedStatement statement = connection.prepareStatement("select "+getIdColumn()+", "+getExpiryTimeColumn()+
+                                                                              " from "+getTableName()+
+                                                                              " where "+getIdColumn()+" = ? and "+
+                                                                              getContextPathColumn()+" is null and "+
+                                                                              getVirtualHostColumn()+" = ?");
+                    return statement;
+                }
+            }
+
+            PreparedStatement statement = connection.prepareStatement("select "+getIdColumn()+", "+getExpiryTimeColumn()+
+                                                                      " from "+getTableName()+
+                                                                      " where "+getIdColumn()+" = ? and "+
+                                                                      getContextPathColumn()+" = ? and "+
+                                                                      getVirtualHostColumn()+" = ?");
+            return statement;
+        }
+
+        public void fillCheckSessionExistsStatement (PreparedStatement statement, String id, SessionContext contextId)
+        throws SQLException
+        {
+            statement.clearParameters();
+            ParameterMetaData metaData = statement.getParameterMetaData();
+            if (metaData.getParameterCount() < 3)
+            {
+                statement.setString(1, id);
+                statement.setString(2, contextId.getVhost());
+            }
+            else
+            {
+                statement.setString(1, id);
+                statement.setString(2, contextId.getCanonicalContextPath());
+                statement.setString(3, contextId.getVhost());
+            }
+        }
+        
+        
+        public PreparedStatement getLoadStatement (Connection connection, String id, SessionContext contextId)
+        throws SQLException
+        { 
+            if (_dbAdaptor == null)
+                throw new IllegalStateException("No DB adaptor");
+
+
+            if (contextId.getCanonicalContextPath() == null || "".equals(contextId.getCanonicalContextPath()))
+            {
+                if (_dbAdaptor.isEmptyStringNull())
+                {
+                    PreparedStatement statement = connection.prepareStatement("select * from "+getTableName()+
+                                                                              " where "+getIdColumn()+" = ? and "+
+                                                                              getContextPathColumn()+" is null and "+
+                                                                              getVirtualHostColumn()+" = ?");
+                    statement.setString(1, id);
+                    statement.setString(2, contextId.getVhost());
+
+                    return statement;
+                }
+            }
+
+            PreparedStatement statement = connection.prepareStatement("select * from "+getTableName()+
+                                                                      " where "+getIdColumn()+" = ? and "+getContextPathColumn()+
+                                                                      " = ? and "+getVirtualHostColumn()+" = ?");
+            statement.setString(1, id);
+            statement.setString(2, contextId.getCanonicalContextPath());
+            statement.setString(3, contextId.getVhost());
+
+            return statement;
+        }
+
+        
+        
+        public PreparedStatement getUpdateStatement (Connection connection, String id, SessionContext contextId)
+        throws SQLException
+        {
+            if (_dbAdaptor == null)
+                throw new IllegalStateException("No DB adaptor");
+
+            String s = "update "+getTableName()+
+                    " set "+getLastNodeColumn()+" = ?, "+getAccessTimeColumn()+" = ?, "+
+                    getLastAccessTimeColumn()+" = ?, "+getLastSavedTimeColumn()+" = ?, "+getExpiryTimeColumn()+" = ?, "+
+                    getMaxIntervalColumn()+" = ?, "+getMapColumn()+" = ? where ";
+
+            if (contextId.getCanonicalContextPath() == null || "".equals(contextId.getCanonicalContextPath()))
+            {
+                if (_dbAdaptor.isEmptyStringNull())
+                {
+                    PreparedStatement statement = connection.prepareStatement(s+getIdColumn()+" = ? and "+
+                            getContextPathColumn()+" is null and "+
+                            getVirtualHostColumn()+" = ?");
+                    statement.setString(1, id);
+                    statement.setString(2, contextId.getVhost());
+                    return statement;
+                }
+            }
+            PreparedStatement statement = connection.prepareStatement(s+getIdColumn()+" = ? and "+getContextPathColumn()+
+                                                                      " = ? and "+getVirtualHostColumn()+" = ?");
+            statement.setString(1, id);
+            statement.setString(2, contextId.getCanonicalContextPath());
+            statement.setString(3, contextId.getVhost());
+
+            return statement;
+        }
+        
+        
+
+
+        public PreparedStatement getDeleteStatement (Connection connection, String id, SessionContext contextId)
+        throws Exception
+        { 
+            if (_dbAdaptor == null)
+
+                throw new IllegalStateException("No DB adaptor");
+
+
+            if (contextId.getCanonicalContextPath() == null || "".equals(contextId.getCanonicalContextPath()))
+            {
+                if (_dbAdaptor.isEmptyStringNull())
+                {
+                    PreparedStatement statement = connection.prepareStatement("delete from "+getTableName()+
+                                                                              " where "+getIdColumn()+" = ? and "+getContextPathColumn()+
+                                                                              " = ? and "+getVirtualHostColumn()+" = ?");
+                    statement.setString(1, id);
+                    statement.setString(2, contextId.getVhost());
+                    return statement;
+                }
+            }
+
+            PreparedStatement statement = connection.prepareStatement("delete from "+getTableName()+
+                                                                      " where "+getIdColumn()+" = ? and "+getContextPathColumn()+
+                                                                      " = ? and "+getVirtualHostColumn()+" = ?");
+            statement.setString(1, id);
+            statement.setString(2, contextId.getCanonicalContextPath());
+            statement.setString(3, contextId.getVhost());
+
+            return statement;
+
+        }
+
+        
+        /**
+         * Set up the tables in the database
+         * @throws SQLException
+         */
+        /**
+         * @throws SQLException
+         */
+        public void prepareTables()
+        throws SQLException
+        {
+            try (Connection connection = _dbAdaptor.getConnection();
+                 Statement statement = connection.createStatement())
+            {
+                //make the id table
+                connection.setAutoCommit(true);
+                DatabaseMetaData metaData = connection.getMetaData();
+                _dbAdaptor.adaptTo(metaData);
+                    
+                
+                //make the session table if necessary
+                String tableName = _dbAdaptor.convertIdentifier(getTableName());
+                try (ResultSet result = metaData.getTables(null, null, tableName, null))
+                {
+                    if (!result.next())
+                    {
+                        //table does not exist, so create it
+                        statement.executeUpdate(getCreateStatementAsString());
+                    }
+                    else
+                    {
+                        //session table exists, check it has maxinterval column
+                        ResultSet colResult = null;
+                        try
+                        {
+                            colResult = metaData.getColumns(null, null,
+                                                            _dbAdaptor.convertIdentifier(getTableName()), 
+                                                            _dbAdaptor.convertIdentifier(getMaxIntervalColumn()));
+                        }
+                        catch (SQLException s)
+                        {
+                            LOG.warn("Problem checking if "+getTableName()+
+                                     " table contains "+getMaxIntervalColumn()+" column. Ensure table contains column definition: \""
+                                    + getMaxIntervalColumn()+" long not null default -999\"");
+                            throw s;
+                        }
+                        try
+                        {
+                            if (!colResult.next())
+                            {
+                                try
+                                {
+                                    //add the maxinterval column
+                                    statement.executeUpdate(getAlterTableForMaxIntervalAsString());
+                                }
+                                catch (SQLException s)
+                                {
+                                    LOG.warn("Problem adding "+getMaxIntervalColumn()+
+                                             " column. Ensure table contains column definition: \""+getMaxIntervalColumn()+
+                                             " long not null default -999\"");
+                                    throw s;
+                                }
+                            }
+                        }
+                        finally
+                        {
+                            colResult.close();
+                        }
+                    }
+                }
+                //make some indexes on the JettySessions table
+                String index1 = "idx_"+getTableName()+"_expiry";
+                String index2 = "idx_"+getTableName()+"_session";
+
+                boolean index1Exists = false;
+                boolean index2Exists = false;
+                try (ResultSet result = metaData.getIndexInfo(null, null, tableName, false, false))
+                {
+                    while (result.next())
+                    {
+                        String idxName = result.getString("INDEX_NAME");
+                        if (index1.equalsIgnoreCase(idxName))
+                            index1Exists = true;
+                        else if (index2.equalsIgnoreCase(idxName))
+                            index2Exists = true;
+                    }
+                }
+                if (!index1Exists)
+                    statement.executeUpdate(getCreateIndexOverExpiryStatementAsString(index1));
+                if (!index2Exists)
+                    statement.executeUpdate(getCreateIndexOverSessionStatementAsString(index2));
+            }
+        }
+    }
+    
+    
+   
+  
+    public JDBCSessionDataStore ()
+    {
+        super ();
+    }
+
+  
+
+
+
+    @Override
+    protected void doStart() throws Exception
+    {         
+        if (_dbAdaptor == null)
+            throw new IllegalStateException("No jdbc config");
+        
+        _unloadables.clear();
+        initialize();
+        super.doStart();
+    }
+
+
+
+
+    @Override
+    protected void doStop() throws Exception
+    {
+        _unloadables.clear();
+        super.doStop();
+    }
+
+
+
+
+    public void initialize () throws Exception
+    {
+        if (!_initialized)
+        {
+            _initialized = true;
+   
+            //taking the defaults if one not set
+            if (_sessionTableSchema == null)
+                _sessionTableSchema = new SessionTableSchema();
+            
+            _dbAdaptor.initialize();
+            _sessionTableSchema.setDatabaseAdaptor(_dbAdaptor);
+            _sessionTableSchema.prepareTables();
+        }
+    }
+
+
+ 
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#load(org.eclipse.jetty.server.session.SessionKey)
+     */
+    @Override
+    public SessionData load(String id) throws Exception
+    {
+        if (getLoadAttempts() > 0 && loadAttemptsExhausted(id))
+            throw new UnreadableSessionDataException(id, _context, true);
+            
+        final AtomicReference<SessionData> reference = new AtomicReference<SessionData>();
+        final AtomicReference<Exception> exception = new AtomicReference<Exception>();
+        
+        Runnable r = new Runnable()
+        {
+            public void run ()
+            {
+                try (Connection connection = _dbAdaptor.getConnection();
+                     PreparedStatement statement = _sessionTableSchema.getLoadStatement(connection, id, _context);
+                     ResultSet result = statement.executeQuery())
+                {
+                    SessionData data = null;
+                    if (result.next())
+                    {                    
+                        data = newSessionData(id,
+                                              result.getLong(_sessionTableSchema.getCreateTimeColumn()), 
+                                              result.getLong(_sessionTableSchema.getAccessTimeColumn()), 
+                                              result.getLong(_sessionTableSchema.getLastAccessTimeColumn()), 
+                                              result.getLong(_sessionTableSchema.getMaxIntervalColumn()));
+                        data.setCookieSet(result.getLong(_sessionTableSchema.getCookieTimeColumn()));
+                        data.setLastNode(result.getString(_sessionTableSchema.getLastNodeColumn()));
+                        data.setLastSaved(result.getLong(_sessionTableSchema.getLastSavedTimeColumn()));
+                        data.setExpiry(result.getLong(_sessionTableSchema.getExpiryTimeColumn()));
+                        data.setContextPath(result.getString(_sessionTableSchema.getContextPathColumn())); //TODO needed? this is part of the key now
+                        data.setVhost(result.getString(_sessionTableSchema.getVirtualHostColumn())); //TODO needed??? this is part of the key now
+
+                        try (InputStream is = _dbAdaptor.getBlobInputStream(result, _sessionTableSchema.getMapColumn());
+                             ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
+                        {
+                            Object o = ois.readObject();
+                            data.putAllAttributes((Map<String,Object>)o);
+                        }
+                        catch (Exception e)
+                        {
+                            if (getLoadAttempts() > 0)
+                            {
+                                incLoadAttempt (id);
+                            }
+                            throw new UnreadableSessionDataException (id, _context, e);
+                        }
+                        
+                        //if the session successfully loaded, remove failed attempts
+                        _unloadables.remove(id);
+                        
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("LOADED session {}", data);
+                    }
+                    else
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("No session {}", id);
+                    
+                    reference.set(data);
+                }
+                catch (UnreadableSessionDataException e)
+                {
+                    if (getLoadAttempts() > 0 && loadAttemptsExhausted(id) && isDeleteUnloadableSessions())
+                    {
+                        try
+                        {
+                            delete (id);
+                            _unloadables.remove(id);
+                        }
+                        catch (Exception x)
+                        {
+                            LOG.warn("Problem deleting unloadable session {}", id);
+                        }
+
+                    }
+                    exception.set(e);
+                }
+                catch (Exception e)
+                {
+                    exception.set(e);
+                }
+            }
+        };
+
+        //ensure this runs with context classloader set
+        _context.run(r);
+
+        if (exception.get() != null)
+            throw exception.get();
+
+        return reference.get();
+    }
+
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#delete(java.lang.String)
+     */
+    @Override
+    public boolean delete(String id) throws Exception
+    {
+        try (Connection connection = _dbAdaptor.getConnection();
+             PreparedStatement statement = _sessionTableSchema.getDeleteStatement(connection, id, _context))
+        {
+            connection.setAutoCommit(true);
+            int rows = statement.executeUpdate();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Deleted Session {}:{}",id,(rows>0));
+            
+            return rows > 0;
+        }
+    }
+
+
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore()
+     */
+    @Override
+    public void doStore(String id, SessionData data, boolean isNew) throws Exception
+    {
+        if (data==null || id==null)
+            return;
+
+        if (isNew)
+        {     
+            doInsert(id, data);
+        }
+        else
+        {
+            doUpdate(id, data);            
+        }
+    }
+
+
+    private void doInsert (String id, SessionData data) 
+    throws Exception
+    {
+        String s = _sessionTableSchema.getInsertSessionStatementAsString();
+
+
+        try (Connection connection = _dbAdaptor.getConnection())        
+        {
+            connection.setAutoCommit(true);
+            try  (PreparedStatement statement = connection.prepareStatement(s))
+            {
+                statement.setString(1, id); //session id
+                statement.setString(2, _context.getCanonicalContextPath()); //context path
+                statement.setString(3, _context.getVhost()); //first vhost
+                statement.setString(4, data.getLastNode());//my node id
+                statement.setLong(5, data.getAccessed());//accessTime
+                statement.setLong(6, data.getLastAccessed()); //lastAccessTime
+                statement.setLong(7, data.getCreated()); //time created
+                statement.setLong(8, data.getCookieSet());//time cookie was set
+                statement.setLong(9, data.getLastSaved()); //last saved time
+                statement.setLong(10, data.getExpiry());
+                statement.setLong(11, data.getMaxInactiveMs());
+
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                ObjectOutputStream oos = new ObjectOutputStream(baos);
+                oos.writeObject(data.getAllAttributes());
+                oos.flush();
+                byte[] bytes = baos.toByteArray();
+
+                ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+                statement.setBinaryStream(12, bais, bytes.length);//attribute map as blob
+                statement.executeUpdate();
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Inserted session "+data);
+            }
+        }
+    }
+
+    private void doUpdate (String id, SessionData data)
+            throws Exception
+    {
+        try (Connection connection = _dbAdaptor.getConnection())        
+        {
+            connection.setAutoCommit(true);
+            try (PreparedStatement statement = _sessionTableSchema.getUpdateSessionStatement(connection, _context.getCanonicalContextPath()))
+            {
+                statement.setString(1, data.getLastNode());//should be my node id
+                statement.setLong(2, data.getAccessed());//accessTime
+                statement.setLong(3, data.getLastAccessed()); //lastAccessTime
+                statement.setLong(4, data.getLastSaved()); //last saved time
+                statement.setLong(5, data.getExpiry());
+                statement.setLong(6, data.getMaxInactiveMs());
+
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                ObjectOutputStream oos = new ObjectOutputStream(baos);
+                oos.writeObject(data.getAllAttributes());
+                oos.flush();
+                byte[] bytes = baos.toByteArray();
+                ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+                statement.setBinaryStream(7, bais, bytes.length);//attribute map as blob
+
+                if ((_context.getCanonicalContextPath() == null || "".equals(_context.getCanonicalContextPath())) && _dbAdaptor.isEmptyStringNull())
+                {
+                    statement.setString(8, id);
+                    statement.setString(9, _context.getVhost()); 
+                }
+                else
+                {
+                    statement.setString(8, id);
+                    statement.setString(9, _context.getCanonicalContextPath());
+                    statement.setString(10, _context.getVhost());
+                }
+
+                statement.executeUpdate();
+
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Updated session "+data);
+            }
+        }
+    }
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired()
+     */
+    @Override
+    public Set<String> getExpired(Set<String> candidates)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Getting expired sessions "+System.currentTimeMillis());
+
+        long now = System.currentTimeMillis();
+        
+        
+        Set<String> expiredSessionKeys = new HashSet<>();
+        try (Connection connection = _dbAdaptor.getConnection())
+        {
+            connection.setAutoCommit(true);
+            
+            /*
+             * 1. Select sessions for our node and context that have expired
+             */
+            long upperBound = now;
+            if (LOG.isDebugEnabled())
+                LOG.debug ("{}- Pass 1: Searching for sessions for node {} and context {} expired before {}", _context.getWorkerName(), _context.getCanonicalContextPath(), upperBound);
+
+            try (PreparedStatement statement = _sessionTableSchema.getMyExpiredSessionsStatement(connection, _context.getCanonicalContextPath(), _context.getVhost(), upperBound))
+            {
+                try (ResultSet result = statement.executeQuery())
+                {
+                    while (result.next())
+                    {
+                        String sessionId = result.getString(_sessionTableSchema.getIdColumn());
+                        long exp = result.getLong(_sessionTableSchema.getExpiryTimeColumn());
+                        expiredSessionKeys.add(sessionId);
+                        if (LOG.isDebugEnabled()) LOG.debug (_context.getCanonicalContextPath()+"- Found expired sessionId="+sessionId);
+                    }
+                }
+            }
+
+            /*
+             *  2. Select sessions for any node or context that have expired a long time ago (ie at least 3 grace periods ago)
+             */
+            try (PreparedStatement selectExpiredSessions = _sessionTableSchema.getAllAncientExpiredSessionsStatement(connection))
+            {
+                upperBound = now - (3 * _gracePeriodMs);
+                if (upperBound > 0)
+                {
+                    if (LOG.isDebugEnabled()) LOG.debug("{}- Pass 2: Searching for sessions expired before {}",_context.getWorkerName(), upperBound);
+
+                    selectExpiredSessions.setLong(1, upperBound);
+                    try (ResultSet result = selectExpiredSessions.executeQuery())
+                    {
+                        while (result.next())
+                        {
+                            String sessionId = result.getString(_sessionTableSchema.getIdColumn());
+                            String ctxtpth = result.getString(_sessionTableSchema.getContextPathColumn());
+                            String vh = result.getString(_sessionTableSchema.getVirtualHostColumn());
+                            expiredSessionKeys.add(sessionId);
+                            if (LOG.isDebugEnabled()) LOG.debug ("{}- Found expired sessionId=",_context.getWorkerName(), sessionId);
+                        }
+                    }
+                }
+            }
+            
+
+            Set<String> notExpiredInDB = new HashSet<>();
+            for (String k: candidates)
+            {
+                //there are some keys that the session store thought had expired, but were not
+                //found in our sweep either because it is no longer in the db, or its
+                //expiry time was updated
+                if (!expiredSessionKeys.contains(k))
+                    notExpiredInDB.add(k);
+            }
+
+
+            if (!notExpiredInDB.isEmpty())
+            {
+                //we have some sessions to check
+                try (PreparedStatement checkSessionExists = _sessionTableSchema.getCheckSessionExistsStatement(connection, _context.getCanonicalContextPath()))
+                {
+                    for (String k: notExpiredInDB)
+                    {
+                        _sessionTableSchema.fillCheckSessionExistsStatement (checkSessionExists, k, _context);
+                        try (ResultSet result = checkSessionExists.executeQuery())
+                        {        
+                            if (!result.next())
+                            {
+                                //session doesn't exist any more, can be expired
+                                expiredSessionKeys.add(k);
+                            }
+                            //else its expiry time has not been reached
+                        }
+                        catch (Exception e)
+                        {
+                            LOG.warn("Problem checking if potentially expired session {} exists in db", k,e);
+                        }
+                    }
+
+                }
+            }
+
+            return expiredSessionKeys;
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+            return expiredSessionKeys; //return whatever we got
+        } 
+       
+    }
+    public int getGracePeriodSec ()
+    {
+        return (int)(_gracePeriodMs == 0L? 0 : _gracePeriodMs/1000L);
+    }
+    
+    public void setGracePeriodSec (int sec)
+    {
+        if (sec < 0)
+            _gracePeriodMs = 0;
+        else
+            _gracePeriodMs = sec * 1000L;
+    }
+    
+    public void setDatabaseAdaptor (DatabaseAdaptor dbAdaptor)
+    {
+        checkStarted();
+        _dbAdaptor = dbAdaptor;
+    }
+    
+    public void setSessionTableSchema (SessionTableSchema schema)
+    {
+        checkStarted();
+        _sessionTableSchema = schema;
+    }
+
+    public void setLoadAttempts (int attempts)
+    {
+        checkStarted();
+        _attempts = attempts;
+    }
+
+    public int getLoadAttempts ()
+    {
+        return _attempts;
+    }
+    
+    public boolean loadAttemptsExhausted (String id)
+    {
+        AtomicInteger i = _unloadables.get(id);
+        if (i == null)
+            return false;
+        return (i.get() >= _attempts);
+    }
+    
+    public void setDeleteUnloadableSessions (boolean delete)
+    {
+        checkStarted();
+        _deleteUnloadables = delete;
+    }
+    
+    public boolean isDeleteUnloadableSessions ()
+    {
+        return _deleteUnloadables;
+    }
+    
+    
+    protected void incLoadAttempt (String id)
+    {
+        AtomicInteger i = new AtomicInteger(0);
+        AtomicInteger count = _unloadables.putIfAbsent(id, i);
+        if (count == null)
+            count = i;
+        count.incrementAndGet();
+    }
+    
+  
+    
+    public int getLoadAttempts (String id)
+    {
+        AtomicInteger i = _unloadables.get(id);
+        if (i == null)
+            return 0;
+        return i.get();
+    }
+    
+    public Set<String> getUnloadableSessions ()
+    {
+        return new HashSet<String>(_unloadables.keySet());
+    }
+    
+   public void clearUnloadableSessions()
+   {
+       _unloadables.clear();
+   }
+
+
+
+
+
+   /** 
+    * @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
+    */
+   @Override
+   public boolean isPassivating()
+   {
+       return true;
+   }
+}
+
+
+
+
+
+
+
+
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
index 359f8d3..cf1b5cb 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
@@ -18,35 +18,20 @@
 
 package org.eclipse.jetty.server.session;
 
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.sql.Blob;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
-import java.sql.Driver;
-import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.HashSet;
-import java.util.Locale;
 import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
 
-import javax.naming.InitialContext;
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
-import javax.sql.DataSource;
 
-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.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
-import org.eclipse.jetty.util.thread.Scheduler;
 
 
 
@@ -57,334 +42,19 @@
  * to support distributed sessions.
  *
  */
-public class JDBCSessionIdManager extends AbstractSessionIdManager
+public class JDBCSessionIdManager extends org.eclipse.jetty.server.session.AbstractSessionIdManager
 {
-    final static Logger LOG = SessionHandler.LOG;
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
     public final static int MAX_INTERVAL_NOT_SET = -999;
 
     protected final HashSet<String> _sessionIds = new HashSet<String>();
     protected Server _server;
-    protected Driver _driver;
-    protected String _driverClassName;
-    protected String _connectionUrl;
-    protected DataSource _datasource;
-    protected String _jndiName;
+    protected SessionScavenger _scavenger;
 
-    protected int _deleteBlockSize = 10; //number of ids to include in where 'in' clause
+    private DatabaseAdaptor _dbAdaptor = new DatabaseAdaptor();
 
-    protected Scheduler.Task _task; //scavenge task
-    protected Scheduler _scheduler;
-    protected Scavenger _scavenger;
-    protected boolean _ownScheduler;
-    protected long _lastScavengeTime;
-    protected long _scavengeIntervalMs = 1000L * 60 * 10; //10mins
-
-
-    protected String _createSessionIdTable;
-    protected String _createSessionTable;
-
-    protected String _selectBoundedExpiredSessions;
-    private String _selectExpiredSessions;
-    
-    protected String _insertId;
-    protected String _deleteId;
-    protected String _queryId;
-
-    protected  String _insertSession;
-    protected  String _deleteSession;
-    protected  String _updateSession;
-    protected  String _updateSessionNode;
-    protected  String _updateSessionAccessTime;
-
-    protected DatabaseAdaptor _dbAdaptor = new DatabaseAdaptor();
     protected SessionIdTableSchema _sessionIdTableSchema = new SessionIdTableSchema();
-    protected SessionTableSchema _sessionTableSchema = new SessionTableSchema();
-    
-  
-
- 
-    /**
-     * SessionTableSchema
-     *
-     */
-    public static class SessionTableSchema
-    {        
-        protected DatabaseAdaptor _dbAdaptor;
-        protected String _tableName = "JettySessions";
-        protected String _rowIdColumn = "rowId";
-        protected String _idColumn = "sessionId";
-        protected String _contextPathColumn = "contextPath";
-        protected String _virtualHostColumn = "virtualHost"; 
-        protected String _lastNodeColumn = "lastNode";
-        protected String _accessTimeColumn = "accessTime"; 
-        protected String _lastAccessTimeColumn = "lastAccessTime";
-        protected String _createTimeColumn = "createTime";
-        protected String _cookieTimeColumn = "cookieTime";
-        protected String _lastSavedTimeColumn = "lastSavedTime";
-        protected String _expiryTimeColumn = "expiryTime";
-        protected String _maxIntervalColumn = "maxInterval";
-        protected String _mapColumn = "map";
-        
-        
-        protected void setDatabaseAdaptor(DatabaseAdaptor dbadaptor)
-        {
-            _dbAdaptor = dbadaptor;
-        }
-        
-        
-        public String getTableName()
-        {
-            return _tableName;
-        }
-        public void setTableName(String tableName)
-        {
-            checkNotNull(tableName);
-            _tableName = tableName;
-        }
-        public String getRowIdColumn()
-        {       
-            if ("rowId".equals(_rowIdColumn) && _dbAdaptor.isRowIdReserved())
-                _rowIdColumn = "srowId";
-            return _rowIdColumn;
-        }
-        public void setRowIdColumn(String rowIdColumn)
-        {
-            checkNotNull(rowIdColumn);
-            if (_dbAdaptor == null)
-                throw new IllegalStateException ("DbAdaptor is null");
-            
-            if (_dbAdaptor.isRowIdReserved() && "rowId".equals(rowIdColumn))
-                throw new IllegalArgumentException("rowId is reserved word for Oracle");
-            
-            _rowIdColumn = rowIdColumn;
-        }
-        public String getIdColumn()
-        {
-            return _idColumn;
-        }
-        public void setIdColumn(String idColumn)
-        {
-            checkNotNull(idColumn);
-            _idColumn = idColumn;
-        }
-        public String getContextPathColumn()
-        {
-            return _contextPathColumn;
-        }
-        public void setContextPathColumn(String contextPathColumn)
-        {
-            checkNotNull(contextPathColumn);
-            _contextPathColumn = contextPathColumn;
-        }
-        public String getVirtualHostColumn()
-        {
-            return _virtualHostColumn;
-        }
-        public void setVirtualHostColumn(String virtualHostColumn)
-        {
-            checkNotNull(virtualHostColumn);
-            _virtualHostColumn = virtualHostColumn;
-        }
-        public String getLastNodeColumn()
-        {
-            return _lastNodeColumn;
-        }
-        public void setLastNodeColumn(String lastNodeColumn)
-        {
-            checkNotNull(lastNodeColumn);
-            _lastNodeColumn = lastNodeColumn;
-        }
-        public String getAccessTimeColumn()
-        {
-            return _accessTimeColumn;
-        }
-        public void setAccessTimeColumn(String accessTimeColumn)
-        {
-            checkNotNull(accessTimeColumn);
-            _accessTimeColumn = accessTimeColumn;
-        }
-        public String getLastAccessTimeColumn()
-        {
-            return _lastAccessTimeColumn;
-        }
-        public void setLastAccessTimeColumn(String lastAccessTimeColumn)
-        {
-            checkNotNull(lastAccessTimeColumn);
-            _lastAccessTimeColumn = lastAccessTimeColumn;
-        }
-        public String getCreateTimeColumn()
-        {
-            return _createTimeColumn;
-        }
-        public void setCreateTimeColumn(String createTimeColumn)
-        {
-            checkNotNull(createTimeColumn);
-            _createTimeColumn = createTimeColumn;
-        }
-        public String getCookieTimeColumn()
-        {
-            return _cookieTimeColumn;
-        }
-        public void setCookieTimeColumn(String cookieTimeColumn)
-        {
-            checkNotNull(cookieTimeColumn);
-            _cookieTimeColumn = cookieTimeColumn;
-        }
-        public String getLastSavedTimeColumn()
-        {
-            return _lastSavedTimeColumn;
-        }
-        public void setLastSavedTimeColumn(String lastSavedTimeColumn)
-        {
-            checkNotNull(lastSavedTimeColumn);
-            _lastSavedTimeColumn = lastSavedTimeColumn;
-        }
-        public String getExpiryTimeColumn()
-        {
-            return _expiryTimeColumn;
-        }
-        public void setExpiryTimeColumn(String expiryTimeColumn)
-        {
-            checkNotNull(expiryTimeColumn);
-            _expiryTimeColumn = expiryTimeColumn;
-        }
-        public String getMaxIntervalColumn()
-        {
-            return _maxIntervalColumn;
-        }
-        public void setMaxIntervalColumn(String maxIntervalColumn)
-        {
-            checkNotNull(maxIntervalColumn);
-            _maxIntervalColumn = maxIntervalColumn;
-        }
-        public String getMapColumn()
-        {
-            return _mapColumn;
-        }
-        public void setMapColumn(String mapColumn)
-        {
-            checkNotNull(mapColumn);
-            _mapColumn = mapColumn;
-        }
-        
-        public String getCreateStatementAsString ()
-        {
-            if (_dbAdaptor == null)
-                throw new IllegalStateException ("No DBAdaptor");
-            
-            String blobType = _dbAdaptor.getBlobType();
-            String longType = _dbAdaptor.getLongType();
-            
-            return "create table "+_tableName+" ("+getRowIdColumn()+" varchar(120), "+_idColumn+" varchar(120), "+
-                    _contextPathColumn+" varchar(60), "+_virtualHostColumn+" varchar(60), "+_lastNodeColumn+" varchar(60), "+_accessTimeColumn+" "+longType+", "+
-                    _lastAccessTimeColumn+" "+longType+", "+_createTimeColumn+" "+longType+", "+_cookieTimeColumn+" "+longType+", "+
-                    _lastSavedTimeColumn+" "+longType+", "+_expiryTimeColumn+" "+longType+", "+_maxIntervalColumn+" "+longType+", "+
-                    _mapColumn+" "+blobType+", primary key("+getRowIdColumn()+"))";
-        }
-        
-        public String getCreateIndexOverExpiryStatementAsString (String indexName)
-        {
-            return "create index "+indexName+" on "+getTableName()+" ("+getExpiryTimeColumn()+")";
-        }
-        
-        public String getCreateIndexOverSessionStatementAsString (String indexName)
-        {
-            return "create index "+indexName+" on "+getTableName()+" ("+getIdColumn()+", "+getContextPathColumn()+")";
-        }
-        
-        public String getAlterTableForMaxIntervalAsString ()
-        {
-            if (_dbAdaptor == null)
-                throw new IllegalStateException ("No DBAdaptor");
-            String longType = _dbAdaptor.getLongType();
-            String stem = "alter table "+getTableName()+" add "+getMaxIntervalColumn()+" "+longType;
-            if (_dbAdaptor.getDBName().contains("oracle"))
-                return stem + " default "+ MAX_INTERVAL_NOT_SET + " not null";
-            else
-                return stem +" not null default "+ MAX_INTERVAL_NOT_SET;
-        }
-        
-        private void checkNotNull(String s)
-        {
-            if (s == null)
-                throw new IllegalArgumentException(s);
-        }
-        public String getInsertSessionStatementAsString()
-        {
-           return "insert into "+getTableName()+
-            " ("+getRowIdColumn()+", "+getIdColumn()+", "+getContextPathColumn()+", "+getVirtualHostColumn()+", "+getLastNodeColumn()+
-            ", "+getAccessTimeColumn()+", "+getLastAccessTimeColumn()+", "+getCreateTimeColumn()+", "+getCookieTimeColumn()+
-            ", "+getLastSavedTimeColumn()+", "+getExpiryTimeColumn()+", "+getMaxIntervalColumn()+", "+getMapColumn()+") "+
-            " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
-        }
-        public String getDeleteSessionStatementAsString()
-        {
-            return "delete from "+getTableName()+
-            " where "+getRowIdColumn()+" = ?";
-        }
-        public String getUpdateSessionStatementAsString()
-        {
-            return "update "+getTableName()+
-                    " set "+getIdColumn()+" = ?, "+getLastNodeColumn()+" = ?, "+getAccessTimeColumn()+" = ?, "+
-                    getLastAccessTimeColumn()+" = ?, "+getLastSavedTimeColumn()+" = ?, "+getExpiryTimeColumn()+" = ?, "+
-                    getMaxIntervalColumn()+" = ?, "+getMapColumn()+" = ? where "+getRowIdColumn()+" = ?";
-        }
-        public String getUpdateSessionNodeStatementAsString()
-        {
-            return "update "+getTableName()+
-                    " set "+getLastNodeColumn()+" = ? where "+getRowIdColumn()+" = ?";
-        }
-        public String getUpdateSessionAccessTimeStatementAsString()
-        {
-           return "update "+getTableName()+
-            " set "+getLastNodeColumn()+" = ?, "+getAccessTimeColumn()+" = ?, "+getLastAccessTimeColumn()+" = ?, "+
-                   getLastSavedTimeColumn()+" = ?, "+getExpiryTimeColumn()+" = ?, "+getMaxIntervalColumn()+" = ? where "+getRowIdColumn()+" = ?";
-        }
-        
-        public String getBoundedExpiredSessionsStatementAsString()
-        {
-            return "select * from "+getTableName()+" where "+getLastNodeColumn()+" = ? and "+getExpiryTimeColumn()+" >= ? and "+getExpiryTimeColumn()+" <= ?";
-        }
-        
-        public String getSelectExpiredSessionsStatementAsString()
-        {
-            return "select * from "+getTableName()+" where "+getExpiryTimeColumn()+" >0 and "+getExpiryTimeColumn()+" <= ?";
-        }
-     
-        public PreparedStatement getLoadStatement (Connection connection, String rowId, String contextPath, String virtualHosts)
-        throws SQLException
-        { 
-            if (_dbAdaptor == null)
-                throw new IllegalStateException("No DB adaptor");
-
-
-            if (contextPath == null || "".equals(contextPath))
-            {
-                if (_dbAdaptor.isEmptyStringNull())
-                {
-                    PreparedStatement statement = connection.prepareStatement("select * from "+getTableName()+
-                                                                              " where "+getIdColumn()+" = ? and "+
-                                                                              getContextPathColumn()+" is null and "+
-                                                                              getVirtualHostColumn()+" = ?");
-                    statement.setString(1, rowId);
-                    statement.setString(2, virtualHosts);
-
-                    return statement;
-                }
-            }
-
-            PreparedStatement statement = connection.prepareStatement("select * from "+getTableName()+
-                                                                      " where "+getIdColumn()+" = ? and "+getContextPathColumn()+
-                                                                      " = ? and "+getVirtualHostColumn()+" = ?");
-            statement.setString(1, rowId);
-            statement.setString(2, contextPath);
-            statement.setString(3, virtualHosts);
-
-            return statement;
-        }
-    }
-    
-    
     
     /**
      * SessionIdTableSchema
@@ -392,14 +62,10 @@
      */
     public static class SessionIdTableSchema
     {
-        protected DatabaseAdaptor _dbAdaptor;
         protected String _tableName = "JettySessionIds";
         protected String _idColumn = "id";
-
-        public void setDatabaseAdaptor(DatabaseAdaptor dbAdaptor)
-        {
-            _dbAdaptor = dbAdaptor;
-        }
+        protected DatabaseAdaptor _jdbc;
+      
         public String getIdColumn()
         {
             return _idColumn;
@@ -442,399 +108,72 @@
             return "create table "+_tableName+" ("+_idColumn+" varchar(120), primary key("+_idColumn+"))";
         }
         
+        protected void prepareTables (DatabaseAdaptor jdbc)
+        throws Exception
+        {
+            _jdbc = jdbc;
+            try (Connection connection = _jdbc.getConnection();
+                 Statement statement = connection.createStatement())
+            {
+                //make the id table
+                connection.setAutoCommit(true);
+                DatabaseMetaData metaData = connection.getMetaData();
+                _jdbc.adaptTo(metaData);
+        
+                
+                //checking for table existence is case-sensitive, but table creation is not
+                String tableName = _jdbc.convertIdentifier(getTableName());
+                try (ResultSet result = metaData.getTables(null, null, tableName, null))
+                {
+                    if (!result.next())
+                    {
+                        //table does not exist, so create it
+                        statement.executeUpdate(getCreateStatementAsString());
+                    }
+                } 
+            }
+        }
+        
         private void checkNotNull(String s)
         {
             if (s == null)
                 throw new IllegalArgumentException(s);
         }
     }
-
-
-    /**
-     * DatabaseAdaptor
-     *
-     * Handles differences between databases.
-     *
-     * Postgres uses the getBytes and setBinaryStream methods to access
-     * a "bytea" datatype, which can be up to 1Gb of binary data. MySQL
-     * is happy to use the "blob" type and getBlob() methods instead.
-     *
-     * TODO if the differences become more major it would be worthwhile
-     * refactoring this class.
-     */
-    public static class DatabaseAdaptor
-    {
-        String _dbName;
-        boolean _isLower;
-        boolean _isUpper;
-        
-        protected String _blobType; //if not set, is deduced from the type of the database at runtime
-        protected String _longType; //if not set, is deduced from the type of the database at runtime
-
-
-        public DatabaseAdaptor ()
-        {           
-        }
-        
-        
-        public void adaptTo(DatabaseMetaData dbMeta)  
-        throws SQLException
-        {
-            _dbName = dbMeta.getDatabaseProductName().toLowerCase(Locale.ENGLISH);
-            if (LOG.isDebugEnabled())
-                LOG.debug ("Using database {}",_dbName);
-            _isLower = dbMeta.storesLowerCaseIdentifiers();
-            _isUpper = dbMeta.storesUpperCaseIdentifiers(); 
-        }
-        
-       
-        public void setBlobType(String blobType)
-        {
-            _blobType = blobType;
-        }
-        
-        public String getBlobType ()
-        {
-            if (_blobType != null)
-                return _blobType;
-
-            if (_dbName.startsWith("postgres"))
-                return "bytea";
-
-            return "blob";
-        }
-        
-
-        public void setLongType(String longType)
-        {
-            _longType = longType;
-        }
-        
-
-        public String getLongType ()
-        {
-            if (_longType != null)
-                return _longType;
-
-            if (_dbName == null)
-                throw new IllegalStateException ("DbAdaptor missing metadata");
-            
-            if (_dbName.startsWith("oracle"))
-                return "number(20)";
-
-            return "bigint";
-        }
-        
-
-        /**
-         * Convert a camel case identifier into either upper or lower
-         * depending on the way the db stores identifiers.
-         *
-         * @param identifier the raw identifier
-         * @return the converted identifier
-         */
-        public String convertIdentifier (String identifier)
-        {
-            if (_dbName == null)
-                throw new IllegalStateException ("DbAdaptor missing metadata");
-            
-            if (_isLower)
-                return identifier.toLowerCase(Locale.ENGLISH);
-            if (_isUpper)
-                return identifier.toUpperCase(Locale.ENGLISH);
-
-            return identifier;
-        }
-
-        public String getDBName ()
-        {
-            return _dbName;
-        }
-
-
-        public InputStream getBlobInputStream (ResultSet result, String columnName)
-        throws SQLException
-        {
-            if (_dbName == null)
-                throw new IllegalStateException ("DbAdaptor missing metadata");
-            
-            if (_dbName.startsWith("postgres"))
-            {
-                byte[] bytes = result.getBytes(columnName);
-                return new ByteArrayInputStream(bytes);
-            }
-
-            Blob blob = result.getBlob(columnName);
-            return blob.getBinaryStream();
-        }
-
-
-        public boolean isEmptyStringNull ()
-        {
-            if (_dbName == null)
-                throw new IllegalStateException ("DbAdaptor missing metadata");
-            
-            return (_dbName.startsWith("oracle"));
-        }
-        
-        /**
-         * rowId is a reserved word for Oracle, so change the name of this column
-         * @return true if db in use is oracle
-         */
-        public boolean isRowIdReserved ()
-        {
-            if (_dbName == null)
-                throw new IllegalStateException ("DbAdaptor missing metadata");
-            
-            return (_dbName != null && _dbName.startsWith("oracle"));
-        }
-    }
-
-    
-    /**
-     * Scavenger
-     *
-     */
-    protected class Scavenger implements Runnable
-    {
-
-        @Override
-        public void run()
-        {
-           try
-           {
-               scavenge();
-           }
-           finally
-           {
-               if (_scheduler != null && _scheduler.isRunning())
-                   _task = _scheduler.schedule(this, _scavengeIntervalMs, TimeUnit.MILLISECONDS);
-           }
-        }
-    }
-
-
+  
+   
     public JDBCSessionIdManager(Server server)
     {
-        super();
-        _server=server;
+        super(server);
     }
 
     public JDBCSessionIdManager(Server server, Random random)
     {
-       super(random);
-       _server=server;
+       super(server,random);
     }
 
-    /**
-     * Configure jdbc connection information via a jdbc Driver
-     *
-     * @param driverClassName the driver classname
-     * @param connectionUrl the driver connection url
-     */
-    public void setDriverInfo (String driverClassName, String connectionUrl)
-    {
-        _driverClassName=driverClassName;
-        _connectionUrl=connectionUrl;
-    }
-
-    /**
-     * Configure jdbc connection information via a jdbc Driver
-     *
-     * @param driverClass the driver class
-     * @param connectionUrl the driver connection url
-     */
-    public void setDriverInfo (Driver driverClass, String connectionUrl)
-    {
-        _driver=driverClass;
-        _connectionUrl=connectionUrl;
-    }
-
-
-    public void setDatasource (DataSource ds)
-    {
-        _datasource = ds;
-    }
-
-    public DataSource getDataSource ()
-    {
-        return _datasource;
-    }
-
-    public String getDriverClassName()
-    {
-        return _driverClassName;
-    }
-
-    public String getConnectionUrl ()
-    {
-        return _connectionUrl;
-    }
-
-    public void setDatasourceName (String jndi)
-    {
-        _jndiName=jndi;
-    }
-
-    public String getDatasourceName ()
-    {
-        return _jndiName;
-    }
-
-    /**
-     * @param name the name of the blob
-     * @deprecated see DbAdaptor.setBlobType
-     */
-    @Deprecated
-    public void setBlobType (String name)
-    {
-        _dbAdaptor.setBlobType(name);
-    }
-
-    public DatabaseAdaptor getDbAdaptor()
-    {
-        return _dbAdaptor;
-    }
-
-    public void setDbAdaptor(DatabaseAdaptor dbAdaptor)
-    {
-        if (dbAdaptor == null)
-            throw new IllegalStateException ("DbAdaptor cannot be null");
-        
-        _dbAdaptor = dbAdaptor;
-    }
-
-    /**
-     * @return the blob type
-     * @deprecated see DbAdaptor.getBlobType
-     */
-    @Deprecated
-    public String getBlobType ()
-    {
-        return _dbAdaptor.getBlobType();
-    }
-
-    /**
-     * @return the long type
-     * @deprecated see DbAdaptor.getLogType
-     */
-    @Deprecated
-    public String getLongType()
-    {
-        return _dbAdaptor.getLongType();
-    }
-
-    /**
-     * @param longType the long type
-     * @deprecated see DbAdaptor.setLongType
-     */
-    @Deprecated
-    public void setLongType(String longType)
-    {
-       _dbAdaptor.setLongType(longType);
-    }
-    
     public SessionIdTableSchema getSessionIdTableSchema()
     {
         return _sessionIdTableSchema;
     }
 
-    public void setSessionIdTableSchema(SessionIdTableSchema sessionIdTableSchema)
-    {
-        if (sessionIdTableSchema == null)
-            throw new IllegalArgumentException("Null SessionIdTableSchema");
-        
-        _sessionIdTableSchema = sessionIdTableSchema;
-    }
 
-    public SessionTableSchema getSessionTableSchema()
-    {
-        return _sessionTableSchema;
-    }
 
-    public void setSessionTableSchema(SessionTableSchema sessionTableSchema)
-    {
-        _sessionTableSchema = sessionTableSchema;
-    }
-
-    public void setDeleteBlockSize (int bsize)
-    {
-        this._deleteBlockSize = bsize;
-    }
-
-    public int getDeleteBlockSize ()
-    {
-        return this._deleteBlockSize;
-    }
     
-    public void setScavengeInterval (long sec)
-    {
-        if (sec<=0)
-            sec=60;
-
-        long old_period=_scavengeIntervalMs;
-        long period=sec*1000L;
-
-        _scavengeIntervalMs=period;
-
-        //add a bit of variability into the scavenge time so that not all
-        //nodes with the same scavenge time sync up
-        long tenPercent = _scavengeIntervalMs/10;
-        if ((System.currentTimeMillis()%2) == 0)
-            _scavengeIntervalMs += tenPercent;
-
-        if (LOG.isDebugEnabled())
-            LOG.debug("Scavenging every "+_scavengeIntervalMs+" ms");
-        
-        synchronized (this)
-        {
-            //if (_timer!=null && (period!=old_period || _task==null))
-            if (_scheduler != null && (period!=old_period || _task==null))
-            {
-                if (_task!=null)
-                    _task.cancel();
-                if (_scavenger == null)
-                    _scavenger = new Scavenger();
-                _task = _scheduler.schedule(_scavenger,_scavengeIntervalMs,TimeUnit.MILLISECONDS);
-            }
-        }
-    }
-
-    public long getScavengeInterval ()
-    {
-        return _scavengeIntervalMs/1000;
-    }
-
-
+    /** 
+     * Record the session id as being in use
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#useId(java.lang.String)
+     */
     @Override
-    public void addSession(HttpSession session)
+    public void useId (Session session)
     {
         if (session == null)
             return;
 
         synchronized (_sessionIds)
         {
-            String id = ((JDBCSessionManager.Session)session).getClusterId();
-            try
-            {
-                insert(id);
-                _sessionIds.add(id);
-            }
-            catch (Exception e)
-            {
-                LOG.warn("Problem storing session id="+id, e);
-            }
-        }
-    }
-    
-  
-    public void addSession(String id)
-    {
-        if (id == null)
-            return;
-
-        synchronized (_sessionIds)
-        {           
+            String id = session.getId();
             try
             {
                 insert(id);
@@ -849,22 +188,20 @@
 
 
 
+
+    /** 
+     * Remove the id from in-use set
+     * 
+     * Prevents another context from using this id
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#removeId(java.lang.String)
+     */
     @Override
-    public void removeSession(HttpSession session)
-    {
-        if (session == null)
-            return;
-
-        removeSession(((JDBCSessionManager.Session)session).getClusterId());
-    }
-
-
-
-    public void removeSession (String id)
+    public boolean removeId (String id)
     {
 
         if (id == null)
-            return;
+            return false;
 
         synchronized (_sessionIds)
         {
@@ -872,301 +209,19 @@
                 LOG.debug("Removing sessionid="+id);
             try
             {
-                _sessionIds.remove(id);
-                delete(id);
+                boolean remove = _sessionIds.remove(id);
+                boolean dbremove = delete(id);
+                return remove || dbremove;
             }
             catch (Exception e)
             {
                 LOG.warn("Problem removing session id="+id, e);
+                return false;
             }
         }
 
     }
 
-
-    @Override
-    public boolean idInUse(String id)
-    {
-        if (id == null)
-            return false;
-
-        String clusterId = getClusterId(id);
-        boolean inUse = false;
-        synchronized (_sessionIds)
-        {
-            inUse = _sessionIds.contains(clusterId);
-        }
-
-        
-        if (inUse)
-            return true; //optimisation - if this session is one we've been managing, we can check locally
-
-        //otherwise, we need to go to the database to check
-        try
-        {
-            return exists(clusterId);
-        }
-        catch (Exception e)
-        {
-            LOG.warn("Problem checking inUse for id="+clusterId, e);
-            return false;
-        }
-    }
-
-    /**
-     * Invalidate the session matching the id on all contexts.
-     *
-     * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
-     */
-    @Override
-    public void invalidateAll(String id)
-    {
-        //take the id out of the list of known sessionids for this node
-        removeSession(id);
-
-        synchronized (_sessionIds)
-        {
-            //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 JDBCSessionManager)
-                    {
-                        ((JDBCSessionManager)manager).invalidateSession(id);
-                    }
-                }
-            }
-        }
-    }
-
-
-    @Override
-    public void renewSessionId (String oldClusterId, String oldNodeId, HttpServletRequest request)
-    {
-        //generate a new id
-        String newClusterId = newSessionId(request.hashCode());
-
-        synchronized (_sessionIds)
-        {
-            removeSession(oldClusterId);//remove the old one from the list (and database)
-            addSession(newClusterId); //add in the new session id to the list (and database)
-
-            //tell all contexts to update the id 
-            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 JDBCSessionManager)
-                    {
-                        ((JDBCSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
-                    }
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Start up the id manager.
-     *
-     * Makes necessary database tables and starts a Session
-     * scavenger thread.
-     */
-    @Override
-    public void doStart()
-    throws Exception
-    {           
-        initializeDatabase();
-        prepareTables();   
-        super.doStart();
-        if (LOG.isDebugEnabled()) 
-            LOG.debug("Scavenging interval = "+getScavengeInterval()+" sec");
-        
-         //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");
-  
-        setScavengeInterval(getScavengeInterval());
-    }
-
-    /**
-     * Stop the scavenger.
-     */
-    @Override
-    public void doStop ()
-    throws Exception
-    {
-        synchronized(this)
-        {
-            if (_task!=null)
-                _task.cancel();
-            _task=null;
-            if (_ownScheduler && _scheduler !=null)
-                _scheduler.stop();
-            _scheduler=null;
-        }
-        _sessionIds.clear();
-        super.doStop();
-    }
-
-    /**
-     * Get a connection from the driver or datasource.
-     *
-     * @return the connection for the datasource
-     * @throws SQLException if unable to get the connection
-     */
-    protected Connection getConnection ()
-    throws SQLException
-    {
-        if (_datasource != null)
-            return _datasource.getConnection();
-        else
-            return DriverManager.getConnection(_connectionUrl);
-    }
-
-    /**
-     * Set up the tables in the database
-     * @throws SQLException
-     */
-    /**
-     * @throws SQLException
-     */
-    private void prepareTables()
-    throws SQLException
-    {
-        if (_sessionIdTableSchema == null)
-            throw new IllegalStateException ("No SessionIdTableSchema");
-        
-        if (_sessionTableSchema == null)
-            throw new IllegalStateException ("No SessionTableSchema");
-        
-        try (Connection connection = getConnection();
-             Statement statement = connection.createStatement())
-        {
-            //make the id table
-            connection.setAutoCommit(true);
-            DatabaseMetaData metaData = connection.getMetaData();
-            _dbAdaptor.adaptTo(metaData);
-            _sessionTableSchema.setDatabaseAdaptor(_dbAdaptor);
-            _sessionIdTableSchema.setDatabaseAdaptor(_dbAdaptor);
-            
-            _createSessionIdTable = _sessionIdTableSchema.getCreateStatementAsString();
-            _insertId = _sessionIdTableSchema.getInsertStatementAsString();
-            _deleteId =  _sessionIdTableSchema.getDeleteStatementAsString();
-            _queryId = _sessionIdTableSchema.getSelectStatementAsString();
-            
-            //checking for table existence is case-sensitive, but table creation is not
-            String tableName = _dbAdaptor.convertIdentifier(_sessionIdTableSchema.getTableName());
-            try (ResultSet result = metaData.getTables(null, null, tableName, null))
-            {
-                if (!result.next())
-                {
-                    //table does not exist, so create it
-                    statement.executeUpdate(_createSessionIdTable);
-                }
-            }         
-            
-            //make the session table if necessary
-            tableName = _dbAdaptor.convertIdentifier(_sessionTableSchema.getTableName());
-            try (ResultSet result = metaData.getTables(null, null, tableName, null))
-            {
-                if (!result.next())
-                {
-                    //table does not exist, so create it
-                    _createSessionTable = _sessionTableSchema.getCreateStatementAsString();
-                    statement.executeUpdate(_createSessionTable);
-                }
-                else
-                {
-                    //session table exists, check it has maxinterval column
-                    ResultSet colResult = null;
-                    try
-                    {
-                        colResult = metaData.getColumns(null, null,
-                                                        _dbAdaptor.convertIdentifier(_sessionTableSchema.getTableName()), 
-                                                        _dbAdaptor.convertIdentifier(_sessionTableSchema.getMaxIntervalColumn()));
-                    }
-                    catch (SQLException s)
-                    {
-                        LOG.warn("Problem checking if "+_sessionTableSchema.getTableName()+
-                                 " table contains "+_sessionTableSchema.getMaxIntervalColumn()+" column. Ensure table contains column definition: \""
-                                +_sessionTableSchema.getMaxIntervalColumn()+" long not null default -999\"");
-                        throw s;
-                    }
-                    try
-                    {
-                        if (!colResult.next())
-                        {
-                            try
-                            {
-                                //add the maxinterval column
-                                statement.executeUpdate(_sessionTableSchema.getAlterTableForMaxIntervalAsString());
-                            }
-                            catch (SQLException s)
-                            {
-                                LOG.warn("Problem adding "+_sessionTableSchema.getMaxIntervalColumn()+
-                                         " column. Ensure table contains column definition: \""+_sessionTableSchema.getMaxIntervalColumn()+
-                                         " long not null default -999\"");
-                                throw s;
-                            }
-                        }
-                    }
-                    finally
-                    {
-                        colResult.close();
-                    }
-                }
-            }
-            //make some indexes on the JettySessions table
-            String index1 = "idx_"+_sessionTableSchema.getTableName()+"_expiry";
-            String index2 = "idx_"+_sessionTableSchema.getTableName()+"_session";
-
-            boolean index1Exists = false;
-            boolean index2Exists = false;
-            try (ResultSet result = metaData.getIndexInfo(null, null, tableName, false, false))
-            {
-                while (result.next())
-                {
-                    String idxName = result.getString("INDEX_NAME");
-                    if (index1.equalsIgnoreCase(idxName))
-                        index1Exists = true;
-                    else if (index2.equalsIgnoreCase(idxName))
-                        index2Exists = true;
-                }
-            }
-            if (!index1Exists)
-                statement.executeUpdate(_sessionTableSchema.getCreateIndexOverExpiryStatementAsString(index1));
-            if (!index2Exists)
-                statement.executeUpdate(_sessionTableSchema.getCreateIndexOverSessionStatementAsString(index2));
-
-            //set up some strings representing the statements for session manipulation
-            _insertSession = _sessionTableSchema.getInsertSessionStatementAsString();
-            _deleteSession = _sessionTableSchema.getDeleteSessionStatementAsString();
-            _updateSession = _sessionTableSchema.getUpdateSessionStatementAsString();
-            _updateSessionNode = _sessionTableSchema.getUpdateSessionNodeStatementAsString();
-            _updateSessionAccessTime = _sessionTableSchema.getUpdateSessionAccessTimeStatementAsString();
-            _selectBoundedExpiredSessions = _sessionTableSchema.getBoundedExpiredSessionsStatementAsString();
-            _selectExpiredSessions = _sessionTableSchema.getSelectExpiredSessionsStatementAsString();
-        }
-    }
-
     /**
      * Insert a new used session id into the table.
      *
@@ -1176,8 +231,8 @@
     private void insert (String id)
     throws SQLException
     {
-        try (Connection connection = getConnection();
-                PreparedStatement query = connection.prepareStatement(_queryId))
+        try (Connection connection = _dbAdaptor.getConnection();
+             PreparedStatement query = connection.prepareStatement(_sessionIdTableSchema.getSelectStatementAsString()))
         {
             connection.setAutoCommit(true);
             query.setString(1, id);
@@ -1186,7 +241,7 @@
                 //only insert the id if it isn't in the db already
                 if (!result.next())
                 {
-                    try (PreparedStatement statement = connection.prepareStatement(_insertId))
+                    try (PreparedStatement statement = connection.prepareStatement(_sessionIdTableSchema.getInsertStatementAsString()))
                     {
                         statement.setString(1, id);
                         statement.executeUpdate();
@@ -1202,18 +257,18 @@
      * @param id
      * @throws SQLException
      */
-    private void delete (String id)
+    private boolean delete (String id)
     throws SQLException
     {
-        try (Connection connection = getConnection();
-                PreparedStatement statement = connection.prepareStatement(_deleteId))
+        try (Connection connection = _dbAdaptor.getConnection();
+             PreparedStatement statement = connection.prepareStatement(_sessionIdTableSchema.getDeleteStatementAsString()))
         {
             connection.setAutoCommit(true);
             statement.setString(1, id);
-            statement.executeUpdate();
+            return (statement.executeUpdate() > 0);
         }
     }
-
+    
 
     /**
      * Check if a session id exists.
@@ -1225,8 +280,8 @@
     private boolean exists (String id)
     throws SQLException
     {
-        try (Connection connection = getConnection();
-                PreparedStatement statement = connection.prepareStatement(_queryId))
+        try (Connection connection = _dbAdaptor.getConnection();
+             PreparedStatement statement = connection.prepareStatement(_sessionIdTableSchema.getSelectStatementAsString()))
         {
             connection.setAutoCommit(true);
             statement.setString(1, id);
@@ -1237,325 +292,101 @@
         }
     }
 
-    /**
-     * Look for sessions in the database that have expired.
-     *
-     * We do this in the SessionIdManager and not the SessionManager so
-     * that we only have 1 scavenger, otherwise if there are n SessionManagers
-     * there would be n scavengers, all contending for the database.
-     *
-     * We look first for sessions that expired in the previous interval, then
-     * for sessions that expired previously - these are old sessions that no
-     * node is managing any more and have become stuck in the database.
-     */
-    private void scavenge ()
+
+    @Override
+    public boolean isIdInUse(String id)
     {
-        Set<String> candidateIds = getAllCandidateExpiredSessionIds();
+        if (id == null)
+            return false;
+
+        String sessionId = getId(id);
+        boolean inUse = false;
+        synchronized (_sessionIds)
+        {
+            inUse = _sessionIds.contains(sessionId);
+        }
+
         
-        Connection connection = null;
+        if (inUse)
+            return true; //optimisation - if this session is one we've been managing, we can check locally
+
+        //otherwise, we need to go to the database to check
         try
         {
-            if (LOG.isDebugEnabled())
-                LOG.debug(getWorkerName()+"- Scavenge sweep started at "+System.currentTimeMillis());
-            if (_lastScavengeTime > 0)
-            {
-                connection = getConnection();
-                connection.setAutoCommit(true);
-                Set<String> expiredSessionIds = new HashSet<String>();
-                
-                
-                //Pass 1: find sessions for which we were last managing node that have just expired since last pass
-                long lowerBound = (_lastScavengeTime - _scavengeIntervalMs);
-                long upperBound = _lastScavengeTime;
-                if (LOG.isDebugEnabled())
-                    LOG.debug (getWorkerName()+"- Pass 1: Searching for sessions expired between "+lowerBound + " and "+upperBound);
-
-                try (PreparedStatement statement = connection.prepareStatement(_selectBoundedExpiredSessions))
-                {
-                    statement.setString(1, getWorkerName());
-                    statement.setLong(2, lowerBound);
-                    statement.setLong(3, upperBound);
-                    try (ResultSet result = statement.executeQuery())
-                    {
-                        while (result.next())
-                        {
-                            String sessionId = result.getString(_sessionTableSchema.getIdColumn());
-                            expiredSessionIds.add(sessionId);
-                            if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId);
-                        }
-                    }
-                }
-                scavengeSessions(candidateIds, expiredSessionIds, false);
-
-
-                //Pass 2: find sessions that have expired a while ago for which this node was their last manager
-                try (PreparedStatement selectExpiredSessions = connection.prepareStatement(_selectExpiredSessions))
-                {
-                    expiredSessionIds.clear();
-                    upperBound = _lastScavengeTime - (2 * _scavengeIntervalMs);
-                    if (upperBound > 0)
-                    {
-                        if (LOG.isDebugEnabled()) LOG.debug(getWorkerName()+"- Pass 2: Searching for sessions expired before "+upperBound);
-                        selectExpiredSessions.setLong(1, upperBound);
-                        try (ResultSet result = selectExpiredSessions.executeQuery())
-                        {
-                            while (result.next())
-                            {
-                                String sessionId = result.getString(_sessionTableSchema.getIdColumn());
-                                String lastNode = result.getString(_sessionTableSchema.getLastNodeColumn());
-                                if ((getWorkerName() == null && lastNode == null) || (getWorkerName() != null && getWorkerName().equals(lastNode)))
-                                    expiredSessionIds.add(sessionId);
-                                if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId+" last managed by "+getWorkerName());
-                            }
-                        }
-                        scavengeSessions(candidateIds, expiredSessionIds, false);
-                    }
-
-
-                    //Pass 3:
-                    //find all sessions that have expired at least a couple of scanIntervals ago
-                    //if we did not succeed in loading them (eg their related context no longer exists, can't be loaded etc) then
-                    //they are simply deleted
-                    upperBound = _lastScavengeTime - (3 * _scavengeIntervalMs);
-                    expiredSessionIds.clear();
-                    if (upperBound > 0)
-                    {
-                        if (LOG.isDebugEnabled()) LOG.debug(getWorkerName()+"- Pass 3: searching for sessions expired before "+upperBound);
-                        selectExpiredSessions.setLong(1, upperBound);
-                        try (ResultSet result = selectExpiredSessions.executeQuery())
-                        {
-                            while (result.next())
-                            {
-                                String sessionId = result.getString(_sessionTableSchema.getIdColumn());
-                                expiredSessionIds.add(sessionId);
-                                if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId="+sessionId);
-                            }
-                        }
-                        scavengeSessions(candidateIds, expiredSessionIds, true);
-                    }
-                }
-                
-                //Tell session managers to check remaining sessions in memory that may have expired 
-                //but are no longer in the database
-                scavengeSessions(candidateIds);
-            }
+            return exists(sessionId);
         }
         catch (Exception e)
         {
-            if (isRunning())
-                LOG.warn("Problem selecting expired sessions", e);
-            else
-                LOG.ignore(e);
-        }
-        finally
-        {
-            _lastScavengeTime=System.currentTimeMillis();
-            if (LOG.isDebugEnabled()) LOG.debug(getWorkerName()+"- Scavenge sweep ended at "+_lastScavengeTime);
-            if (connection != null)
-            {
-                try
-                {
-                    connection.close();
-                }
-                catch (SQLException e)
-                {
-                    LOG.warn(e);
-                }
-            }
+            LOG.warn("Problem checking inUse for id="+sessionId, e);
+            return false;
         }
     }
-    
-    
+ 
+
     /**
-     * @param expiredSessionIds
+     * Invalidate the session matching the id on all contexts.
+     *
+     * @see org.eclipse.jetty.server.SessionIdManager#expireAll(java.lang.String)
      */
-    private void scavengeSessions (Set<String> candidateIds, Set<String> expiredSessionIds, boolean forceDelete)
+    @Override
+    public void expireAll(String id)
     {       
-        Set<String> remainingIds = new HashSet<String>(expiredSessionIds);
-        Set<SessionManager> managers = getAllSessionManagers();
-        for (SessionManager m:managers)
+        synchronized (_sessionIds)
         {
-            Set<String> successfullyExpiredIds = ((JDBCSessionManager)m).expire(expiredSessionIds);
-            if (successfullyExpiredIds != null)
-            {
-                remainingIds.removeAll(successfullyExpiredIds);
-                candidateIds.removeAll(successfullyExpiredIds);
-            }
-        }
-    
-
-        //Any remaining ids are of those sessions that no context removed
-        if (!remainingIds.isEmpty() && forceDelete)
-        {
-            LOG.info("Forcibly deleting unrecoverable expired sessions {}", remainingIds);
-            try
-            {
-                //ensure they aren't in the local list of in-use session ids
-                synchronized (_sessionIds)
-                {
-                    _sessionIds.removeAll(remainingIds);
-                }
-                
-                cleanExpiredSessionIds(remainingIds);
-            }
-            catch (Exception e)
-            {
-                LOG.warn("Error removing expired session ids", e);
-            }
+           super.expireAll(id);
         }
     }
+
+
     
+    @Override
+    public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request)
+    {
+        synchronized (_sessionIds)
+        {
+            super.renewSessionId(oldClusterId, oldNodeId, request);
+        }
+    }
+
     /**
-     * These are the session ids that the session managers thought had 
-     * expired, but were not expired in the database. This could be
-     * because the session is live on another node, or that the
-     * session no longer exists in the database because some other
-     * node removed it.
-     * @param candidateIds
+     * Get the database adaptor in order to configure it
+     * @return
      */
-    private void scavengeSessions (Set<String> candidateIds)
+    public DatabaseAdaptor getDatabaseAdaptor ()
     {
-        if (candidateIds.isEmpty())
-            return;
-        
-        
-        Set<SessionManager> managers = getAllSessionManagers();
-        
-        for (SessionManager m:managers)
-        {
-            //tell the session managers to check the sessions that have expired in memory
-            //if they are no longer in the database, they should be removed
-            ((JDBCSessionManager)m).expireCandidates(candidateIds);
-        }
-    }
-    
-    private Set<String>  getAllCandidateExpiredSessionIds()
-    {
-        HashSet<String> candidateIds = new HashSet<>();
-        
-        Set<SessionManager> managers = getAllSessionManagers();
-        
-        for (SessionManager m:managers)
-        {
-            candidateIds.addAll(((JDBCSessionManager)m).getCandidateExpiredIds());
-        }
-        
-        return candidateIds;
+        return _dbAdaptor;
     }
     
     
-    private Set<SessionManager> getAllSessionManagers()
-    {
-        HashSet<SessionManager> managers = new HashSet<>();
-    
-        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 JDBCSessionManager)
-                    managers.add(manager);
-            }
-        }
-        return managers;
-    }
-
-
-   
-    
-    private void cleanExpiredSessionIds (Set<String> expiredIds)
-    throws Exception
-    {
-        if (expiredIds == null || expiredIds.isEmpty())
-            return;
-        
-        String[] ids = expiredIds.toArray(new String[expiredIds.size()]);
-        try (Connection con = getConnection())
-        {
-            con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
-            con.setAutoCommit(false);
-
-            int start = 0;
-            int end = 0;
-            int blocksize = _deleteBlockSize;
-            int block = 0;
-       
-            try (Statement statement = con.createStatement())
-            {
-                while (end < ids.length)
-                {
-                    start = block*blocksize;
-                    if ((ids.length -  start)  >= blocksize)
-                        end = start + blocksize;
-                    else
-                        end = ids.length;
-
-                    //take them out of the sessionIds table
-                    statement.executeUpdate(fillInClause("delete from "+_sessionIdTableSchema.getTableName()+" where "+_sessionIdTableSchema.getIdColumn()+" in ", ids, start, end));
-                    //take them out of the sessions table
-                    statement.executeUpdate(fillInClause("delete from "+_sessionTableSchema.getTableName()+" where "+_sessionTableSchema.getIdColumn()+" in ", ids, start, end));
-                    block++;
-                }
-            }
-            catch (Exception e)
-            {
-                con.rollback();
-                throw e;
-            }
-            con.commit();
-        }
-    }
-
     
     
     /**
-     * 
-     * @param sql
-     * @param atoms
-     * @throws Exception
+     * Start up the id manager.
+     *
+     * Makes necessary database tables and starts a Session
+     * scavenger thread.
      */
-    private String fillInClause (String sql, String[] literals, int start, int end)
+    @Override
+    public void doStart()
     throws Exception
-    {
-        StringBuffer buff = new StringBuffer();
-        buff.append(sql);
-        buff.append("(");
-        for (int i=start; i<end; i++)
-        {
-            buff.append("'"+(literals[i])+"'");
-            if (i+1<end)
-                buff.append(",");
-        }
-        buff.append(")");
-        return buff.toString();
-    }
-    
-    
-    
-    private void initializeDatabase ()
-    throws Exception
-    {
-        if (_datasource != null)
-            return; //already set up
+    {                
+        _dbAdaptor.initialize();
+        _sessionIdTableSchema.prepareTables(_dbAdaptor);
         
-        if (_jndiName!=null)
-        {
-            InitialContext ic = new InitialContext();
-            _datasource = (DataSource)ic.lookup(_jndiName);
-        }
-        else if ( _driver != null && _connectionUrl != null )
-        {
-            DriverManager.registerDriver(_driver);
-        }
-        else if (_driverClassName != null && _connectionUrl != null)
-        {
-            Class.forName(_driverClassName);
-        }
-        else
-            throw new IllegalStateException("No database configured for sessions");
+        super.doStart();
+      
     }
-    
+
+    /**
+     * Stop
+     */
+    @Override
+    public void doStop ()
+    throws Exception
+    {
+        _sessionIds.clear();
+
+        super.doStop();
+    } 
    
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
index b788628..43018a7 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
@@ -19,1221 +19,56 @@
 
 package org.eclipse.jetty.server.session;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.ObjectOutputStream;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSessionEvent;
-import javax.servlet.http.HttpSessionListener;
-
-import org.eclipse.jetty.server.SessionIdManager;
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.server.session.JDBCSessionIdManager.SessionTableSchema;
-import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
 /**
- * JDBCSessionManager.
- * <p>
- * SessionManager that persists sessions to a database to enable clustering.
- * <p>
- * Session data is persisted to the JettySessions table:
- * <dl>
- * <dt>rowId</dt><dd>(unique in cluster: webapp name/path + virtualhost + sessionId)</dd>
- * <dt>contextPath</dt><dd>(of the context owning the session)</dd>
- * <dt>sessionId</dt><dd>(unique in a context)</dd>
- * <dt>lastNode</dt><dd>(name of node last handled session)</dd>
- * <dt>accessTime</dt><dd>(time in milliseconds session was accessed)</dd>
- * <dt>lastAccessTime</dt><dd>(previous time in milliseconds session was accessed)</dd>
- * <dt>createTime</dt><dd>(time in milliseconds session created)</dd>
- * <dt>cookieTime</dt><dd>(time in milliseconds session cookie created)</dd>
- * <dt>lastSavedTime</dt><dd>(last time in milliseconds session access times were saved)</dd>
- * <dt>expiryTime</dt><dd>(time in milliseconds that the session is due to expire)</dd>
- * <dt>map</dt><dd>(attribute map)</dd>
- * </dl>
+ * JDBCSessionManager
  *
- * As an optimization, to prevent thrashing the database, we do not persist
- * the accessTime and lastAccessTime every time the session is accessed. Rather,
- * we write it out every so often. The frequency is controlled by the saveIntervalSec
- * field.
  */
-public class JDBCSessionManager extends AbstractSessionManager
+public class JDBCSessionManager extends SessionManager
 {
-    private static final Logger LOG = Log.getLogger(JDBCSessionManager.class);
+  
+    protected DatabaseAdaptor _db;
+    protected JDBCSessionDataStore _sessionDataStore;
 
-    private ConcurrentHashMap<String, Session> _sessions;
-    protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
-    protected long _saveIntervalSec = 60; //only persist changes to session access times every 60 secs
-    protected SessionTableSchema _sessionTableSchema;
-
-   
-
-
-    /**
-     * Session
-     *
-     * Session instance.
-     */
-    public class Session extends MemSession
-    {
-        private static final long serialVersionUID = 5208464051134226143L;
-        
-        /**
-         * If dirty, session needs to be (re)persisted
-         */
-        protected boolean _dirty=false;
-        
-        
-     
-        
-        
-        /**
-         * Time in msec since the epoch that the session will expire
-         */
-        protected long _expiryTime;
-        
-        
-        /**
-         * Time in msec since the epoch that the session was last persisted
-         */
-        protected long _lastSaved;
-        
-        
-        /**
-         * Unique identifier of the last node to host the session
-         */
-        protected String _lastNode;
-        
-        
-        /**
-         * Virtual host for context (used to help distinguish 2 sessions with same id on different contexts)
-         */
-        protected String _virtualHost;
-        
-        
-        /**
-         * Unique row in db for session
-         */
-        protected String _rowId;
-        
-        
-        /**
-         * Mangled context name (used to help distinguish 2 sessions with same id on different contexts)
-         */
-        protected String _canonicalContext;
-        
-   
-        /**
-         * Session from a request.
-         *
-         * @param request the request
-         */
-        protected Session (HttpServletRequest request)
-        {
-            super(JDBCSessionManager.this,request);
-            int maxInterval=getMaxInactiveInterval();
-            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
-            _virtualHost = JDBCSessionManager.getVirtualHost(_context);
-            _canonicalContext = canonicalize(_context.getContextPath());
-            _lastNode = getSessionIdManager().getWorkerName();
-        }
-        
-        
-        /**
-         * Session restored from database
-         * @param sessionId the session id
-         * @param rowId the row id
-         * @param created the created timestamp
-         * @param accessed the access timestamp
-         * @param maxInterval the max inactive interval (in seconds)
-         */
-        protected Session (String sessionId, String rowId, long created, long accessed, long maxInterval)
-        {
-            super(JDBCSessionManager.this, created, accessed, sessionId);
-            _rowId = rowId;
-            super.setMaxInactiveInterval((int)maxInterval); //restore the session's previous inactivity interval setting
-            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
-        }
-        
-        
-        protected synchronized String getRowId()
-        {
-            return _rowId;
-        }
-        
-        protected synchronized void setRowId(String rowId)
-        {
-            _rowId = rowId;
-        }
-        
-        public synchronized void setVirtualHost (String vhost)
-        {
-            _virtualHost=vhost;
-        }
-
-        public synchronized String getVirtualHost ()
-        {
-            return _virtualHost;
-        }
-        
-        public synchronized long getLastSaved ()
-        {
-            return _lastSaved;
-        }
-
-        public synchronized void setLastSaved (long time)
-        {
-            _lastSaved=time;
-        }
-
-        public synchronized void setExpiryTime (long time)
-        {
-            _expiryTime=time;
-        }
-
-        public synchronized long getExpiryTime ()
-        {
-            return _expiryTime;
-        }
-        
-
-        public synchronized void setCanonicalContext(String str)
-        {
-            _canonicalContext=str;
-        }
-
-        public synchronized String getCanonicalContext ()
-        {
-            return _canonicalContext;
-        }
-        
-       
-        public synchronized void setLastNode (String node)
-        {
-            _lastNode=node;
-        }
-
-        public synchronized String getLastNode ()
-        {
-            return _lastNode;
-        }
-
-        @Override
-        public void setAttribute (String name, Object value)
-        {
-            Object old = changeAttribute(name, value);
-            if (value == null && old == null)
-                return; //if same as remove attribute but attribute was already removed, no change
-            
-            _dirty = true;
-        }
-
-        @Override
-        public void removeAttribute (String name)
-        {
-            Object old = changeAttribute(name, null);
-            if (old != null) //only dirty if there was a previous value
-                _dirty=true;
-        }
-
-
-        /**
-         * Entry to session.
-         * Called by SessionHandler on inbound request and the session already exists in this node's memory.
-         *
-         * @see org.eclipse.jetty.server.session.AbstractSession#access(long)
-         */
-        @Override
-        protected boolean access(long time)
-        {
-            synchronized (this)
-            {
-                if (super.access(time))
-                {
-                    int maxInterval=getMaxInactiveInterval();
-                    _expiryTime = (maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
-                    return true;
-                }
-                return false;
-            }
-        }
-        
-        
-        
-
-
-        /** 
-         * Change the max idle time for this session. This recalculates the expiry time.
-         * @see org.eclipse.jetty.server.session.AbstractSession#setMaxInactiveInterval(int)
-         */
-        @Override
-        public void setMaxInactiveInterval(int secs)
-        {
-            synchronized (this)
-            {
-                super.setMaxInactiveInterval(secs);
-                int maxInterval=getMaxInactiveInterval();
-                _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
-                //force the session to be written out right now
-                try
-                {
-                    updateSessionAccessTime(this);
-                }
-                catch (Exception e)
-                {
-                    LOG.warn("Problem saving changed max idle time for session "+ this, e);
-                }
-            }
-        }
-
-
-        /**
-         * Exit from session
-         * @see org.eclipse.jetty.server.session.AbstractSession#complete()
-         */
-        @Override
-        protected void complete()
-        {
-            synchronized (this)
-            {
-                super.complete();
-                try
-                {
-                    if (isValid())
-                    {
-                        if (_dirty)
-                        {
-                            //The session attributes have changed, write to the db, ensuring
-                            //http passivation/activation listeners called
-                            save(true);
-                        }
-                        else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
-                        {
-                            updateSessionAccessTime(this);
-                        }
-                    }
-                }
-                catch (Exception e)
-                {
-                    LOG.warn("Problem persisting changed session data id="+getId(), e);
-                }
-                finally
-                {
-                    _dirty=false;
-                }
-            }
-        }
-
-        protected void save() throws Exception
-        {
-            synchronized (this)
-            {
-                try
-                {
-                    updateSession(this);
-                }
-                finally
-                {
-                    _dirty = false;
-                }
-            }
-        }
-
-        protected void save (boolean reactivate) throws Exception
-        {
-            synchronized (this)
-            {
-                if (_dirty)
-                {
-                    //The session attributes have changed, write to the db, ensuring
-                    //http passivation/activation listeners called
-                    willPassivate();                      
-                    updateSession(this);
-                    if (reactivate)
-                        didActivate();  
-                }
-            }
-        }
-
-        
-        @Override
-        protected void timeout() throws IllegalStateException
-        {
-            if (LOG.isDebugEnabled())
-                LOG.debug("Timing out session id="+getClusterId());
-            super.timeout();
-        }
-        
-        
-        @Override
-        public String toString ()
-        {
-            return "Session rowId="+_rowId+",id="+getId()+",lastNode="+_lastNode+
-                            ",created="+getCreationTime()+",accessed="+getAccessed()+
-                            ",lastAccessed="+getLastAccessedTime()+",cookieSet="+getCookieSetTime()+
-                            ",maxInterval="+getMaxInactiveInterval()+",lastSaved="+_lastSaved+",expiry="+_expiryTime;
-        }
-    }
-
-
-
-
-    /**
-     * Set the time in seconds which is the interval between
-     * saving the session access time to the database.
-     *
-     * This is an optimization that prevents the database from
-     * being overloaded when a session is accessed very frequently.
-     *
-     * On session exit, if the session attributes have NOT changed,
-     * the time at which we last saved the accessed
-     * time is compared to the current accessed time. If the interval
-     * is at least saveIntervalSecs, then the access time will be
-     * persisted to the database.
-     *
-     * If any session attribute does change, then the attributes and
-     * the accessed time are persisted.
-     *
-     * @param sec the save interval in seconds
-     */
-    public void setSaveInterval (long sec)
-    {
-        _saveIntervalSec=sec;
-    }
-
-    public long getSaveInterval ()
-    {
-        return _saveIntervalSec;
-    }
-
-
-
-    /**
-     * A method that can be implemented in subclasses to support
-     * distributed caching of sessions. This method will be
-     * called whenever the session is written to the database
-     * because the session data has changed.
-     *
-     * This could be used eg with a JMS backplane to notify nodes
-     * that the session has changed and to delete the session from
-     * the node's cache, and re-read it from the database.
-     * @param session the session to invalidate
-     */
-    public void cacheInvalidate (Session session)
-    {
-
-    }
-
-
-    /**
-     * A session has been requested by its id on this node.
-     *
-     * Load the session by id AND context path from the database.
-     * Multiple contexts may share the same session id (due to dispatching)
-     * but they CANNOT share the same contents.
-     *
-     * Check if last node id is my node id, if so, then the session we have
-     * in memory cannot be stale. If another node used the session last, then
-     * we need to refresh from the db.
-     *
-     * NOTE: this method will go to the database, so if you only want to check
-     * for the existence of a Session in memory, use _sessions.get(id) instead.
-     *
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
-     */
-    @Override
-    public Session getSession(String idInCluster)
-    {
-        Session session = null;
-        
-        synchronized (this)
-        {
-            Session memSession = (Session)_sessions.get(idInCluster);
-            
-            //check if we need to reload the session -
-            //as an optimization, don't reload on every access
-            //to reduce the load on the database. This introduces a window of
-            //possibility that the node may decide that the session is local to it,
-            //when the session has actually been live on another node, and then
-            //re-migrated to this node. This should be an extremely rare occurrence,
-            //as load-balancers are generally well-behaved and consistently send
-            //sessions to the same node, changing only iff that node fails.
-            //Session data = null;
-            long now = System.currentTimeMillis();
-            if (LOG.isDebugEnabled())
-            {
-                if (memSession==null)
-                    LOG.debug("getSession("+idInCluster+"): not in session map,"+
-                            " now="+now+
-                            " lastSaved="+(memSession==null?0:memSession._lastSaved)+
-                            " interval="+(_saveIntervalSec * 1000L));
-                else
-                    LOG.debug("getSession("+idInCluster+"): in session map, "+
-                            " hashcode="+memSession.hashCode()+
-                            " now="+now+
-                            " lastSaved="+(memSession==null?0:memSession._lastSaved)+
-                            " interval="+(_saveIntervalSec * 1000L)+
-                            " lastNode="+memSession._lastNode+
-                            " thisNode="+getSessionIdManager().getWorkerName()+
-                            " difference="+(now - memSession._lastSaved));
-            }
-
-            try
-            {
-                if (memSession==null)
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
-                    session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
-                }
-                else if ((now - memSession._lastSaved) >= (_saveIntervalSec * 1000L))
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
-                    session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
-                }
-                else
-                {
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("getSession("+idInCluster+"): session in session map");
-                    session = memSession;
-                }
-            }
-            catch (Exception e)
-            {
-                LOG.warn("Unable to load session "+idInCluster, e);
-                return null;
-            }
-
-
-            //If we have a session
-            if (session != null)
-            {
-                //If the session was last used on a different node, or session doesn't exist on this node
-                if (!session.getLastNode().equals(getSessionIdManager().getWorkerName()) || memSession==null)
-                {
-                    //if session doesn't expire, or has not already expired, update it and put it in this nodes' memory
-                    if (session._expiryTime <= 0 || session._expiryTime > now)
-                    {
-                        if (LOG.isDebugEnabled()) 
-                            LOG.debug("getSession("+idInCluster+"): lastNode="+session.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
-
-                        session.setLastNode(getSessionIdManager().getWorkerName());                            
-                        _sessions.put(idInCluster, session);
-                        _sessionsStats.increment();
-
-                        //update in db
-                        try
-                        {
-                            updateSessionNode(session);
-                            session.didActivate();
-                        }
-                        catch (Exception e)
-                        {
-                            LOG.warn("Unable to update freshly loaded session "+idInCluster, e);
-                            return null;
-                        }
-                    }
-                    else
-                    {
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("getSession ({}): Session has expired", idInCluster);
-                        //ensure that the session id for the expired session is deleted so that a new session with the 
-                        //same id cannot be created (because the idInUse() test would succeed)
-                        _jdbcSessionIdMgr.removeSession(idInCluster);
-                        session=null;
-                    }
-
-                }
-                else
-                {
-                    //the session loaded from the db and the one in memory are the same, so keep using the one in memory
-                    session = memSession;
-                    if (LOG.isDebugEnabled())
-                        LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
-                }
-            }
-            else
-            {
-                if (memSession != null)
-                {
-                    //Session must have been removed from db by another node
-                    removeSession(memSession, true);
-                }
-                //No session in db with matching id and context path.
-                LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
-            }
-
-            return session;
-        }
-    }
     
-
-    /**
-     * Get the number of sessions.
-     *
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSessions()
-     */
-    @Override
-    public int getSessions()
+    public JDBCSessionManager()
     {
-        return _sessions.size();
+        _db = new DatabaseAdaptor();
+        _sessionStore = new MemorySessionStore();
+        _sessionDataStore = new JDBCSessionDataStore();
     }
 
-
-    /**
-     * Start the session manager.
-     *
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
-     */
     @Override
     public void doStart() throws Exception
     {
-        if (_sessionIdManager==null)
-            throw new IllegalStateException("No session id manager defined");
-
-        _jdbcSessionIdMgr = (JDBCSessionIdManager)_sessionIdManager;
-        _sessionTableSchema = _jdbcSessionIdMgr.getSessionTableSchema();
-
-        _sessions = new ConcurrentHashMap<String, Session>();
-
+        _sessionDataStore.setDatabaseAdaptor(_db);
+        ((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore);
+        
         super.doStart();
     }
 
-
-    /**
-     * Stop the session manager.
-     *
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop()
-     */
     @Override
     public void doStop() throws Exception
     {
         super.doStop();
-        _sessions.clear();
-        _sessions = null;
-    }
-
-    @Override
-    protected void shutdownSessions()
-    {
-        //Save the current state of all of our sessions,
-        //do NOT delete them (so other nodes can manage them)
-        long gracefulStopMs = getContextHandler().getServer().getStopTimeout();
-        long stopTime = 0;
-        if (gracefulStopMs > 0)
-            stopTime = System.nanoTime() + (TimeUnit.NANOSECONDS.convert(gracefulStopMs, TimeUnit.MILLISECONDS));        
-
-        ArrayList<Session> sessions = (_sessions == null? new ArrayList<Session>() :new ArrayList<Session>(_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 (Session session : sessions)
-            {
-                try
-                {
-                    session.save(false);
-                }
-                catch (Exception e)
-                {
-                    LOG.warn(e);
-                }
-                _sessions.remove(session.getClusterId());
-            }
-
-            //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<Session>(_sessions.values());
-        }
     }
 
     
     /**
-     * 
-     * @see org.eclipse.jetty.server.SessionManager#renewSessionId(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
-     */
-    public void renewSessionId (String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
-    {
-        Session session = null;
-        try
-        {
-            session = (Session)_sessions.remove(oldClusterId);
-            if (session != null)
-            {
-                synchronized (session)
-                {
-                    session.setClusterId(newClusterId); //update ids
-                    session.setNodeId(newNodeId);
-                    _sessions.put(newClusterId, session); //put it into list in memory
-                    updateSession(session); //update database
-                }
-            }
-        }
-        catch (Exception e)
-        {
-            LOG.warn(e);
-        }
-
-        super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
-    }
-
-    
-
-    /**
-     * Invalidate a session.
-     *
-     * @param idInCluster the id in the cluster
-     */
-    protected void invalidateSession (String idInCluster)
-    {
-        Session session = (Session)_sessions.get(idInCluster);
-
-        if (session != null)
-        {
-            session.invalidate();
-        }
-    }
-
-    /**
-     * Delete an existing session, both from the in-memory map and
-     * the database.
-     *
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
-     */
-    @Override
-    protected boolean removeSession(String idInCluster)
-    {
-        Session session = (Session)_sessions.remove(idInCluster);
-        try
-        {
-            if (session != null)
-                deleteSession(session);
-        }
-        catch (Exception e)
-        {
-            LOG.warn("Problem deleting session id="+idInCluster, e);
-        }
-        return session!=null;
-    }
-
-
-    /**
-     * Add a newly created session to our in-memory list for this node and persist it.
-     *
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
-     */
-    @Override
-    protected void addSession(AbstractSession session)
-    {
-        if (session==null)
-            return;
-
-        _sessions.put(session.getClusterId(), (Session)session);
-
-        try
-        {
-            synchronized (session)
-            {
-                session.willPassivate();
-                storeSession(((JDBCSessionManager.Session)session));
-                session.didActivate();
-            }
-        }
-        catch (Exception e)
-        {
-            LOG.warn("Unable to store new session id="+session.getId() , e);
-        }
-    }
-
-
-    /**
-     * Make a new Session.
-     *
-     * @see org.eclipse.jetty.server.session.AbstractSessionManager#newSession(javax.servlet.http.HttpServletRequest)
-     */
-    @Override
-    protected AbstractSession newSession(HttpServletRequest request)
-    {
-        return new Session(request);
-    }
-    
-    protected AbstractSession newSession (String sessionId, String rowId, long created, long accessed, long maxInterval)
-    {
-        return new Session(sessionId, rowId, created, accessed, maxInterval);
-    }
-
-    /* ------------------------------------------------------------ */
-    /** Remove session from manager
-     * @param session The session to remove
-     * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
-     * {@link SessionIdManager#invalidateAll(String)} should be called.
-     */
-    @Override
-    public boolean removeSession(AbstractSession session, boolean invalidate)
-    {
-        // Remove session from context and global maps
-        boolean removed = super.removeSession(session, invalidate);
-
-        if (removed)
-        {
-            if (!invalidate)
-            {
-                session.willPassivate();
-            }
-        }
-        
-        return removed;
-    }
-
-
-    /**
-     * Expire any Sessions we have in memory matching the list of
-     * expired Session ids.
-     *
-     * @param sessionIds the session ids to expire
-     * @return the set of successfully expired ids
-     */
-    protected Set<String> expire (Set<String> sessionIds)
-    {
-        //don't attempt to scavenge if we are shutting down
-        if (isStopping() || isStopped())
-            return null;
-
-        
-        Thread thread=Thread.currentThread();
-        ClassLoader old_loader=thread.getContextClassLoader();
-        
-        Set<String> successfullyExpiredIds = new HashSet<String>();
-        try
-        {
-            Iterator<?> itor = sessionIds.iterator();
-            while (itor.hasNext())
-            {
-                String sessionId = (String)itor.next();
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Expiring session id "+sessionId);
-
-                Session session = (Session)_sessions.get(sessionId);
-
-                //if session is not in our memory, then fetch from db so we can call the usual listeners on it
-                if (session == null)
-                {
-                    if (LOG.isDebugEnabled())LOG.debug("Force loading session id "+sessionId);
-                    session = loadSession(sessionId, canonicalize(_context.getContextPath()), getVirtualHost(_context));
-                    if (session != null)
-                    {
-                        //loaded an expired session last managed on this node for this context, add it to the list so we can 
-                        //treat it like a normal expired session
-                        _sessions.put(session.getClusterId(), session);
-                        _sessionsStats.increment();
-                    }
-                    else
-                    {
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("Unrecognized session id="+sessionId);
-                        continue;
-                    }
-                }
-
-                if (session != null)
-                {
-                    session.timeout();
-                    successfullyExpiredIds.add(session.getClusterId());
-                }
-            }
-            return successfullyExpiredIds;
-        }
-        catch (Throwable t)
-        {
-            LOG.warn("Problem expiring sessions", t);
-            return successfullyExpiredIds;
-        }
-        finally
-        {
-            thread.setContextClassLoader(old_loader);
-        }
-    }
-    
-    protected void expireCandidates (Set<String> candidateIds)
-    {
-        Iterator<String> itor = candidateIds.iterator();
-        long now = System.currentTimeMillis();
-        while (itor.hasNext())
-        {
-            String id = itor.next();
-
-            //check if expired in db
-            try
-            {
-                Session memSession = _sessions.get(id);
-                if (memSession == null)
-                {
-                    continue; //no longer in memory
-                }
-
-                Session s = loadSession(id,  canonicalize(_context.getContextPath()), getVirtualHost(_context));
-                if (s == null)
-                {
-                    //session no longer exists, can be safely expired
-                    memSession.timeout();
-                }
-            }
-            catch (Exception e)
-            {
-                LOG.warn("Error checking db for expiry for session {}", id);
-            }
-        }
-    }
-    
-    protected Set<String> getCandidateExpiredIds ()
-    {
-        HashSet<String> expiredIds = new HashSet<>();
-
-        Iterator<String> itor = _sessions.keySet().iterator();
-        while (itor.hasNext())
-        {
-            String id = itor.next();
-            //check to see if session should have expired
-            Session session = _sessions.get(id);
-            if (session._expiryTime > 0 &&  System.currentTimeMillis() > session._expiryTime)
-                expiredIds.add(id);           
-        }
-        return expiredIds;
-    }
-
-
-    /**
-     * Load a session from the database
-     * @param id the id
-     * @param canonicalContextPath the canonical context path
-     * @param vhost the virtual host
-     * @return the session data that was loaded
-     * @throws Exception if unable to load the session
-     */
-    protected Session loadSession (final String id, final String canonicalContextPath, final String vhost)
-    throws Exception
-    {
-        final AtomicReference<Session> _reference = new AtomicReference<Session>();
-        final AtomicReference<Exception> _exception = new AtomicReference<Exception>();
-        Runnable load = new Runnable()
-        {
-            /** 
-             * @see java.lang.Runnable#run()
-             */
-            @SuppressWarnings("unchecked")
-            public void run()
-            {
-                try (Connection connection = getConnection();
-                        PreparedStatement statement = _sessionTableSchema.getLoadStatement(connection, id, canonicalContextPath, vhost);
-                        ResultSet result = statement.executeQuery())
-                {
-                    Session session = null;
-                    if (result.next())
-                    {                    
-                        long maxInterval = result.getLong(_sessionTableSchema.getMaxIntervalColumn());
-                        if (maxInterval == JDBCSessionIdManager.MAX_INTERVAL_NOT_SET)
-                        {
-                            maxInterval = getMaxInactiveInterval(); //if value not saved for maxInactiveInterval, use current value from sessionmanager
-                        }
-                        session = (Session)newSession(id, result.getString(_sessionTableSchema.getRowIdColumn()), 
-                                                  result.getLong(_sessionTableSchema.getCreateTimeColumn()), 
-                                                  result.getLong(_sessionTableSchema.getAccessTimeColumn()), 
-                                                  maxInterval);
-                        session.setCookieSetTime(result.getLong(_sessionTableSchema.getCookieTimeColumn()));
-                        session.setLastAccessedTime(result.getLong(_sessionTableSchema.getLastAccessTimeColumn()));
-                        session.setLastNode(result.getString(_sessionTableSchema.getLastNodeColumn()));
-                        session.setLastSaved(result.getLong(_sessionTableSchema.getLastSavedTimeColumn()));
-                        session.setExpiryTime(result.getLong(_sessionTableSchema.getExpiryTimeColumn()));
-                        session.setCanonicalContext(result.getString(_sessionTableSchema.getContextPathColumn()));
-                        session.setVirtualHost(result.getString(_sessionTableSchema.getVirtualHostColumn()));
-                                           
-                        try (InputStream is = ((JDBCSessionIdManager)getSessionIdManager())._dbAdaptor.getBlobInputStream(result, _sessionTableSchema.getMapColumn());
-                                ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
-                        {
-                            Object o = ois.readObject();
-                            session.addAttributes((Map<String,Object>)o);
-                        }
-
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("LOADED session "+session);
-                    }
-                    else
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("Failed to load session "+id);
-                    _reference.set(session);
-                }
-                catch (Exception e)
-                {
-                    _exception.set(e);
-                }
-            }
-        };
-
-        if (_context==null)
-            load.run();
-        else
-            _context.getContextHandler().handle(null,load);
-
-        if (_exception.get()!=null)
-        {
-            //if the session could not be restored, take its id out of the pool of currently-in-use
-            //session ids
-            _jdbcSessionIdMgr.removeSession(id);
-            throw _exception.get();
-        }
-
-        return _reference.get();
-    }
-
-    /**
-     * Insert a session into the database.
-     *
-     * @param session the session
-     * @throws Exception if unable to store the session
-     */
-    protected void storeSession (Session session)
-    throws Exception
-    {
-        if (session==null)
-            return;
-
-        //put into the database
-        try (Connection connection = getConnection();
-                PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._insertSession))
-        {
-            String rowId = calculateRowId(session);
-
-            long now = System.currentTimeMillis();
-            connection.setAutoCommit(true);
-            statement.setString(1, rowId); //rowId
-            statement.setString(2, session.getClusterId()); //session id
-            statement.setString(3, session.getCanonicalContext()); //context path
-            statement.setString(4, session.getVirtualHost()); //first vhost
-            statement.setString(5, getSessionIdManager().getWorkerName());//my node id
-            statement.setLong(6, session.getAccessed());//accessTime
-            statement.setLong(7, session.getLastAccessedTime()); //lastAccessTime
-            statement.setLong(8, session.getCreationTime()); //time created
-            statement.setLong(9, session.getCookieSetTime());//time cookie was set
-            statement.setLong(10, now); //last saved time
-            statement.setLong(11, session.getExpiryTime());
-            statement.setLong(12, session.getMaxInactiveInterval());
-
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            ObjectOutputStream oos = new ObjectOutputStream(baos);
-            oos.writeObject(session.getAttributeMap());
-            oos.flush();
-            byte[] bytes = baos.toByteArray();
-
-            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
-            statement.setBinaryStream(13, bais, bytes.length);//attribute map as blob
-           
-
-            statement.executeUpdate();
-            session.setRowId(rowId); //set it on the in-memory data as well as in db
-            session.setLastSaved(now);
-        }
-        if (LOG.isDebugEnabled())
-            LOG.debug("Stored session "+session);
-    }
-
-
-    /**
-     * Update data on an existing persisted session.
-     *
-     * @param data the session
-     * @throws Exception if unable to update the session
-     */
-    protected void updateSession (Session data)
-    throws Exception
-    {
-        if (data==null)
-            return;
-
-        try (Connection connection = getConnection();
-                PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSession))
-        {
-            long now = System.currentTimeMillis();
-            connection.setAutoCommit(true);
-            statement.setString(1, data.getClusterId());
-            statement.setString(2, getSessionIdManager().getWorkerName());//my node id
-            statement.setLong(3, data.getAccessed());//accessTime
-            statement.setLong(4, data.getLastAccessedTime()); //lastAccessTime
-            statement.setLong(5, now); //last saved time
-            statement.setLong(6, data.getExpiryTime());
-            statement.setLong(7, data.getMaxInactiveInterval());
-
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            ObjectOutputStream oos = new ObjectOutputStream(baos);
-            oos.writeObject(data.getAttributeMap());
-            oos.flush();
-            byte[] bytes = baos.toByteArray();
-            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
-
-            statement.setBinaryStream(8, bais, bytes.length);//attribute map as blob
-            statement.setString(9, data.getRowId()); //rowId
-            statement.executeUpdate();
-
-            data.setLastSaved(now);
-        }
-        if (LOG.isDebugEnabled())
-            LOG.debug("Updated session "+data);
-    }
-
-
-    /**
-     * Update the node on which the session was last seen to be my node.
-     *
-     * @param data the session
-     * @throws Exception if unable to update the session node
-     */
-    protected void updateSessionNode (Session data)
-    throws Exception
-    {
-        String nodeId = getSessionIdManager().getWorkerName();
-        try (Connection connection = getConnection();
-                PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionNode))
-        {
-            connection.setAutoCommit(true);
-            statement.setString(1, nodeId);
-            statement.setString(2, data.getRowId());
-            statement.executeUpdate();
-        }
-        if (LOG.isDebugEnabled())
-            LOG.debug("Updated last node for session id="+data.getId()+", lastNode = "+nodeId);
-    }
-
-    /**
-     * Persist the time the session was last accessed.
-     *
-     * @param data the session
-     * @throws Exception
-     */
-    private void updateSessionAccessTime (Session data)
-    throws Exception
-    {
-        try (Connection connection = getConnection();
-                PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionAccessTime))
-        {
-            long now = System.currentTimeMillis();
-            connection.setAutoCommit(true);
-            statement.setString(1, getSessionIdManager().getWorkerName());
-            statement.setLong(2, data.getAccessed());
-            statement.setLong(3, data.getLastAccessedTime());
-            statement.setLong(4, now);
-            statement.setLong(5, data.getExpiryTime());
-            statement.setLong(6, data.getMaxInactiveInterval());
-            statement.setString(7, data.getRowId());
-          
-            statement.executeUpdate();
-            data.setLastSaved(now);
-        }
-        if (LOG.isDebugEnabled())
-            LOG.debug("Updated access time session id="+data.getId()+" with lastsaved="+data.getLastSaved());
-    }
-
-
-
-
-    /**
-     * Delete a session from the database. Should only be called
-     * when the session has been invalidated.
-     *
-     * @param data the session data
-     * @throws Exception if unable to delete the session
-     */
-    protected void deleteSession (Session data)
-    throws Exception
-    {
-        try (Connection connection = getConnection();
-                PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._deleteSession))
-        {
-            connection.setAutoCommit(true);
-            statement.setString(1, data.getRowId());
-            statement.executeUpdate();
-            if (LOG.isDebugEnabled())
-                LOG.debug("Deleted Session "+data);
-        }
-    }
-
-
-
-    /**
-     * Get a connection from the driver.
-     * @return
-     * @throws SQLException
-     */
-    private Connection getConnection ()
-    throws SQLException
-    {
-        return ((JDBCSessionIdManager)getSessionIdManager()).getConnection();
-    }
-
-    /**
-     * Calculate a unique id for this session across the cluster.
-     *
-     * Unique id is composed of: contextpath_virtualhost0_sessionid
-     * @param data
+     * Get the db adaptor to configure jdbc settings
      * @return
      */
-    private String calculateRowId (Session data)
+    public DatabaseAdaptor getDatabaseAdaptor()
     {
-        String rowId = canonicalize(_context.getContextPath());
-        rowId = rowId + "_" + getVirtualHost(_context);
-        rowId = rowId+"_"+data.getId();
-        return rowId;
+        return _db;
     }
-
+    
     /**
-     * Get the first virtual host for the context.
-     *
-     * Used to help identify the exact session/contextPath.
-     *
-     * @return 0.0.0.0 if no virtual host is defined
-     */
-    private static String getVirtualHost (ContextHandler.Context context)
-    {
-        String vhost = "0.0.0.0";
-
-        if (context==null)
-            return vhost;
-
-        String [] vhosts = context.getContextHandler().getVirtualHosts();
-        if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
-            return vhost;
-
-        return vhosts[0];
-    }
-
-    /**
-     * Make an acceptable file name from a context path.
-     *
-     * @param path
+     * Get the SessionDataStore to configure it
      * @return
      */
-    private static String canonicalize (String path)
+    public JDBCSessionDataStore getSessionDataStore ()
     {
-        if (path==null)
-            return "";
-
-        return path.replace('/', '_').replace('.','_').replace('\\','_');
+        return _sessionDataStore;
     }
+    
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/MemSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/MemSession.java
deleted file mode 100644
index 8ebed7e..0000000
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/MemSession.java
+++ /dev/null
@@ -1,146 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.server.session;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.servlet.http.HttpServletRequest;
-
-
-/**
- * MemSession
- *
- * A session whose data is kept in memory
- */
-public class MemSession extends AbstractSession
-{
-
-    private final Map<String,Object> _attributes=new HashMap<String, Object>();
-
-    protected MemSession(AbstractSessionManager abstractSessionManager, HttpServletRequest request)
-    {
-        super(abstractSessionManager, request);
-    }
-
-    public MemSession(AbstractSessionManager abstractSessionManager, long created, long accessed, String clusterId)
-    {
-        super(abstractSessionManager, created, accessed, clusterId);
-    }
-    
-    
-    /* ------------------------------------------------------------- */
-    @Override
-    public Map<String,Object> getAttributeMap()
-    {
-        return _attributes;
-    }
-   
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public int getAttributes()
-    {
-        synchronized (this)
-        {
-            checkValid();
-            return _attributes.size();
-        }
-    }
-
-    /* ------------------------------------------------------------ */
-    @SuppressWarnings({ "unchecked" })
-    @Override
-    public Enumeration<String> doGetAttributeNames()
-    {
-        List<String> names=_attributes==null?Collections.EMPTY_LIST:new ArrayList<String>(_attributes.keySet());
-        return Collections.enumeration(names);
-    }
-
-    
-    /* ------------------------------------------------------------ */
-    @Override
-    public Set<String> getNames()
-    {
-        synchronized (this)
-        {
-            return new HashSet<String>(_attributes.keySet());
-        }
-    }
-   
-    
-    /* ------------------------------------------------------------- */
-    @Override
-    public void clearAttributes()
-    {
-        while (_attributes!=null && _attributes.size()>0)
-        {
-            ArrayList<String> keys;
-            synchronized(this)
-            {
-                keys=new ArrayList<String>(_attributes.keySet());
-            }
-
-            Iterator<String> iter=keys.iterator();
-            while (iter.hasNext())
-            {
-                String key=(String)iter.next();
-
-                Object value;
-                synchronized(this)
-                {
-                    value=doPutOrRemove(key,null);
-                }
-                unbindValue(key,value);
-
-                ((AbstractSessionManager)getSessionManager()).doSessionAttributeListeners(this,key,value,null);
-            }
-        }
-        if (_attributes!=null)
-            _attributes.clear();
-    }
-
-    /* ------------------------------------------------------------ */
-    public void addAttributes(Map<String,Object> map)
-    {
-        _attributes.putAll(map);
-    }
-    
-    /* ------------------------------------------------------------ */
-    @Override
-    public Object doPutOrRemove(String name, Object value)
-    {
-        return value==null?_attributes.remove(name):_attributes.put(name,value);
-    }
-
-    /* ------------------------------------------------------------ */
-    @Override
-    public Object doGet(String name)
-    {
-        return _attributes.get(name);
-    }
-    
-}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/MemorySessionStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/MemorySessionStore.java
new file mode 100644
index 0000000..9ed3505
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/MemorySessionStore.java
@@ -0,0 +1,249 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.statistic.CounterStatistic;
+
+/**
+ * MemorySessionStore
+ *
+ * A session store that keeps its sessions in memory in a hashmap
+ */
+public class MemorySessionStore extends AbstractSessionStore
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
+    
+    protected ConcurrentHashMap<String, Session> _sessions = new ConcurrentHashMap<String, Session>();
+    
+    private final CounterStatistic _stats = new CounterStatistic();
+    
+    
+    /**
+     * MemorySession
+     *
+     *
+     */
+    public class MemorySession extends Session
+    {
+
+        /**
+         * @param manager
+         * @param request
+         * @param data
+         */
+        public MemorySession(HttpServletRequest request, SessionData data)
+        {
+            super(request, data);
+        }
+        
+        
+        
+
+        /**
+         * @param manager
+         * @param data
+         */
+        public MemorySession(SessionData data)
+        {
+            super(data);
+        }
+    }
+    
+    
+    
+    public MemorySessionStore ()
+    {
+       super();
+    }
+    
+    
+    public long getSessions ()
+    {
+        return _stats.getCurrent();
+    }
+    
+    
+    public long getSessionsMax()
+    {
+        return _stats.getMax();
+    }
+    
+    
+    public long getSessionsTotal()
+    {
+        return _stats.getTotal();
+    }
+    
+    public void resetStats()
+    {
+        _stats.reset();
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionStore#doGet(java.lang.String)
+     */
+    @Override
+    public Session doGet(String id)
+    {
+        if (id == null)
+            return null;
+        
+        Session session = _sessions.get(id);
+       
+        return session;
+    }
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionStore#doPutIfAbsent(java.lang.String, org.eclipse.jetty.server.session.Session)
+     */
+    @Override
+    public Session doPutIfAbsent(String id, Session session)
+    {
+        Session s = _sessions.putIfAbsent(id, session);
+        if (s == null)
+            _stats.increment();
+       return s;
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionStore#doExists(java.lang.String)
+     */
+    @Override
+    public boolean doExists(String id)
+    {
+       return _sessions.containsKey(id);
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionStore#doDelete(java.lang.String)
+     */
+    @Override
+    public Session doDelete(String id)
+    {
+        Session s = _sessions.remove(id);
+        if (s != null)
+            _stats.decrement();
+        return  s;
+    }
+    
+    
+
+
+    @Override
+    public Set<String> doGetExpiredCandidates()
+    {
+        Set<String> candidates = new HashSet<String>();
+        long now = System.currentTimeMillis();
+        
+        for (Session s:_sessions.values())
+        {
+            if (s.isExpiredAt(now))
+            {
+                candidates.add(s.getId());
+            }
+        }
+        return candidates;
+    }
+
+
+
+
+
+
+    @Override
+    public void shutdown ()
+    {
+        // loop over all the sessions in memory (a few times if necessary to catch sessions that have been
+        // added while we're running
+        int loop=100;
+        while (!_sessions.isEmpty() && loop-- > 0)
+        {
+            for (Session session: _sessions.values())
+            {
+                //if we have a backing store and the session is dirty make sure it is written out
+                if (_sessionDataStore != null)
+                {
+                    if (session.getSessionData().isDirty())
+                    {
+                        session.willPassivate();
+                        try
+                        {
+                            _sessionDataStore.store(session.getId(), session.getSessionData());
+                        }
+                        catch (Exception e)
+                        {
+                            LOG.warn(e);
+                        }
+                    }
+                    doDelete (session.getId()); //remove from memory
+                }
+                else
+                {
+                    //not preserving sessions on exit
+                    try
+                    {
+                        session.invalidate();
+                    }
+                    catch (Exception e)
+                    {
+                        LOG.ignore(e);
+                    }
+                }
+            }
+        }
+    }
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionStore#newSession(java.lang.String)
+     */
+    @Override
+    public Session newSession(HttpServletRequest request, String id, long time, long maxInactiveMs)
+    {
+        MemorySession s =  new MemorySession(request, _sessionDataStore.newSessionData(id, time, time, time, maxInactiveMs));
+        return s;
+    }
+
+
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionStore#newSession(org.eclipse.jetty.server.session.SessionData)
+     */
+    @Override
+    public Session newSession(SessionData data)
+    {
+        MemorySession s = new MemorySession (data);
+        return s;
+    }
+
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NeverStaleStrategy.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NeverStaleStrategy.java
new file mode 100644
index 0000000..26a4df9
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NeverStaleStrategy.java
@@ -0,0 +1,40 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+/**
+ * NeverStale
+ *
+ * This strategy says that a session never needs to be refreshed by the cluster.
+ * 
+ */
+public class NeverStaleStrategy implements StalenessStrategy
+{
+
+    /** 
+     * @see org.eclipse.jetty.server.session.StalenessStrategy#isStale(org.eclipse.jetty.server.session.Session)
+     */
+    @Override
+    public boolean isStale(Session session)
+    {
+        return false;
+    }
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionDataStore.java
new file mode 100644
index 0000000..1104418
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionDataStore.java
@@ -0,0 +1,88 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.util.Set;
+
+/**
+ * NullSessionDataStore
+ *
+ *
+ */
+public class NullSessionDataStore extends AbstractSessionDataStore
+{
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#load(java.lang.String)
+     */
+    @Override
+    public SessionData load(String id) throws Exception
+    {
+        return null;
+    }
+
+    
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#newSessionData(org.eclipse.jetty.server.session.SessionKey, long, long, long, long)
+     */
+    @Override
+    public SessionData newSessionData(String id, long created, long accessed, long lastAccessed, long maxInactiveMs)
+    {
+        return new SessionData(id, _context.getCanonicalContextPath(), _context.getVhost(), created, accessed, lastAccessed, maxInactiveMs);
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#delete(java.lang.String)
+     */
+    @Override
+    public boolean delete(String id) throws Exception
+    {
+       return true;
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionDataStore#doStore()
+     */
+    @Override
+    public void doStore(String id, SessionData data, boolean isNew) throws Exception
+    {
+        //noop
+    }
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#getExpired()
+     */
+    @Override
+    public Set<String> getExpired(Set<String> candidates)
+    {
+       return candidates; //whatever is suggested we accept
+    }
+
+
+    /** 
+     * @see org.eclipse.jetty.server.session.SessionDataStore#isPassivating()
+     */
+    @Override
+    public boolean isPassivating()
+    {
+        return false;
+    }
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java
new file mode 100644
index 0000000..474ba01
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java
@@ -0,0 +1,772 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionContext;
+import javax.servlet.http.HttpSessionEvent;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Locker;
+import org.eclipse.jetty.util.thread.Locker.Lock;
+
+
+
+
+/**
+ * Session
+ *
+ *
+ */
+public class Session implements SessionManager.SessionIf
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
+    
+    public final static String SESSION_CREATED_SECURE="org.eclipse.jetty.security.sessionCreatedSecure";
+    
+    
+    public enum State {VALID, INVALID, INVALIDATING};
+        
+    
+    protected SessionData _sessionData;
+    protected SessionManager _manager;
+    protected String _extendedId; //the _id plus the worker name
+    protected long _requests;
+    private boolean _idChanged;
+    private boolean _newSession;
+    private State _state = State.VALID; //state of the session:valid,invalid or being invalidated
+    private Locker _lock = new Locker();
+    
+    public Session (HttpServletRequest request, SessionData data)
+    {
+        _sessionData = data;
+        _newSession = true;
+        _requests = 1;
+    }
+    
+    
+    public Session (SessionData data)
+    {
+        _sessionData = data;
+        _requests = 1;
+    }
+    
+    
+    public void setSessionManager (SessionManager manager)
+    {
+        _manager = manager;
+    }
+    
+    
+    public void setExtendedId (String extendedId)
+    {
+        _extendedId = extendedId;
+    }
+    
+    /* ------------------------------------------------------------- */
+    protected void cookieSet()
+    {
+        try (Lock lock = lock())
+        {
+           _sessionData.setCookieSet(_sessionData.getAccessed());
+        }
+    }
+    /* ------------------------------------------------------------ */
+    protected boolean access(long time)
+    {
+        try (Lock lock=lock())
+        {
+            if (!isValid())
+                return false;
+            _newSession=false;
+            long lastAccessed = _sessionData.getAccessed();
+            _sessionData.setAccessed(time);
+            _sessionData.setLastAccessed(lastAccessed);
+            int maxInterval=getMaxInactiveInterval();
+           _sessionData.setExpiry(maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
+            if (isExpiredAt(time))
+            {
+                invalidate();
+                return false;
+            }
+            _requests++;
+            return true;
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected void complete()
+    {
+        try (Lock lock = lock())
+        {
+            _requests--;
+        }
+    }
+
+    
+
+    /* ------------------------------------------------------------- */
+    /** Check to see if session has expired as at the time given.
+     * @param time the time in milliseconds
+     * @return true if expired
+     */
+    protected boolean isExpiredAt(long time)
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            return _sessionData.isExpiredAt(time);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param nodename
+     */
+    public void setLastNode (String nodename)
+    {
+        _sessionData.setLastNode(nodename);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return
+     */
+    public String getLastNode ()
+    {
+        return _sessionData.getLastNode();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Call binding and attribute listeners based on the new and old
+     * values of the attribute.
+     * 
+     * @param name name of the attribute
+     * @param newValue  new value of the attribute
+     * @param oldValue previous value of the attribute
+     */
+    protected void callSessionAttributeListeners (String name, Object newValue, Object oldValue)
+    {
+        if (newValue==null || !newValue.equals(oldValue))
+        {
+            if (oldValue!=null)
+                unbindValue(name,oldValue);
+            if (newValue!=null)
+                bindValue(name,newValue);
+
+            if (_manager == null)
+                throw new IllegalStateException ("No session manager for session "+ _sessionData.getId());
+            _manager.doSessionAttributeListeners(this,name,oldValue,newValue);
+        }
+    }
+
+    
+    
+    /* ------------------------------------------------------------- */
+    /**
+     * Unbind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)}) 
+     * @param name the name with which the object is bound or unbound  
+     * @param value the bound value
+     */
+    public void unbindValue(java.lang.String name, Object value)
+    {
+        if (value!=null&&value instanceof HttpSessionBindingListener)
+            ((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this,name));
+    }
+    
+
+    /* ------------------------------------------------------------- */
+    /** 
+     * Bind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)}) 
+     * @param name the name with which the object is bound or unbound  
+     * @param value the bound value
+     */
+    public void bindValue(java.lang.String name, Object value)
+    {
+        if (value!=null&&value instanceof HttpSessionBindingListener)
+            ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this,name));
+    }
+
+
+    /* ------------------------------------------------------------- */
+    /**
+     * Call the activation listeners. This must be called holding the
+     * _lock.
+     */
+    public void didActivate()
+    {
+        HttpSessionEvent event = new HttpSessionEvent(this);
+        for (Iterator<String> iter = _sessionData.getKeys().iterator(); iter.hasNext();)
+        {
+            Object value = _sessionData.getAttribute(iter.next());
+            if (value instanceof HttpSessionActivationListener)
+            {
+                HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
+                listener.sessionDidActivate(event);
+            }
+        }
+    }
+
+
+    /* ------------------------------------------------------------- */
+    /**
+     * Call the passivation listeners. This must be called holding the
+     * _lock
+     */
+    public void willPassivate()
+    {
+        HttpSessionEvent event = new HttpSessionEvent(this);
+        for (Iterator<String> iter = _sessionData.getKeys().iterator(); iter.hasNext();)
+        {
+            Object value = _sessionData.getAttribute(iter.next());
+            if (value instanceof HttpSessionActivationListener)
+            {
+                HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
+                listener.sessionWillPassivate(event);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isValid()
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            return _state==State.VALID;
+        }
+    }
+
+
+    /* ------------------------------------------------------------- */
+    public long getCookieSetTime()
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            return _sessionData.getCookieSet();
+        }
+    }
+
+
+    /* ------------------------------------------------------------- */
+    @Override
+    public long getCreationTime() throws IllegalStateException
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            checkValidForRead();
+            return _sessionData.getCreated();
+        }
+    }
+
+
+
+    /** 
+     * @see javax.servlet.http.HttpSession#getId()
+     */
+    @Override
+    public String getId()
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            return _sessionData.getId();
+        }
+    }
+
+    
+    public String getExtendedId()
+    {
+        return _extendedId;
+    }
+    
+    public String getContextPath()
+    {
+        return _sessionData.getContextPath();
+    }
+
+    
+    public String getVHost ()
+    {
+        return _sessionData.getVhost();
+    }
+    
+
+    /** 
+     * @see javax.servlet.http.HttpSession#getLastAccessedTime()
+     */
+    @Override
+    public long getLastAccessedTime()
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            return _sessionData.getLastAccessed();
+        }
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpSession#getServletContext()
+     */
+    @Override
+    public ServletContext getServletContext()
+    {
+        if (_manager == null)
+            throw new IllegalStateException ("No session manager for session "+ _sessionData.getId());
+       return _manager._context;
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpSession#setMaxInactiveInterval(int)
+     */
+    @Override
+    public void setMaxInactiveInterval(int secs)
+    {
+        try (Lock lock = lock())
+        {
+            _sessionData.setMaxInactiveMs((long)secs*1000L);  
+            _sessionData.setExpiry(_sessionData.getMaxInactiveMs() <= 0 ? 0 : (System.currentTimeMillis() + _sessionData.getMaxInactiveMs()*1000L));
+            _sessionData.setDirty(true);
+        }
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpSession#getMaxInactiveInterval()
+     */
+    @Override
+    public int getMaxInactiveInterval()
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            return (int)(_sessionData.getMaxInactiveMs()/1000);
+        }
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpSession#getSessionContext()
+     */
+    @Override
+    public HttpSessionContext getSessionContext()
+    {
+        checkValidForRead();
+        return SessionManager.__nullSessionContext;
+    }
+
+    
+    public SessionManager getSessionManager()
+    {
+        return _manager;
+    }
+    
+    
+    /* ------------------------------------------------------------- */
+    /**
+     * asserts that the session is valid
+     * @throws IllegalStateException if the session is invalid
+     */
+    protected void checkValidForWrite() throws IllegalStateException
+    {    
+        if (!_lock.isLocked())
+            throw new IllegalStateException();
+
+        if (_state != State.VALID)
+            throw new IllegalStateException();
+    }
+    
+    
+    /* ------------------------------------------------------------- */
+    /**
+     * asserts that the session is valid
+     * @throws IllegalStateException if the session is invalid
+     */
+    protected void checkValidForRead () throws IllegalStateException
+    {
+        if (!_lock.isLocked())
+            throw new IllegalStateException();
+        if (_state == State.INVALID)
+            throw new IllegalStateException();
+    }
+    
+    
+
+
+    /** 
+     * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String)
+     */
+    @Override
+    public Object getAttribute(String name)
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            checkValidForRead();
+            return _sessionData.getAttribute(name);
+        }
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpSession#getValue(java.lang.String)
+     */
+    @Override
+    public Object getValue(String name)
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        { 
+            return _sessionData.getAttribute(name);
+        }
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpSession#getAttributeNames()
+     */
+    @Override
+    public Enumeration<String> getAttributeNames()
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            checkValidForRead();
+            final Iterator<String> itor = _sessionData.getKeys().iterator();
+            return new Enumeration<String> ()
+            {
+
+                @Override
+                public boolean hasMoreElements()
+                {
+                    return itor.hasNext();
+                }
+
+                @Override
+                public String nextElement()
+                {
+                    return itor.next();
+                }
+
+            };
+        }
+    }
+
+
+
+
+    /* ------------------------------------------------------------ */
+    public int getAttributes()
+    {
+        return _sessionData.getKeys().size();
+    }
+
+
+
+
+    /* ------------------------------------------------------------ */
+    public Set<String> getNames()
+    {
+        return Collections.unmodifiableSet(_sessionData.getKeys());
+    }
+
+
+    /* ------------------------------------------------------------- */
+    /**
+     * @deprecated As of Version 2.2, this method is replaced by
+     *             {@link #getAttributeNames}
+     */
+    @Deprecated
+    @Override
+    public String[] getValueNames() throws IllegalStateException
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            checkValidForRead();
+            Iterator<String> itor = _sessionData.getKeys().iterator();
+            if (!itor.hasNext())
+                return new String[0];
+            ArrayList<String> names = new ArrayList<String>();
+            while (itor.hasNext())
+                names.add(itor.next());
+            return names.toArray(new String[names.size()]);
+        }
+    }
+
+    /* ------------------------------------------------------------- */
+    /** 
+     * @see javax.servlet.http.HttpSession#setAttribute(java.lang.String, java.lang.Object)
+     */
+    @Override
+    public void setAttribute(String name, Object value)
+    {
+        Object old=null;
+        try (Lock lock = lock())
+        {
+            //if session is not valid, don't accept the set
+            checkValidForWrite();
+            old=_sessionData.setAttribute(name,value);
+        }
+        if (value == null && old == null)
+            return; //if same as remove attribute but attribute was already removed, no change
+        callSessionAttributeListeners(name, value, old);
+    }
+    
+    
+    
+    /* ------------------------------------------------------------- */
+    /** 
+     * @see javax.servlet.http.HttpSession#putValue(java.lang.String, java.lang.Object)
+     */
+    @Override
+    public void putValue(String name, Object value)
+    {
+        setAttribute(name,value);
+    }
+    
+    
+    
+    /* ------------------------------------------------------------- */
+    /** 
+     * @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String)
+     */
+    @Override
+    public void removeAttribute(String name)
+    {
+       setAttribute(name, null);
+    }
+    
+    
+    
+    /* ------------------------------------------------------------- */
+    /** 
+     * @see javax.servlet.http.HttpSession#removeValue(java.lang.String)
+     */
+    @Override
+    public void removeValue(String name)
+    {
+       setAttribute(name, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param request
+     */
+    public void renewId(HttpServletRequest request)
+    {
+        if (_manager == null)
+            throw new IllegalStateException ("No session manager for session "+ _sessionData.getId());
+        
+        String id = null;
+        String extendedId = null;
+        try (Lock lock = lock())
+        {
+            checkValidForWrite(); //don't renew id on a session that is not valid
+            id = _sessionData.getId(); //grab the values as they are now
+            extendedId = getExtendedId();
+        }
+        
+        _manager._sessionIdManager.renewSessionId(id, extendedId, request); 
+        setIdChanged(true);
+    }
+       
+    
+    /* ------------------------------------------------------------- */
+    /** Swap the id on a session from old to new, keeping the object
+     * the same.
+     * 
+     * @param oldId
+     * @param oldExtendedId
+     * @param newId
+     * @param newExtendedId
+     */
+    public void renewId (String oldId, String oldExtendedId, String newId, String newExtendedId)
+    {
+        try (Lock lock = lock())
+        {
+            checkValidForWrite(); //can't change id on invalid session
+            
+            if (!oldId.equals(getId()))
+                throw new IllegalStateException("Id clash detected on renewal: was "+oldId+" but is "+ getId());
+            
+            //save session with new id
+            _sessionData.setId(newId);
+            setExtendedId(newExtendedId);
+            _sessionData.setLastSaved(0); //forces an insert
+            _sessionData.setDirty(true);  //forces an insert
+        }
+    }
+
+    /* ------------------------------------------------------------- */
+    /** Called by users to invalidate a session, or called by the
+     * access method as a request enters the session if the session
+     * has expired, or called by manager as a result of scavenger
+     * expiring session
+     * 
+     * @see javax.servlet.http.HttpSession#invalidate()
+     */
+    @Override
+    public void invalidate()
+    {
+        if (_manager == null)
+            throw new IllegalStateException ("No session manager for session "+ _sessionData.getId());
+
+        boolean result = false;
+
+        try (Lock lock = lock())
+        {
+            switch (_state)
+            {
+                case INVALID:
+                {
+                    throw new IllegalStateException(); //spec does not allow invalidate of already invalid session
+                }
+                case VALID:
+                {
+                    //only first change from valid to invalidating should be actionable
+                    result = true;
+                    _state = State.INVALIDATING;
+                    break;
+                }
+                default:
+                {
+                    LOG.info("Session {} already being invalidated", _sessionData.getId());
+                }
+            }
+        }
+
+        try
+        {
+            //if the session was not already invalid, or in process of being invalidated, do invalidate
+            if (result)
+            {
+                //tell id mgr to remove session from all other contexts
+                ((AbstractSessionIdManager)_manager.getSessionIdManager()).invalidateAll(_sessionData.getId());
+            }
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+        }
+    }
+    
+    
+    
+    /* ------------------------------------------------------------- */
+    /** Grab the lock on the session
+     * @return
+     */
+    public Lock lock ()
+    {
+        return _lock.lock();
+    }
+
+
+
+
+
+    /* ------------------------------------------------------------- */
+    protected void doInvalidate() throws IllegalStateException
+    {
+        try (Lock lock = lock())
+        {
+            try
+            {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("invalidate {}",_sessionData.getId());
+                if (isValid())
+                {
+                    Set<String> keys = null;
+
+                    do
+                    {
+                        keys = _sessionData.getKeys();
+                        for (String key:keys)
+                        {
+                            Object  old=_sessionData.setAttribute(key,null);
+                            if (old == null)
+                                return; //if same as remove attribute but attribute was already removed, no change
+                            callSessionAttributeListeners(key, null, old);
+                        }
+
+                    }
+                    while (!keys.isEmpty());
+                }
+            }
+            finally
+            {
+                // mark as invalid
+                _state = State.INVALID;
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------- */
+    @Override
+    public boolean isNew() throws IllegalStateException
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            checkValidForRead();
+            return _newSession;
+        }
+    }
+    
+    
+
+    /* ------------------------------------------------------------- */
+    public void setIdChanged(boolean changed)
+    {
+        try (Lock lock = lock())
+        {
+            _idChanged=changed;
+        }
+    }
+    
+    
+    /* ------------------------------------------------------------- */
+    public boolean isIdChanged ()
+    {
+        try (Lock lock = _lock.lockIfNotHeld())
+        {
+            return _idChanged;
+        }
+    }
+    
+    
+    /* ------------------------------------------------------------- */
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager.SessionIf#getSession()
+     */
+    @Override
+    public Session getSession()
+    {
+        // TODO why is this used
+        return this;
+    }
+  
+    /* ------------------------------------------------------------- */
+    protected SessionData getSessionData()
+    {
+        return _sessionData;
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionContext.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionContext.java
new file mode 100644
index 0000000..3f49300
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionContext.java
@@ -0,0 +1,134 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandler.Context;
+
+/**
+ * SessionContext
+ *
+ * The worker name which identifies this server instance, and the particular
+ * Context.
+ * 
+ * A SessionManager is 1:1 with a SessionContext.
+ */
+public class SessionContext
+{
+    public final static String NULL_VHOST = "0.0.0.0";
+    private ContextHandler.Context _context;
+    private String _workerName;
+    private String _canonicalContextPath;
+    private String _vhost;
+    
+    
+    public String getWorkerName()
+    {
+        return _workerName;
+    }
+
+
+    public SessionContext (String workerName, ContextHandler.Context context)
+    {
+        _workerName = workerName;
+        _context = context;
+        _canonicalContextPath = canonicalizeContextPath(_context);
+        _vhost = canonicalizeVHost(_context);
+    }
+    
+    
+    public Context getContext ()
+    {
+        return _context;
+    }
+    
+    public String getCanonicalContextPath()
+    {
+        return _canonicalContextPath;
+    }
+    
+    public String getVhost()
+    {
+        return _vhost;
+    }
+    
+    public String toString ()
+    {
+        return _workerName+"_"+_canonicalContextPath +"_"+_vhost;
+    }
+    
+    
+    /**
+     * Run a runnable in the context (with context classloader set) if
+     * there is one, otherwise just run it.
+     * @param r
+     */
+    public void run (Runnable r)
+    {
+        if (_context != null)
+            _context.getContextHandler().handle(r);
+        else
+            r.run();
+    }
+    
+    private String canonicalizeContextPath (Context context)
+    {
+        if (context == null)
+            return "";
+        return canonicalize (context.getContextPath());
+    }
+    
+    
+    /**
+     * Get the first virtual host for the context.
+     *
+     * Used to help identify the exact session/contextPath.
+     *
+     * @return 0.0.0.0 if no virtual host is defined
+     */
+    private String canonicalizeVHost (Context context)
+    {
+        String vhost = NULL_VHOST;
+
+        if (context==null)
+            return vhost;
+
+        String [] vhosts = context.getContextHandler().getVirtualHosts();
+        if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
+            return vhost;
+
+        return vhosts[0];
+    }
+    
+    /**
+     * Make an acceptable name from a context path.
+     *
+     * @param path
+     * @return
+     */
+    private String canonicalize (String path)
+    {
+        if (path==null)
+            return "";
+
+        return path.replace('/', '_').replace('.','_').replace('\\','_');
+    }
+    
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java
new file mode 100644
index 0000000..df62303
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java
@@ -0,0 +1,293 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * SessionData
+ *
+ * The data associated with a session. A Session object has a 1:1 relationship
+ * with a SessionData object. The behaviour of sessions is implemented in the
+ * Session object (eg calling listeners, keeping timers etc). A Session's
+ * associated SessionData is the object which can be persisted, serialized etc.
+ */
+public class SessionData implements Serializable
+{
+
+   
+    private static final long serialVersionUID = 1L;
+
+    protected String _id;
+
+    protected String _contextPath;
+    protected String _vhost;
+ 
+    
+    protected String _lastNode;
+    protected long _expiry;
+
+    protected long _created;
+    protected long _cookieSet;
+    protected long _accessed;         // the time of the last access
+    protected long _lastAccessed;     // the time of the last access excluding this one
+   // protected boolean _invalid;
+    protected long _maxInactiveMs;
+    protected Map<String,Object> _attributes = new ConcurrentHashMap<String, Object>();
+    protected boolean _dirty;
+    protected long _lastSaved; //time in msec since last save
+    
+
+    /**
+     * @param id
+     * @param cpath
+     * @param vhost
+     * @param created
+     * @param accessed
+     * @param lastAccessed
+     * @param maxInactiveMs
+     */
+    public SessionData (String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs)
+    {
+        _id = id;
+        setContextPath(cpath);
+        setVhost(vhost);
+        _created = created;
+        _accessed = accessed;
+        _lastAccessed = lastAccessed;
+        _maxInactiveMs = maxInactiveMs;
+    }
+    
+
+    public long getLastSaved()
+    {
+        return _lastSaved;
+    }
+
+
+
+    public void setLastSaved(long lastSaved)
+    {
+        _lastSaved = lastSaved;
+    }
+
+
+    public boolean isDirty()
+    {
+        return _dirty;
+    }
+
+    public void setDirty(boolean dirty)
+    {
+        _dirty = dirty;
+    }
+    
+    public Object getAttribute (String name)
+    {
+        return _attributes.get(name);
+    }
+
+    public Set<String> getKeys()
+    {
+        return _attributes.keySet();
+    }
+    
+    public Object setAttribute (String name, Object value)
+    {
+        Object old = (value==null?_attributes.remove(name):_attributes.put(name,value));
+        if (value == null && old == null)
+            return old; //if same as remove attribute but attribute was already removed, no change
+        
+        setDirty (name);
+       return old;
+    }
+    
+    
+    public void setDirty (String name)
+    {
+        setDirty (true);
+    }
+    
+    
+    
+    public void putAllAttributes (Map<String,Object> attributes)
+    {
+        _attributes.putAll(attributes);
+    }
+    
+    public Map<String,Object> getAllAttributes()
+    {
+        return Collections.unmodifiableMap(_attributes);
+    }
+    
+    public String getId()
+    {
+        return _id;
+    }
+
+    public void setId(String id)
+    {
+        _id = id;
+    }
+
+    public String getContextPath()
+    {
+        return _contextPath;
+    }
+
+    public void setContextPath(String contextPath)
+    {
+        _contextPath = contextPath;
+    }
+
+    public String getVhost()
+    {
+        return _vhost;
+    }
+
+    public void setVhost(String vhost)
+    {
+        _vhost = vhost;
+    }
+
+    public String getLastNode()
+    {
+        return _lastNode;
+    }
+
+    public void setLastNode(String lastNode)
+    {
+        _lastNode = lastNode;
+    }
+
+    public long getExpiry()
+    {
+        return _expiry;
+    }
+
+    public void setExpiry(long expiry)
+    {
+        _expiry = expiry;
+    }
+
+    public long getCreated()
+    {
+        return _created;
+    }
+
+    public void setCreated(long created)
+    {
+        _created = created;
+    }
+
+    public long getCookieSet()
+    {
+        return _cookieSet;
+    }
+
+    public void setCookieSet(long cookieSet)
+    {
+        _cookieSet = cookieSet;
+    }
+
+    public long getAccessed()
+    {
+        return _accessed;
+    }
+
+    public void setAccessed(long accessed)
+    {
+        _accessed = accessed;
+    }
+
+    public long getLastAccessed()
+    {
+        return _lastAccessed;
+    }
+
+    public void setLastAccessed(long lastAccessed)
+    {
+        _lastAccessed = lastAccessed;
+    }
+
+   /* public boolean isInvalid()
+    {
+        return _invalid;
+    }
+*/
+ 
+
+    public long getMaxInactiveMs()
+    {
+        return _maxInactiveMs;
+    }
+
+    public void setMaxInactiveMs(long maxInactive)
+    {
+        _maxInactiveMs = maxInactive;
+    }
+
+    private void writeObject(java.io.ObjectOutputStream out) throws IOException
+    {  
+        out.writeUTF(_id); //session id
+        out.writeUTF(_contextPath); //context path
+        out.writeUTF(_vhost); //first vhost
+
+        out.writeLong(_accessed);//accessTime
+        out.writeLong(_lastAccessed); //lastAccessTime
+        out.writeLong(_created); //time created
+        out.writeLong(_cookieSet);//time cookie was set
+        out.writeUTF(_lastNode); //name of last node managing
+  
+        out.writeLong(_expiry); 
+        out.writeLong(_maxInactiveMs);
+        out.writeObject(_attributes);
+    }
+    
+    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
+    {
+        _id = in.readUTF();
+        _contextPath = in.readUTF();
+        _vhost = in.readUTF();
+        
+        _accessed = in.readLong();//accessTime
+        _lastAccessed = in.readLong(); //lastAccessTime
+        _created = in.readLong(); //time created
+        _cookieSet = in.readLong();//time cookie was set
+        _lastNode = in.readUTF(); //last managing node
+        _expiry = in.readLong(); 
+        _maxInactiveMs = in.readLong();
+        _attributes = (ConcurrentHashMap<String,Object>)in.readObject();
+    }
+    
+    
+    public boolean isExpiredAt (long time)
+    {
+        if (getExpiry() <= 0)
+            return false; //never expires
+        
+        return (getExpiry() < time);
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionDataStore.java
new file mode 100644
index 0000000..404573a
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionDataStore.java
@@ -0,0 +1,103 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.util.Set;
+
+import org.eclipse.jetty.util.component.LifeCycle;
+
+/**
+ * SessionDataStore
+ *
+ * A store for the data contained in a Session object. The store
+ * would usually be persistent.
+ */
+public interface SessionDataStore extends LifeCycle
+{
+    /**
+     * Initialize this session data store for the
+     * given context. A SessionDataStore can only 
+     * be used by one context(/session manager).
+     * 
+     * @param context
+     */
+    void initialize(SessionContext context);
+    
+    
+    
+    /**
+     * Read in session data from storage
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    public SessionData load (String id) throws Exception;
+    
+    
+    /**
+     * Create a new SessionData 
+     * @return
+     */
+    public SessionData newSessionData (String id, long created, long accessed, long lastAccessed, long maxInactiveMs);
+    
+    
+    
+    
+    /**
+     * Write out session data to storage
+     * @param id
+     * @param data
+     * @throws Exception
+     */
+    public void store (String id, SessionData data) throws Exception;
+    
+    
+    
+    /**
+     * Delete session data from storage
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    public boolean delete (String id) throws Exception;
+    
+    
+    
+   
+    /**
+     * Called periodically, this method should search the data store
+     * for sessions that have been expired for a 'reasonable' amount 
+     * of time. 
+     * @param candidates if provided, these are keys of sessions that
+     * the SessionStore thinks has expired and should be verified by the
+     * SessionDataStore
+     * @return
+     */
+    public Set<String> getExpired (Set<String> candidates);
+    
+    
+    
+    /**
+     * True if this type of datastore will passivate session objects
+     * @return
+     */
+    public boolean isPassivating ();
+    
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
index 2126583..e0fb84f 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
@@ -243,6 +243,7 @@
         if (requested_session_id != null && sessionManager != null)
         {
             HttpSession session = sessionManager.getHttpSession(requested_session_id);
+            
             if (session != null && sessionManager.isValid(session))
                 baseRequest.setSession(session);
             return;
@@ -273,7 +274,6 @@
                         if (requested_session_id != null)
                         {
                             session = sessionManager.getHttpSession(requested_session_id);
-
                             if (session != null && sessionManager.isValid(session))
                             {
                                 break;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionManager.java
similarity index 77%
rename from jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
rename to jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionManager.java
index 408d08d..6df6acd 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionManager.java
@@ -16,6 +16,7 @@
 //  ========================================================================
 //
 
+
 package org.eclipse.jetty.server.session;
 
 import static java.lang.Math.round;
@@ -43,29 +44,28 @@
 import org.eclipse.jetty.http.HttpCookie;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.SessionIdManager;
-import org.eclipse.jetty.server.SessionManager;
 import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.StringUtil;
 import org.eclipse.jetty.util.annotation.ManagedAttribute;
-import org.eclipse.jetty.util.annotation.ManagedObject;
 import org.eclipse.jetty.util.annotation.ManagedOperation;
 import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.statistic.CounterStatistic;
 import org.eclipse.jetty.util.statistic.SampleStatistic;
 
-/**
- * An Abstract implementation of SessionManager.
- * <p>
- * The partial implementation of SessionManager interface provides the majority of the handling required to implement a
- * SessionManager. Concrete implementations of SessionManager based on AbstractSessionManager need only implement the
- * newSession method to return a specialized version of the Session inner class that provides an attribute Map.
- */
-@SuppressWarnings("deprecation")
-@ManagedObject("Abstract Session Manager")
-public abstract class AbstractSessionManager extends ContainerLifeCycle implements SessionManager
-{
-    final static Logger __log = SessionHandler.LOG;
 
+
+/**
+ * SessionManager
+ * 
+ * Handles session lifecycle. There is one SessionManager per context.
+ *
+ */
+public class SessionManager extends ContainerLifeCycle implements org.eclipse.jetty.server.SessionManager
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+            
     public Set<SessionTrackingMode> __defaultSessionTrackingModes =
         Collections.unmodifiableSet(
             new HashSet<SessionTrackingMode>(
@@ -92,7 +92,7 @@
         }
     };
 
-    private boolean _usingCookies=true;
+   
 
     /* ------------------------------------------------------------ */
     // Setting of max inactive interval for new sessions
@@ -110,6 +110,7 @@
 
     protected ClassLoader _loader;
     protected ContextHandler.Context _context;
+    protected SessionContext _sessionContext;
     protected String _sessionCookie=__DefaultSessionCookie;
     protected String _sessionIdPathParameterName = __DefaultSessionIdPathParameterName;
     protected String _sessionIdPathParameterNamePrefix =";"+ _sessionIdPathParameterName +"=";
@@ -120,17 +121,19 @@
     protected boolean _nodeIdInSessionId;
     protected boolean _checkingRemoteSessionIdEncoding;
     protected String _sessionComment;
-
+    protected SessionStore _sessionStore;
+    protected final SampleStatistic _sessionTimeStats = new SampleStatistic();
+    protected final CounterStatistic _sessionsCreatedStats = new CounterStatistic();
     public Set<SessionTrackingMode> _sessionTrackingModes;
 
     private boolean _usingURLs;
-
-    protected final CounterStatistic _sessionsStats = new CounterStatistic();
-    protected final SampleStatistic _sessionTimeStats = new SampleStatistic();
+    private boolean _usingCookies=true;
 
 
+    
+    
     /* ------------------------------------------------------------ */
-    public AbstractSessionManager()
+    public SessionManager()
     {
         setSessionTrackingModes(__defaultSessionTrackingModes);
     }
@@ -146,13 +149,17 @@
     {
         return _context.getContextHandler();
     }
-
+    
+    
+    /* ------------------------------------------------------------ */
     @ManagedAttribute("path of the session cookie, or null for default")
     public String getSessionPath()
     {
         return _sessionPath;
     }
-
+    
+    
+    /* ------------------------------------------------------------ */
     @ManagedAttribute("if greater the zero, the time in seconds a session cookie will last for")
     public int getMaxCookieAge()
     {
@@ -165,7 +172,7 @@
     {
         long now=System.currentTimeMillis();
 
-        AbstractSession s = ((SessionIf)session).getSession();
+        Session s = ((SessionIf)session).getSession();
 
        if (s.access(now))
        {
@@ -213,14 +220,27 @@
     @Override
     public void complete(HttpSession session)
     {
-        AbstractSession s = ((SessionIf)session).getSession();
+        Session s = ((SessionIf)session).getSession();
         s.complete();
+        try
+        {
+            if (s.isValid())
+                _sessionStore.put(s.getId(), s);
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+        }
     }
 
     /* ------------------------------------------------------------ */
     @Override
     public void doStart() throws Exception
     {
+        if (_sessionStore == null)
+            throw new IllegalStateException("No session store configured");
+        
+        
         _context=ContextHandler.getCurrentContext();
         _loader=Thread.currentThread().getContextClassLoader();
 
@@ -240,7 +260,7 @@
                     try
                     {
                         Thread.currentThread().setContextClassLoader(serverLoader);
-                        _sessionIdManager=new HashSessionIdManager();
+                        _sessionIdManager=new HashSessionIdManager(server);
                         server.setSessionIdManager(_sessionIdManager);
                         server.manage(_sessionIdManager);
                         _sessionIdManager.start();
@@ -260,35 +280,44 @@
         // Look for a session cookie name
         if (_context!=null)
         {
-            String tmp=_context.getInitParameter(SessionManager.__SessionCookieProperty);
+            String tmp=_context.getInitParameter(org.eclipse.jetty.server.SessionManager.__SessionCookieProperty);
             if (tmp!=null)
                 _sessionCookie=tmp;
 
-            tmp=_context.getInitParameter(SessionManager.__SessionIdPathParameterNameProperty);
+            tmp=_context.getInitParameter(org.eclipse.jetty.server.SessionManager.__SessionIdPathParameterNameProperty);
             if (tmp!=null)
                 setSessionIdPathParameterName(tmp);
 
             // set up the max session cookie age if it isn't already
             if (_maxCookieAge==-1)
             {
-                tmp=_context.getInitParameter(SessionManager.__MaxAgeProperty);
+                tmp=_context.getInitParameter(org.eclipse.jetty.server.SessionManager.__MaxAgeProperty);
                 if (tmp!=null)
                     _maxCookieAge=Integer.parseInt(tmp.trim());
             }
 
             // set up the session domain if it isn't already
             if (_sessionDomain==null)
-                _sessionDomain=_context.getInitParameter(SessionManager.__SessionDomainProperty);
+                _sessionDomain=_context.getInitParameter(org.eclipse.jetty.server.SessionManager.__SessionDomainProperty);
 
             // set up the sessionPath if it isn't already
             if (_sessionPath==null)
-                _sessionPath=_context.getInitParameter(SessionManager.__SessionPathProperty);
+                _sessionPath=_context.getInitParameter(org.eclipse.jetty.server.SessionManager.__SessionPathProperty);
 
-            tmp=_context.getInitParameter(SessionManager.__CheckRemoteSessionEncoding);
+            tmp=_context.getInitParameter(org.eclipse.jetty.server.SessionManager.__CheckRemoteSessionEncoding);
             if (tmp!=null)
                 _checkingRemoteSessionIdEncoding=Boolean.parseBoolean(tmp);
         }
+       
+        _sessionContext = new SessionContext(_sessionIdManager.getWorkerName(), _context);
 
+       if (_sessionStore instanceof AbstractSessionStore)
+           ((AbstractSessionStore)_sessionStore).setSessionManager(this);
+       
+       
+       _sessionStore.initialize(_sessionContext);
+       _sessionStore.start();
+       
         super.doStart();
     }
 
@@ -296,10 +325,9 @@
     @Override
     public void doStop() throws Exception
     {
-        super.doStop();
-
         shutdownSessions();
-
+        _sessionStore.stop();
+        super.doStop();
         _loader=null;
     }
 
@@ -316,12 +344,12 @@
 
     /* ------------------------------------------------------------ */
     @Override
-    public HttpSession getHttpSession(String nodeId)
+    public HttpSession getHttpSession(String extendedId)
     {
-        String cluster_id = getSessionIdManager().getClusterId(nodeId);
+        String id = getSessionIdManager().getId(extendedId);
 
-        AbstractSession session = getSession(cluster_id);
-        if (session!=null && !session.getNodeId().equals(nodeId))
+        Session session = getSession(id);
+        if (session!=null && !session.getExtendedId().equals(extendedId))
             session.setIdChanged(true);
         return session;
     }
@@ -343,31 +371,13 @@
      * @return seconds
      */
     @Override
-    @ManagedAttribute("defailt maximum time a session may be idle for (in s)")
+    @ManagedAttribute("default maximum time a session may be idle for (in s)")
     public int getMaxInactiveInterval()
     {
         return _dftMaxIdleSecs;
     }
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @return maximum number of sessions
-     */
-    @ManagedAttribute("maximum number of simultaneous sessions")
-    public int getSessionsMax()
-    {
-        return (int)_sessionsStats.getMax();
-    }
 
-    /* ------------------------------------------------------------ */
-    /**
-     * @return total number of sessions
-     */
-    @ManagedAttribute("total number of sessions")
-    public int getSessionsTotal()
-    {
-        return (int)_sessionsStats.getTotal();
-    }
 
     /* ------------------------------------------------------------ */
     @ManagedAttribute("time before a session cookie is re-set (in s)")
@@ -451,7 +461,7 @@
         {
             String sessionPath = (_cookieConfig.getPath()==null) ? contextPath : _cookieConfig.getPath();
             sessionPath = (sessionPath==null||sessionPath.length()==0) ? "/" : sessionPath;
-            String id = getNodeId(session);
+            String id = getExtendedId(session);
             HttpCookie cookie = null;
             if (_sessionComment == null)
             {
@@ -482,7 +492,10 @@
         }
         return null;
     }
-
+    
+    
+    
+    /* ------------------------------------------------------------ */
     @ManagedAttribute("domain of the session cookie, or null for the default")
     public String getSessionDomain()
     {
@@ -499,10 +512,10 @@
     }
 
     /* ------------------------------------------------------------ */
-    @ManagedAttribute("number of currently active sessions")
-    public int getSessions()
+    @ManagedAttribute("number of sessions created by this node")
+    public int getSessionsCreated()
     {
-        return (int)_sessionsStats.getCurrent();
+        return (int) _sessionsCreatedStats.getCurrent();
     }
 
     /* ------------------------------------------------------------ */
@@ -534,24 +547,24 @@
     @Override
     public boolean isValid(HttpSession session)
     {
-        AbstractSession s = ((SessionIf)session).getSession();
+        Session s = ((SessionIf)session).getSession();
         return s.isValid();
     }
 
     /* ------------------------------------------------------------ */
     @Override
-    public String getClusterId(HttpSession session)
+    public String getId(HttpSession session)
     {
-        AbstractSession s = ((SessionIf)session).getSession();
-        return s.getClusterId();
+        Session s = ((SessionIf)session).getSession();
+        return s.getId();
     }
 
     /* ------------------------------------------------------------ */
     @Override
-    public String getNodeId(HttpSession session)
+    public String getExtendedId(HttpSession session)
     {
-        AbstractSession s = ((SessionIf)session).getSession();
-        return s.getNodeId();
+        Session s = ((SessionIf)session).getSession();
+        return s.getExtendedId();
     }
 
     /* ------------------------------------------------------------ */
@@ -561,12 +574,39 @@
     @Override
     public HttpSession newHttpSession(HttpServletRequest request)
     {
-        AbstractSession session=newSession(request);
-        session.setMaxInactiveInterval(_dftMaxIdleSecs);
+        long created=System.currentTimeMillis();
+        String id =_sessionIdManager.newSessionId(request,created);      
+        Session session = _sessionStore.newSession(request, id, created,  (_dftMaxIdleSecs>0?_dftMaxIdleSecs*1000L:-1));
+        session.setExtendedId(_sessionIdManager.getExtendedId(id,request));
+        session.setSessionManager(this);
+        session.setLastNode(_sessionIdManager.getWorkerName());
+        session.getSessionData().setExpiry(_dftMaxIdleSecs <= 0 ? 0 : (created + _dftMaxIdleSecs*1000L));
+
         if (request.isSecure())
-            session.setAttribute(AbstractSession.SESSION_CREATED_SECURE, Boolean.TRUE);
-        addSession(session,true);
-        return session;
+            session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE);
+
+        try
+        {
+            _sessionStore.put(id, session);
+            
+            _sessionsCreatedStats.increment();
+            
+            _sessionIdManager.useId(session);
+            
+            if (_sessionListeners!=null)
+            {
+                HttpSessionEvent event=new HttpSessionEvent(session);
+                for (HttpSessionListener listener : _sessionListeners)
+                    listener.sessionCreated(event);
+            }
+
+            return session;
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+            return null;
+        }      
     }
 
     /* ------------------------------------------------------------ */
@@ -589,7 +629,7 @@
     @ManagedOperation(value="reset statistics", impact="ACTION")
     public void statsReset()
     {
-        _sessionsStats.reset(getSessions());
+        _sessionsCreatedStats.reset();
         _sessionTimeStats.reset();
     }
 
@@ -663,60 +703,78 @@
     }
 
 
-    protected abstract void addSession(AbstractSession session);
-
-    /* ------------------------------------------------------------ */
-    /**
-     * Add the session Registers the session with this manager and registers the
-     * session ID with the sessionIDManager;
-     * @param session the session
-     * @param created true if session was created
-     */
-    protected void addSession(AbstractSession session, boolean created)
-    {
-        synchronized (_sessionIdManager)
-        {
-            _sessionIdManager.addSession(session);
-            addSession(session);
-        }
-
-        if (created)
-        {
-            _sessionsStats.increment();
-            if (_sessionListeners!=null)
-            {
-                HttpSessionEvent event=new HttpSessionEvent(session);
-                for (HttpSessionListener listener : _sessionListeners)
-                    listener.sessionCreated(event);
-            }
-        }
-    }
 
     /* ------------------------------------------------------------ */
     /**
      * Get a known existing session
-     * @param idInCluster The session ID in the cluster, stripped of any worker name.
+     * @param id The session ID stripped of any worker name.
      * @return A Session or null if none exists.
      */
-    public abstract AbstractSession getSession(String idInCluster);
-
+    public Session getSession(String id)
+    {
+        try
+        {
+            Session session =  _sessionStore.get(id, true);
+            if (session != null)
+            {
+                //If the session we got back has expired
+                if (session.isExpiredAt(System.currentTimeMillis()))
+                {
+                    //Expire the session
+                    try
+                    {
+                        session.invalidate();
+                    }
+                    catch (Exception e)
+                    {
+                        LOG.warn("Invalidating session {} found to be expired when requested", id, e);
+                    }
+                    
+                    return null;
+                }
+                
+                session.setExtendedId(_sessionIdManager.getExtendedId(id, null)); //TODO not sure if this is OK?
+                session.setLastNode(_sessionIdManager.getWorkerName());  //TODO write through the change of node?
+            }
+            return session;
+        }
+        catch (UnreadableSessionDataException e)
+        {
+            LOG.warn(e);
+            //Could not retrieve the session with the given id
+            //Tell the session id manager that the session id is not to be used by any other threads/contexts
+            _sessionIdManager.removeId(id);
+            return null;
+        }
+        catch (Exception other)
+        {
+            LOG.warn(other);
+            return null;
+        }
+    }
+    
+    
+    /* ------------------------------------------------------------ */
     /**
      * Prepare sessions for session manager shutdown
      * 
      * @throws Exception if unable to shutdown sesssions
      */
-    protected abstract void shutdownSessions() throws Exception;
+    protected void shutdownSessions() throws Exception
+    {
+        _sessionStore.shutdown();
+    }
 
 
     /* ------------------------------------------------------------ */
     /**
-     * Create a new session instance
-     * @param request the request to build the session from
-     * @return the new session
+     * @return
      */
-    protected abstract AbstractSession newSession(HttpServletRequest request);
-
-
+    public SessionStore getSessionStore ()
+    {
+        return _sessionStore;
+    }
+    
     /* ------------------------------------------------------------ */
     /**
      * @return true if the cluster node id (worker id) is returned as part of the session id by {@link HttpSession#getId()}. Default is false.
@@ -735,56 +793,47 @@
         _nodeIdInSessionId=nodeIdInSessionId;
     }
 
-    /* ------------------------------------------------------------ */
-    /** Remove session from manager
-     * @param session The session to remove
-     * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
-     * {@link SessionIdManager#invalidateAll(String)} should be called.
-     */
-    public void removeSession(HttpSession session, boolean invalidate)
-    {
-        AbstractSession s = ((SessionIf)session).getSession();
-        removeSession(s,invalidate);
-    }
 
     /* ------------------------------------------------------------ */
     /** 
      * Remove session from manager
-     * @param session The session to remove
+     * @param id The session to remove
      * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
-     * {@link SessionIdManager#invalidateAll(String)} should be called.
+     * {@link SessionIdManager#expireAll(String)} should be called.
      * @return if the session was removed 
      */
-    public boolean removeSession(AbstractSession session, boolean invalidate)
+    public Session removeSession(String id, boolean invalidate)
     {
-        // Remove session from context and global maps
-        boolean removed = removeSession(session.getClusterId());
-
-        if (removed)
+        try
         {
-            _sessionsStats.decrement();
-            _sessionTimeStats.set(round((System.currentTimeMillis() - session.getCreationTime())/1000.0));
-
-            // Remove session from all context and global id maps
-            _sessionIdManager.removeSession(session);
-            if (invalidate)
-                _sessionIdManager.invalidateAll(session.getClusterId());
-
-            if (invalidate && _sessionListeners!=null)
+            //Remove the Session object from the session store and any backing data store
+            Session session = _sessionStore.delete(id);
+            if (session != null)
             {
-                HttpSessionEvent event=new HttpSessionEvent(session);      
-                for (int i = _sessionListeners.size()-1; i>=0; i--)
+                if (invalidate)
                 {
-                    _sessionListeners.get(i).sessionDestroyed(event);
+                    if (_sessionListeners!=null)
+                    {
+                        HttpSessionEvent event=new HttpSessionEvent(session);      
+                        for (int i = _sessionListeners.size()-1; i>=0; i--)
+                        {
+                            _sessionListeners.get(i).sessionDestroyed(event);
+                        }
+                    }
                 }
             }
-        }
-        
-        return removed;
-    }
 
-    /* ------------------------------------------------------------ */
-    protected abstract boolean removeSession(String idInCluster);
+            return session;
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+            return null;
+        }
+    }
+    
+    
+   
 
     /* ------------------------------------------------------------ */
     /**
@@ -888,30 +937,98 @@
     {
         _checkingRemoteSessionIdEncoding=remote;
     }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Change the session id and tell the HttpSessionIdListeners the id changed.
+     * 
+     */
+    @Override
+    public void renewSessionId(String oldId, String oldExtendedId, String newId, String newExtendedId)
+    {
+        try
+        {
+            Session session =_sessionStore.delete(oldId);
+            if (session == null)
+            {
+                LOG.warn("Unable to renew id to "+newId+" for non-existant session "+oldId);
+                return;
+            }
+            
+            //swap the ids
+            session.renewId(oldId, oldExtendedId, newId, newExtendedId);
+            
+            _sessionStore.put(newId, session);
+            
+            //tell session id manager the id is in use
+            _sessionIdManager.useId(session);
+
+            //inform the listeners
+            if (!_sessionIdListeners.isEmpty())
+            {
+                HttpSessionEvent event = new HttpSessionEvent(session);
+                for (HttpSessionIdListener l:_sessionIdListeners)
+                {
+                    l.sessionIdChanged(event, oldId);
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+        }
+    }
     
+   
     
     /* ------------------------------------------------------------ */
     /**
-     * Tell the HttpSessionIdListeners the id changed.
-     * NOTE: this method must be called LAST in subclass overrides, after the session has been updated
-     * with the new id.
-     * @see org.eclipse.jetty.server.SessionManager#renewSessionId(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+     * Called either when a session has expired, or the app has
+     * invalidated it.
+     * 
+     * @param id
      */
-    @Override
-    public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
+    public void invalidate (String id)
     {
-        if (!_sessionIdListeners.isEmpty())
+        if (StringUtil.isBlank(id))
+            return;
+
+        try
         {
-            AbstractSession session = getSession(newClusterId);
-            HttpSessionEvent event = new HttpSessionEvent(session);
-            for (HttpSessionIdListener l:_sessionIdListeners)
+            //remove the session and call the destroy listeners
+            Session session = removeSession(id, true);
+
+            if (session != null)
             {
-                l.sessionIdChanged(event, oldClusterId);
+                _sessionTimeStats.set(round((System.currentTimeMillis() - session.getSessionData().getCreated())/1000.0));
+                session.doInvalidate();   
             }
         }
-
+        catch (Exception e)
+        {
+            LOG.warn(e);
+        }
     }
 
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return
+     */
+    public Set<String> scavenge ()
+    {
+        //don't attempt to scavenge if we are shutting down
+        if (isStopping() || isStopped())
+            return Collections.emptySet();
+
+        return _sessionStore.getExpired();
+    }
+    
+  
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
     /**
      * CookieConfig
      * 
@@ -1028,10 +1145,10 @@
      */
     public interface SessionIf extends HttpSession
     {
-        public AbstractSession getSession();
+        public Session getSession();
     }
 
-    public void doSessionAttributeListeners(AbstractSession session, String name, Object old, Object value)
+    public void doSessionAttributeListeners(Session session, String name, Object old, Object value)
     {
         if (!_sessionAttributeListeners.isEmpty())
         {
@@ -1048,11 +1165,4 @@
             }
         }
     }
-
-    @Override
-    @Deprecated
-    public SessionIdManager getMetaManager()
-    {
-        throw new UnsupportedOperationException();
-    }
 }
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionScavenger.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionScavenger.java
new file mode 100644
index 0000000..ec71f0f
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionScavenger.java
@@ -0,0 +1,235 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.server.SessionIdManager;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+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;
+
+/**
+ * SessionScavenger
+ *
+ * There is 1 session scavenger per SessionIdManager/Server instance.
+ *
+ */
+public class SessionScavenger extends AbstractLifeCycle
+{
+    private  final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+    
+    public static final long DEFAULT_SCAVENGE_MS = 1000L * 60 * 10;
+    protected SessionIdManager _sessionIdManager;
+    protected Scheduler _scheduler;
+    protected Scheduler.Task _task; //scavenge task
+    protected ScavengerRunner _runner;
+    protected boolean _ownScheduler = false;
+    private long _scavengeIntervalMs =  DEFAULT_SCAVENGE_MS;
+    
+    
+    
+    /**
+     * ScavengerRunner
+     *
+     */
+    protected class ScavengerRunner implements Runnable
+    {
+
+        @Override
+        public void run()
+        {
+           try
+           {
+               scavenge();
+           }
+           finally
+           {
+               if (_scheduler != null && _scheduler.isRunning())
+                   _task = _scheduler.schedule(this, _scavengeIntervalMs, TimeUnit.MILLISECONDS);
+           }
+        }
+    }
+    
+    
+    
+    
+    /**
+     * SessionIdManager associated with this scavenger
+     * @param sessionIdManager
+     */
+    public void setSessionIdManager (SessionIdManager sessionIdManager)
+    {
+        _sessionIdManager = sessionIdManager;
+    }
+    
+    
+    /** 
+     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (_sessionIdManager == null)
+            throw new IllegalStateException ("No SessionIdManager for Scavenger");
+        
+        if (!(_sessionIdManager instanceof AbstractSessionIdManager))
+            throw new IllegalStateException ("SessionIdManager is not an AbstractSessionIdManager");
+
+
+        //try and use a common scheduler, fallback to own
+        _scheduler = ((AbstractSessionIdManager)_sessionIdManager).getServer().getBean(Scheduler.class);
+
+        if (_scheduler == null)
+        {
+            _scheduler = new ScheduledExecutorScheduler();
+            _ownScheduler = true;
+            _scheduler.start();
+        }
+        else if (!_scheduler.isStarted())
+            throw new IllegalStateException("Shared scheduler not started");
+
+        setScavengeIntervalSec(getScavengeIntervalSec());
+        
+        super.doStart();
+    }
+
+    /** 
+     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        synchronized(this)
+        {
+            if (_task != null)
+                _task.cancel();
+            _task=null;
+            if (_ownScheduler && _scheduler !=null)
+                _scheduler.stop();
+            _scheduler = null;
+            _runner = null;
+        }
+        super.doStop();
+    }
+    
+    
+    /**
+     * Set the period between scavenge cycles
+     * @param sec
+     */
+    public void setScavengeIntervalSec (long sec)
+    {
+        if (sec<=0)
+            sec=60;
+
+        long old_period=_scavengeIntervalMs;
+        long period=sec*1000L;
+
+        _scavengeIntervalMs=period;
+
+        //add a bit of variability into the scavenge time so that not all
+        //nodes with the same scavenge interval sync up
+        long tenPercent = _scavengeIntervalMs/10;
+        if ((System.currentTimeMillis()%2) == 0)
+            _scavengeIntervalMs += tenPercent;
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("Scavenging every "+_scavengeIntervalMs+" ms");
+        
+        synchronized (this)
+        {
+            if (_scheduler != null && (period!=old_period || _task==null))
+            {
+                if (_task!=null)
+                    _task.cancel();
+                if (_runner == null)
+                    _runner = new ScavengerRunner();
+                _task = _scheduler.schedule(_runner,_scavengeIntervalMs,TimeUnit.MILLISECONDS);
+            }
+        }
+    }
+
+    
+    
+    /**
+     * Get the period between scavenge cycles.
+     * 
+     * @return
+     */
+    public long getScavengeIntervalSec ()
+    {
+        return _scavengeIntervalMs/1000;
+    }
+    
+    
+    
+    /**
+     * Perform a scavenge cycle:
+     *   ask all SessionManagers to find sessions they think have expired and then make
+     *   sure that a session sharing the same id is expired on all contexts
+     */
+    public void scavenge ()
+    {
+        //don't attempt to scavenge if we are shutting down
+        if (isStopping() || isStopped())
+            return;
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("Scavenging sessions");
+
+        //find the session managers
+        for (SessionManager manager:((AbstractSessionIdManager)_sessionIdManager).getSessionManagers())
+        {
+            if (manager != null)
+            {
+                //call scavenge on each manager to find keys for sessions that have expired
+                Set<String> expiredKeys = manager.scavenge();
+
+                //for each expired session, tell the session id manager to invalidate its key on all contexts
+                for (String key:expiredKeys)
+                {
+                    try
+                    {
+                        ((AbstractSessionIdManager)_sessionIdManager).expireAll(key);
+                    }
+                    catch (Exception e)
+                    {
+                        LOG.warn(e);
+                    }
+                }
+            }
+        }
+    }
+
+
+    /** 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString()
+    {
+        return super.toString()+"[interval="+_scavengeIntervalMs+", ownscheduler="+_ownScheduler+"]";
+    }
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionStore.java
new file mode 100644
index 0000000..a725192
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionStore.java
@@ -0,0 +1,47 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.util.component.LifeCycle;
+
+/**
+ * SessionStore
+ *
+ * A store of Session objects.  This store of Session objects can be backed by
+ * a SessionDataStore to persist/distribute the data contained in the Session objects.
+ * 
+ * This store of Session objects ensures that all threads within the same context on
+ * the same node with the same session id will share exactly the same Session object.
+ */
+public interface SessionStore extends LifeCycle
+{
+    void initialize(SessionContext context);
+    Session newSession (HttpServletRequest request, String id,  long time, long maxInactiveMs);
+    Session get(String id, boolean staleCheck) throws Exception;
+    void put(String id, Session session) throws Exception;
+    boolean exists (String id) throws Exception;
+    Session delete (String id) throws Exception;
+    void shutdown ();
+    Set<String> getExpired ();
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/StalePeriodStrategy.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/StalePeriodStrategy.java
new file mode 100644
index 0000000..fa30e1f
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/StalePeriodStrategy.java
@@ -0,0 +1,80 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+/**
+ * StalePeriodStrategy
+ *
+ * A session is regarded as being stale if it has been 
+ * x seconds since it was last read from the cluster.
+ */
+public class StalePeriodStrategy implements StalenessStrategy
+{
+    protected long _staleMs = 0;
+
+    /** 
+     * @see org.eclipse.jetty.server.session.StalenessStrategy#isStale(org.eclipse.jetty.server.session.Session)
+     */
+    @Override
+    public boolean isStale (Session session)
+    {
+        if (session == null)
+            return false;
+        
+        //never persisted, must be fresh session
+        if (session.getSessionData().getLastSaved() == 0)
+            return false;
+        
+        if (_staleMs <= 0)
+        {
+            //TODO always stale, never stale??
+            return false;
+        }
+        else
+        {
+           // return (session.getSessionData().getAccessed() - session.getSessionData().getLastSaved() >= _staleMs);
+            return (System.currentTimeMillis() - session.getSessionData().getLastSaved() >= _staleMs);
+        }
+            
+    }
+    
+    
+    /**
+     * @return
+     */
+    public long getStaleSec ()
+    {
+        return (_staleMs<=0?0L:_staleMs/1000L);
+    }
+    
+    /**
+     * The amount of time in seconds that a session can be held
+     * in memory without being refreshed from the cluster.
+     * @param sec
+     */
+    public void setStaleSec (long sec)
+    {
+        if (sec == 0)
+            _staleMs = 0L;
+        else
+            _staleMs = sec * 1000L;
+    }
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/StalenessStrategy.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/StalenessStrategy.java
new file mode 100644
index 0000000..e23c7d8
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/StalenessStrategy.java
@@ -0,0 +1,30 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+/**
+ * StalenessStrategy
+ *
+ *
+ */
+public interface StalenessStrategy
+{
+    boolean isStale (Session session);
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/UnreadableSessionDataException.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/UnreadableSessionDataException.java
new file mode 100644
index 0000000..8c6a3be
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/UnreadableSessionDataException.java
@@ -0,0 +1,59 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+/**
+ * UnreadableSessionData
+ *
+ *
+ */
+public class UnreadableSessionDataException extends Exception
+{
+    private String _id;
+    private SessionContext _sessionContext;
+    
+    
+    public String getId()
+    {
+        return _id;
+    }
+    
+    public SessionContext getSessionContext()
+    {
+        return _sessionContext;
+    }
+
+
+    public UnreadableSessionDataException (String id, SessionContext contextId, Throwable t)
+    {
+        super ("Unreadable session "+id+" for "+contextId, t);
+        _sessionContext = contextId;
+        _id = id;
+    }
+    
+    public UnreadableSessionDataException (String id, SessionContext contextId, boolean loadAttemptsExhausted)
+    {
+        super("Unreadable session "+id+" for "+contextId+(loadAttemptsExhausted?" max load attempts":""));
+        _sessionContext = contextId;
+        _id = id;
+    }
+    
+
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/UnwriteableSessionDataException.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/UnwriteableSessionDataException.java
new file mode 100644
index 0000000..b222d6a
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/UnwriteableSessionDataException.java
@@ -0,0 +1,49 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+/**
+ * UnwriteableSessionDataException
+ *
+ *
+ */
+public class UnwriteableSessionDataException extends Exception
+{
+    private String _id;
+    private SessionContext _sessionContext;
+    
+    
+    
+    public UnwriteableSessionDataException (String id, SessionContext contextId, Throwable t)
+    {
+        super ("Unwriteable session "+id+" for "+contextId, t);
+       _id = id;
+    }
+    
+    public String getId()
+    {
+        return _id;
+    }
+    
+    public SessionContext getSessionContext()
+    {
+        return _sessionContext;
+    }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/jmx/AbstractSessionManagerMBean.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/jmx/SessionManagerMBean.java
similarity index 84%
rename from jetty-server/src/main/java/org/eclipse/jetty/server/session/jmx/AbstractSessionManagerMBean.java
rename to jetty-server/src/main/java/org/eclipse/jetty/server/session/jmx/SessionManagerMBean.java
index bf6a378..0dd72c1 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/jmx/AbstractSessionManagerMBean.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/jmx/SessionManagerMBean.java
@@ -21,12 +21,12 @@
 import org.eclipse.jetty.server.handler.AbstractHandlerContainer;
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.jmx.AbstractHandlerMBean;
-import org.eclipse.jetty.server.session.AbstractSessionManager;
+import org.eclipse.jetty.server.session.SessionManager;
 import org.eclipse.jetty.server.session.SessionHandler;
 
-public class AbstractSessionManagerMBean extends AbstractHandlerMBean
+public class SessionManagerMBean extends AbstractHandlerMBean
 {
-    public AbstractSessionManagerMBean(Object managedObject)
+    public SessionManagerMBean(Object managedObject)
     {
         super(managedObject);
     }
@@ -34,9 +34,9 @@
     /* ------------------------------------------------------------ */
     public String getObjectContextBasis()
     {
-        if (_managed != null && _managed instanceof AbstractSessionManager)
+        if (_managed != null && _managed instanceof SessionManager)
         {
-            AbstractSessionManager manager = (AbstractSessionManager)_managed;
+            SessionManager manager = (SessionManager)_managed;
             
             String basis = null;
             SessionHandler handler = manager.getSessionHandler();
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
index b23bf78..462cdfb 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
@@ -55,7 +55,8 @@
 import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.session.HashSessionIdManager;
 import org.eclipse.jetty.server.session.HashSessionManager;
-import org.eclipse.jetty.server.session.HashedSession;
+import org.eclipse.jetty.server.session.Session;
+import org.eclipse.jetty.server.session.SessionData;
 import org.eclipse.jetty.util.Callback;
 import org.eclipse.jetty.util.thread.Scheduler;
 import org.eclipse.jetty.util.thread.TimerScheduler;
@@ -478,9 +479,11 @@
         request.setRequestedSessionId("12345");
         request.setRequestedSessionIdFromCookie(false);
         HashSessionManager manager = new HashSessionManager();
-        manager.setSessionIdManager(new HashSessionIdManager());
+        manager.setSessionIdManager(new HashSessionIdManager(_server));
         request.setSessionManager(manager);
-        request.setSession(new TestSession(manager, "12345"));
+        TestSession tsession = new TestSession(manager, "12345");
+        tsession.setExtendedId(manager.getSessionIdManager().getExtendedId("12345", null));
+        request.setSession(tsession);
 
         manager.setCheckingRemoteSessionIdEncoding(false);
 
@@ -553,7 +556,7 @@
                     request.setRequestedSessionId("12345");
                     request.setRequestedSessionIdFromCookie(i>2);
                     HashSessionManager manager = new HashSessionManager();
-                    manager.setSessionIdManager(new HashSessionIdManager());
+                    manager.setSessionIdManager(new HashSessionIdManager(_server));
                     request.setSessionManager(manager);
                     request.setSession(new TestSession(manager, "12345"));
                     manager.setCheckingRemoteSessionIdEncoding(false);
@@ -849,11 +852,12 @@
         return new Response(_channel, _channel.getResponse().getHttpOutput());
     }
 
-    private static class TestSession extends HashedSession
+    private static class TestSession extends Session
     {
         protected TestSession(HashSessionManager hashSessionManager, String id)
         {
-            super(hashSessionManager, 0L, 0L, id);
+            super(new SessionData(id, "", "0.0.0.0", 0, 0, 0, 300));
+            setSessionManager(hashSessionManager);
         }
     }
 }
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/FileSessionManagerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/FileSessionManagerTest.java
new file mode 100644
index 0000000..8dc6304
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/FileSessionManagerTest.java
@@ -0,0 +1,190 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.server.session;
+
+import java.io.File;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.StdErrLog;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FileSessionManagerTest
+{
+    private static StdErrLog _log;
+    private static boolean _stacks;
+  
+    
+    @BeforeClass
+    public static void beforeClass ()
+    {
+        _log = ((StdErrLog)Log.getLogger("org.eclipse.jetty.server.session"));
+        _stacks = _log.isHideStacks();
+        _log.setHideStacks(true);
+    }
+    
+    @AfterClass
+    public static void afterClass()
+    {
+        _log.setHideStacks(_stacks);
+    }
+    
+    
+    
+    @Test
+    public void testDangerousSessionIdRemoval() throws Exception
+    {
+        Server server = new Server();
+        SessionHandler handler = new SessionHandler();
+        handler.setServer(server);
+        final HashSessionIdManager idmgr = new HashSessionIdManager(server);
+        idmgr.setServer(server);
+        server.setSessionIdManager(idmgr);
+        
+        final FileSessionManager manager = new FileSessionManager();
+        manager.getSessionDataStore().setDeleteUnrestorableFiles(true);
+        //manager.setLazyLoad(true);
+        File testDir = MavenTestingUtils.getTargetTestingDir("hashes");
+        testDir.mkdirs();
+        manager.getSessionDataStore().setStoreDir(testDir);
+        manager.setSessionIdManager(idmgr);
+        handler.setSessionManager(manager);
+        manager.start();
+        
+        String expectedFilename = "../../_0.0.0.0_dangerFile";
+        
+        MavenTestingUtils.getTargetFile(expectedFilename).createNewFile();
+        
+        Assert.assertTrue("File should exist!", MavenTestingUtils.getTargetFile(expectedFilename).exists());
+
+        manager.getSession("../../_0.0.0.0_dangerFile");
+        
+        Assert.assertTrue("File should exist!", MavenTestingUtils.getTargetFile(expectedFilename).exists());
+
+    }
+
+    @Test
+    public void testValidSessionIdRemoval() throws Exception
+    {      
+        Server server = new Server();
+        SessionHandler handler = new SessionHandler();
+        handler.setServer(server);
+        final HashSessionIdManager idmgr = new HashSessionIdManager(server);
+        idmgr.setServer(server);
+        server.setSessionIdManager(idmgr);
+        final FileSessionManager manager = new FileSessionManager();
+        manager.getSessionDataStore().setDeleteUnrestorableFiles(true);
+        manager.setSessionIdManager(idmgr);
+        handler.setSessionManager(manager);
+        File testDir = MavenTestingUtils.getTargetTestingDir("hashes");
+        FS.ensureEmpty(testDir);
+
+        manager.getSessionDataStore().setStoreDir(testDir);
+        manager.start();
+
+        String expectedFilename = "_0.0.0.0_validFile123";
+        
+        Assert.assertTrue(new File(testDir, expectedFilename).createNewFile());
+
+        Assert.assertTrue("File should exist!", new File(testDir, expectedFilename).exists());
+
+        manager.getSession("validFile123");
+
+        Assert.assertTrue("File shouldn't exist!", !new File(testDir,expectedFilename).exists());
+    }
+
+    @Test
+    public void testHashSession() throws Exception
+    {
+        File testDir = MavenTestingUtils.getTargetTestingDir("saved");
+        IO.delete(testDir);
+        testDir.mkdirs();
+
+        Server server = new Server();
+        SessionHandler handler = new SessionHandler();
+        handler.setServer(server);
+        FileSessionManager manager = new FileSessionManager();
+        manager.getSessionDataStore().setStoreDir(testDir);
+            @Override
+            public void doStart() throws Exception
+            {
+                super.doStart();
+                Scheduler timerBean = getBean(Scheduler.class);
+                Assert.assertNotNull(timerBean);
+            }
+
+            @Override
+            public void doStop() throws Exception
+            {
+                super.doStop();
+                Scheduler timerBean = getBean(Scheduler.class);
+                Assert.assertNull(timerBean);
+            }
+
+        };
+        manager.setMaxInactiveInterval(5);
+        Assert.assertTrue(testDir.exists());
+        Assert.assertTrue(testDir.canWrite());
+        handler.setSessionManager(manager);
+        
+        AbstractSessionIdManager idManager = new HashSessionIdManager(server);
+        idManager.setServer(server);
+        idManager.setWorkerName("foo");
+        manager.setSessionIdManager(idManager);
+        server.setSessionIdManager(idManager);
+        
+        server.start();
+        manager.start();
+        
+        Session session = (Session)manager.newHttpSession(new Request(null, null));
+        String sessionId = session.getId();
+        
+        session.setAttribute("one", new Integer(1));
+        session.setAttribute("two", new Integer(2));    
+        
+        //stop will persist sessions
+        manager.setMaxInactiveInterval(30); // change max inactive interval for *new* sessions
+        manager.stop();
+        
+        String expectedFilename = "_0.0.0.0_"+session.getId();
+        Assert.assertTrue("File should exist!", new File(testDir, expectedFilename).exists());
+        
+        
+        manager.start();
+        
+        //restore session
+        Session restoredSession = (Session)manager.getSession(sessionId);
+        Assert.assertNotNull(restoredSession);
+        
+        Object o = restoredSession.getAttribute("one");
+        Assert.assertNotNull(o);
+        
+        Assert.assertEquals(1, ((Integer)o).intValue());
+        Assert.assertEquals(5, restoredSession.getMaxInactiveInterval());     
+        
+        server.stop();
+    }
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/HashSessionManagerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/HashSessionManagerTest.java
deleted file mode 100644
index d776f40..0000000
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/HashSessionManagerTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
-//  ------------------------------------------------------------------------
-//  All rights reserved. This program and the accompanying materials
-//  are made available under the terms of the Eclipse Public License v1.0
-//  and Apache License v2.0 which accompanies this distribution.
-//
-//      The Eclipse Public License is available at
-//      http://www.eclipse.org/legal/epl-v10.html
-//
-//      The Apache License v2.0 is available at
-//      http://www.opensource.org/licenses/apache2.0.php
-//
-//  You may elect to redistribute this code under either of these licenses.
-//  ========================================================================
-//
-
-package org.eclipse.jetty.server.session;
-
-import java.io.File;
-
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.toolchain.test.FS;
-import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
-import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.thread.Scheduler;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class HashSessionManagerTest
-{
-    @Test
-    public void testDangerousSessionIdRemoval() throws Exception
-    {
-        final HashSessionManager manager = new HashSessionManager();
-        manager.setDeleteUnrestorableSessions(true);
-        manager.setLazyLoad(true);
-        File testDir = MavenTestingUtils.getTargetTestingDir("hashes");
-        testDir.mkdirs();
-        manager.setStoreDirectory(testDir);
-
-        MavenTestingUtils.getTargetFile("dangerFile.session").createNewFile();
-        
-        Assert.assertTrue("File should exist!", MavenTestingUtils.getTargetFile("dangerFile.session").exists());
-
-        manager.getSession("../../dangerFile.session");
-        
-        Assert.assertTrue("File should exist!", MavenTestingUtils.getTargetFile("dangerFile.session").exists());
-
-    }
-    
-   @Test
-    public void testValidSessionIdRemoval() throws Exception
-    {
-        final HashSessionManager manager = new HashSessionManager();
-        manager.setDeleteUnrestorableSessions(true);
-        manager.setLazyLoad(true);
-        File testDir = MavenTestingUtils.getTargetTestingDir("hashes");
-        FS.ensureEmpty(testDir);
-        
-        manager.setStoreDirectory(testDir);
-
-        Assert.assertTrue(new File(testDir, "validFile.session").createNewFile());
-        
-        Assert.assertTrue("File should exist!", new File(testDir, "validFile.session").exists());
-       
-        manager.getSession("validFile.session");
-
-        Assert.assertTrue("File shouldn't exist!", !new File(testDir,"validFile.session").exists());
-    }
-    
-    @Test
-    public void testHashSession() throws Exception
-    {
-        File testDir = MavenTestingUtils.getTargetTestingDir("saved");
-        IO.delete(testDir);
-        testDir.mkdirs();
-        
-        Server server = new Server();
-        SessionHandler handler = new SessionHandler();
-        handler.setServer(server);
-        HashSessionManager manager = new HashSessionManager()
-        {
-            @Override
-            public void doStart() throws Exception
-            {
-                super.doStart();
-                Scheduler timerBean = getBean(Scheduler.class);
-                Assert.assertNotNull(timerBean);
-            }
-
-            @Override
-            public void doStop() throws Exception
-            {
-                super.doStop();
-                Scheduler timerBean = getBean(Scheduler.class);
-                Assert.assertNull(timerBean);
-            }
-
-        };
-        manager.setStoreDirectory(testDir);
-        manager.setMaxInactiveInterval(5);
-        Assert.assertTrue(testDir.exists());
-        Assert.assertTrue(testDir.canWrite());
-        handler.setSessionManager(manager);
-        
-        AbstractSessionIdManager idManager = new HashSessionIdManager();
-        idManager.setWorkerName("foo");
-        manager.setSessionIdManager(idManager);
-        server.setSessionIdManager(idManager);
-        
-        server.start();
-        manager.start();
-        
-        HashedSession session = (HashedSession)manager.newHttpSession(new Request(null, null));
-        String sessionId = session.getId();
-        
-        session.setAttribute("one", new Integer(1));
-        session.setAttribute("two", new Integer(2));    
-        
-        //stop will persist sessions
-        manager.setMaxInactiveInterval(30); // change max inactive interval for *new* sessions
-        manager.stop();
-        
-        Assert.assertTrue("File should exist!", new File(testDir, session.getId()).exists());
-        
-        //start will restore sessions
-        manager.start();
-        
-        HashedSession restoredSession = (HashedSession)manager.getSession(sessionId);
-        Assert.assertNotNull(restoredSession);
-        
-        Object o = restoredSession.getAttribute("one");
-        Assert.assertNotNull(o);
-        
-        Assert.assertEquals(1, ((Integer)o).intValue());
-        Assert.assertEquals(5, restoredSession.getMaxInactiveInterval());     
-        
-        server.stop();
-    }
-}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
index 497c78d..0a1b3ce 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
@@ -30,6 +30,7 @@
 import javax.servlet.http.HttpSession;
 
 import org.eclipse.jetty.http.HttpCookie;
+import org.eclipse.jetty.server.Server;
 import org.junit.Test;
 
 /**
@@ -37,137 +38,118 @@
  */
 public class SessionCookieTest
 {
-    public class MockSession extends AbstractSession
+   
+    
+    
+    public class MockSessionStore extends AbstractSessionStore
     {
-        protected MockSession(AbstractSessionManager abstractSessionManager, long created, long accessed, String clusterId)
-        {
-            super(abstractSessionManager, created, accessed, clusterId);
-        }
 
         /** 
-         * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String)
+         * @see org.eclipse.jetty.server.session.SessionStore#newSession(org.eclipse.jetty.server.session.SessionKey, long, long, long, long)
          */
         @Override
-        public Object getAttribute(String name)
+        public Session newSession(HttpServletRequest request, String key, long time, long maxInactiveMs)
         {
+            // TODO Auto-generated method stub
             return null;
         }
 
         /** 
-         * @see javax.servlet.http.HttpSession#getAttributeNames()
+         * @see org.eclipse.jetty.server.session.SessionStore#shutdown()
          */
         @Override
-        public Enumeration<String> getAttributeNames()
+        public void shutdown()
         {
-            return null;
-        }
-
-        @Override
-        public String[] getValueNames()
-        {
-            return null;
-        }
-
-        /** 
-         * @see org.eclipse.jetty.server.session.AbstractSession#getAttributeMap()
-         */
-        @Override
-        public Map<String, Object> getAttributeMap()
-        {
-            return null;
-        }
-
-        /** 
-         * @see org.eclipse.jetty.server.session.AbstractSession#getAttributes()
-         */
-        @Override
-        public int getAttributes()
-        {
-            return 0;
-        }
-
-        /** 
-         * @see org.eclipse.jetty.server.session.AbstractSession#getNames()
-         */
-        @Override
-        public Set<String> getNames()
-        {
-            return null;
-        }
-
-        /** 
-         * @see org.eclipse.jetty.server.session.AbstractSession#clearAttributes()
-         */
-        @Override
-        public void clearAttributes()
-        {
+            // TODO Auto-generated method stub
             
         }
 
         /** 
-         * @see org.eclipse.jetty.server.session.AbstractSession#doPutOrRemove(java.lang.String, java.lang.Object)
+         * @see org.eclipse.jetty.server.session.AbstractSessionStore#newSession(org.eclipse.jetty.server.session.SessionData)
          */
         @Override
-        public Object doPutOrRemove(String name, Object value)
+        public Session newSession(SessionData data)
+        {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        /** 
+         * @see org.eclipse.jetty.server.session.AbstractSessionStore#doGet(org.eclipse.jetty.server.session.SessionKey)
+         */
+        @Override
+        public Session doGet(String key)
+        {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        /** 
+         * @see org.eclipse.jetty.server.session.AbstractSessionStore#doPutIfAbsent(org.eclipse.jetty.server.session.SessionKey, org.eclipse.jetty.server.session.Session)
+         */
+        @Override
+        public Session doPutIfAbsent(String key, Session session)
         {
             return null;
         }
 
         /** 
-         * @see org.eclipse.jetty.server.session.AbstractSession#doGet(java.lang.String)
+         * @see org.eclipse.jetty.server.session.AbstractSessionStore#doExists(org.eclipse.jetty.server.session.SessionKey)
          */
         @Override
-        public Object doGet(String name)
+        public boolean doExists(String key)
+        {
+            // TODO Auto-generated method stub
+            return false;
+        }
+
+        /** 
+         * @see org.eclipse.jetty.server.session.AbstractSessionStore#doDelete(org.eclipse.jetty.server.session.SessionKey)
+         */
+        @Override
+        public Session doDelete(String key)
         {
             return null;
         }
 
         /** 
-         * @see org.eclipse.jetty.server.session.AbstractSession#doGetAttributeNames()
+         * @see org.eclipse.jetty.server.session.AbstractSessionStore#doGetExpiredCandidates()
          */
         @Override
-        public Enumeration<String> doGetAttributeNames()
+        public Set<String> doGetExpiredCandidates()
         {
+            // TODO Auto-generated method stub
             return null;
-        }
-
+        } 
     }
 
+    
+    
     public class MockSessionIdManager extends AbstractSessionIdManager
     {
 
         /**
-         * @see org.eclipse.jetty.server.SessionIdManager#idInUse(java.lang.String)
+         * @param server
+         */
+        public MockSessionIdManager(Server server)
+        {
+            super(server);
+        }
+
+        /**
+         * @see org.eclipse.jetty.server.SessionIdManager#isIdInUse(java.lang.String)
          */
         @Override
-        public boolean idInUse(String id)
+        public boolean isIdInUse(String id)
         {
             return false;
         }
 
         /**
-         * @see org.eclipse.jetty.server.SessionIdManager#addSession(javax.servlet.http.HttpSession)
+         * @see org.eclipse.jetty.server.SessionIdManager#expireAll(java.lang.String)
          */
         @Override
-        public void addSession(HttpSession session)
-        {
-
-        }
-
-        /**
-         * @see org.eclipse.jetty.server.SessionIdManager#removeSession(javax.servlet.http.HttpSession)
-         */
-        @Override
-        public void removeSession(HttpSession session)
-        {
-
-        }
-
-        /**
-         * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
-         */
-        @Override
-        public void invalidateAll(String id)
+        public void expireAll(String id)
         {
 
         }
@@ -179,73 +161,49 @@
             
         }
 
-    }
-
-    public class MockSessionManager extends AbstractSessionManager
-    {
-
-        /**
-         * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
+        /** 
+         * @see org.eclipse.jetty.server.SessionIdManager#useId(java.lang.String)
          */
         @Override
-        protected void addSession(AbstractSession session)
-        {
-
-        }
-
-        /**
-         * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
-         */
-        @Override
-        public AbstractSession getSession(String idInCluster)
-        {
-            return null;
-        }
-
-        /**
-         * @see org.eclipse.jetty.server.session.AbstractSessionManager#shutdownSessions()
-         */
-        @Override
-        protected void shutdownSessions() throws Exception
-        {
-
-        }
-
-        /**
-         * @see org.eclipse.jetty.server.session.AbstractSessionManager#newSession(javax.servlet.http.HttpServletRequest)
-         */
-        @Override
-        protected AbstractSession newSession(HttpServletRequest request)
-        {
-            return null;
-        }
-
-        /**
-         * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
-         */
-        @Override
-        protected boolean removeSession(String idInCluster)
-        {
-            return false;
-        }
-
-        @Override
-        public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
+        public void useId(Session session)
         {
             // TODO Auto-generated method stub
             
         }
 
+        /** 
+         * @see org.eclipse.jetty.server.SessionIdManager#removeId(java.lang.String)
+         */
+        @Override
+        public boolean removeId(String id)
+        {
+            return true;
+        }
     }
+    
+    public class MockSessionManager extends SessionManager
+    {
+        public MockSessionManager()
+        {
+            _sessionStore = new MockSessionStore();
+            ((AbstractSessionStore)_sessionStore).setSessionDataStore(new NullSessionDataStore());
+        }
+    }
+
+  
 
     @Test
     public void testSecureSessionCookie () throws Exception
     {
-        MockSessionIdManager idMgr = new MockSessionIdManager();
+        Server server = new Server();
+        MockSessionIdManager idMgr = new MockSessionIdManager(server);
         idMgr.setWorkerName("node1");
         MockSessionManager mgr = new MockSessionManager();
         mgr.setSessionIdManager(idMgr);
-        MockSession session = new MockSession(mgr, System.currentTimeMillis(), System.currentTimeMillis(), "node1123"); //clusterId
+        
+        long now = System.currentTimeMillis();
+        
+        Session session = new Session(new SessionData("123", "_foo", "0.0.0.0", now, now, now, 30)); 
 
         SessionCookieConfig sessionCookieConfig = mgr.getSessionCookieConfig();
         sessionCookieConfig.setSecure(true);
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/Locker.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/Locker.java
index fae9c8a..7f1c44d 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/Locker.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/Locker.java
@@ -37,11 +37,12 @@
 public class Locker
 {
     private static final boolean SPIN = Boolean.getBoolean(Locker.class.getName() + ".spin");
+    private static final Lock LOCKED = new Lock();
 
     private final boolean _spin;
     private final ReentrantLock _lock = new ReentrantLock();
     private final AtomicReference<Thread> _spinLockState = new AtomicReference<>(null);
-    private final Lock _unlock = new Lock();
+    private final Lock _unlock = new UnLock();
 
     public Locker()
     {
@@ -61,6 +62,17 @@
             concLock();
         return _unlock;
     }
+    
+    
+    public Lock lockIfNotHeld ()
+    {
+        if (_spin)
+            throw new UnsupportedOperationException();
+        if (_lock.isHeldByCurrentThread())
+            return LOCKED;
+        _lock.lock();
+        return _unlock;
+    }
 
     private void spinLock()
     {
@@ -78,6 +90,7 @@
             return;
         }
     }
+    
 
     private void concLock()
     {
@@ -93,8 +106,16 @@
         else
             return _lock.isLocked();
     }
-
-    public class Lock implements AutoCloseable
+    public static class Lock implements AutoCloseable
+    {
+        @Override
+        public void close()
+        {
+        }
+    }
+    
+    
+    public class UnLock extends Lock
     {
         @Override
         public void close()
diff --git a/tests/test-sessions/pom.xml b/tests/test-sessions/pom.xml
index 8084a92..a33a34c 100644
--- a/tests/test-sessions/pom.xml
+++ b/tests/test-sessions/pom.xml
@@ -15,6 +15,7 @@
   <modules>
     <module>test-sessions-common</module>
     <module>test-hash-sessions</module>
+    <module>test-file-sessions</module>
     <module>test-jdbc-sessions</module>
     <module>test-mongodb-sessions</module>
     <module>test-infinispan-sessions</module>
diff --git a/tests/test-sessions/test-file-sessions/.gitignore b/tests/test-sessions/test-file-sessions/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/tests/test-sessions/test-file-sessions/pom.xml b/tests/test-sessions/test-file-sessions/pom.xml
new file mode 100644
index 0000000..f937ef4
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+// ========================================================================
+// Copyright (c) Webtide LLC
+// 
+// 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.apache.org/licenses/LICENSE-2.0.txt
+//
+// You may elect to redistribute this code under either of these licenses. 
+// ========================================================================
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.eclipse.jetty.tests</groupId>
+    <artifactId>test-sessions-parent</artifactId>
+    <version>9.3.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>test-file-sessions</artifactId>
+  <name>Jetty Tests :: Sessions :: File</name>
+  <url>http://www.eclipse.org/jetty</url>
+  <properties>
+    <bundle-symbolic-name>${project.groupId}.sessions.file</bundle-symbolic-name>
+  </properties>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <!-- DO NOT DEPLOY (or Release) -->
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+       <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-webapp</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.tests</groupId>
+            <artifactId>test-sessions-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.toolchain</groupId>
+      <artifactId>jetty-test-helper</artifactId>
+      <!-- Leaving at compile scope for intellij bug reasons -->
+      <scope>compile</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java
new file mode 100644
index 0000000..c10d815
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java
@@ -0,0 +1,54 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.File;
+
+import org.eclipse.jetty.util.IO;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ClientCrossContextSessionTest extends AbstractClientCrossContextSessionTest
+{
+    
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+       FileTestServer.teardown();
+    }
+    
+    public AbstractTestServer createServer(int port)
+    {
+        return new FileTestServer(port);
+    }
+
+    @Test
+    public void testCrossContextDispatch() throws Exception
+    {
+        super.testCrossContextDispatch();
+    }
+
+}
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/FileTestServer.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/FileTestServer.java
new file mode 100644
index 0000000..c3c89a4
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/FileTestServer.java
@@ -0,0 +1,84 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.File;
+
+import org.eclipse.jetty.server.SessionIdManager;
+import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.util.IO;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class FileTestServer extends AbstractTestServer
+{
+    static int __workers=0;
+    static File _tmpDir;
+    
+    public  static void setup ()
+    throws Exception
+    {
+        
+        _tmpDir = File.createTempFile("file", null);
+        _tmpDir.delete();
+        _tmpDir.mkdirs();
+        _tmpDir.deleteOnExit();
+    }
+    
+    
+    public static void teardown ()
+    {
+        IO.delete(_tmpDir);
+        _tmpDir = null;
+    }
+    
+    
+    
+    public FileTestServer(int port)
+    {
+        super(port, 30, 10);
+    }
+
+    public FileTestServer(int port, int maxInactivePeriod, int scavengePeriod)
+    {
+        super(port, maxInactivePeriod, scavengePeriod);
+    }
+
+
+    public SessionIdManager newSessionIdManager(Object config)
+    {
+        HashSessionIdManager mgr = new HashSessionIdManager(_server);
+        mgr.setWorkerName("worker"+(__workers++));
+        return mgr;
+    }
+
+    public SessionManager newSessionManager()
+    {
+        FileSessionManager manager = new FileSessionManager();
+        manager.getSessionDataStore().setStoreDir(_tmpDir);
+        return manager;
+    }
+
+    public SessionHandler newSessionHandler(SessionManager sessionManager)
+    {
+        return new SessionHandler(sessionManager);
+    }
+
+}
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
similarity index 60%
rename from tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
rename to tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
index 1a6c5d0..17f298a 100644
--- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ForwardedSessionTest.java
@@ -19,10 +19,6 @@
 
 package org.eclipse.jetty.server.session;
 
-import java.io.File;
-
-import org.eclipse.jetty.server.SessionManager;
-import org.eclipse.jetty.util.IO;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -34,47 +30,23 @@
  */
 public class ForwardedSessionTest extends AbstractForwardedSessionTest
 { 
-   File tmpDir;
     
     @Before
     public void before() throws Exception
     {
-        tmpDir = File.createTempFile("hash-session-forward-test", null);
-        tmpDir.delete();
-        tmpDir.mkdirs();
-        tmpDir.deleteOnExit();
+       FileTestServer.setup();
     }
     
     @After 
     public void after()
     {
-        IO.delete(tmpDir);
+        FileTestServer.teardown();
     }
     
     @Override
     public AbstractTestServer createServer(int port)
     {
-        return new HashTestServer(port)
-        {
-
-            @Override
-            public SessionManager newSessionManager()
-            {
-                HashSessionManager sessionManager = (HashSessionManager)super.newSessionManager();
-                sessionManager.setSavePeriod(2);
-                
-                try
-                {
-                    sessionManager.setStoreDirectory(tmpDir);
-                }
-                catch (Exception e)
-                {
-                    throw new IllegalStateException(e);
-                }
-                return sessionManager;
-            }
-
-        };
+        return new FileTestServer(port);
     }
 
     
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java
new file mode 100644
index 0000000..b044301
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ImmortalSessionTest.java
@@ -0,0 +1,45 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import org.junit.After;
+import org.junit.Before;
+
+public class ImmortalSessionTest extends AbstractImmortalSessionTest
+{
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+       FileTestServer.teardown();
+    }
+    
+    
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new FileTestServer(port,max,scavenge);
+    }
+
+}
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java
new file mode 100644
index 0000000..6076ae0
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/NewSessionTest.java
@@ -0,0 +1,53 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * NewSessionTest
+ */
+public class NewSessionTest extends AbstractNewSessionTest
+{
+    @Before
+    public void before() throws Exception
+    {
+        System.setProperty("org.eclipse.jetty.server.session.LEVEL", "DEBUG");
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+       FileTestServer.teardown();
+    }
+    
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new FileTestServer(port,max,scavenge);
+    }
+
+    @Test
+    public void testNewSession() throws Exception
+    {
+        super.testNewSession();
+    }
+}
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java
new file mode 100644
index 0000000..c34c7cb
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java
@@ -0,0 +1,52 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * OrphanedSessionTest
+ */
+public class OrphanedSessionTest extends AbstractOrphanedSessionTest
+{
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+       FileTestServer.teardown();
+    }
+    
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+       return new FileTestServer(port,max,scavenge);
+    }
+
+    @Test
+    public void testOrphanedSession() throws Exception
+    {
+        super.testOrphanedSession();
+    }
+}
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ProxySerializationTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ProxySerializationTest.java
similarity index 71%
rename from tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ProxySerializationTest.java
rename to tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ProxySerializationTest.java
index 964b613..5bfea93 100644
--- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ProxySerializationTest.java
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ProxySerializationTest.java
@@ -23,6 +23,8 @@
 
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -32,40 +34,29 @@
  */
 public class ProxySerializationTest extends AbstractProxySerializationTest
 {   
+    
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+       FileTestServer.teardown();
+    }
     /** 
      * @see org.eclipse.jetty.server.session.AbstractProxySerializationTest#createServer(int, int, int)
      */
     @Override
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
-        return new HashTestServer(port,max,scavenge);
+        return new FileTestServer(port,max,scavenge);
     }
     
     
     
-    
-    @Override
-    public void customizeContext(ServletContextHandler c)
-    {
-        if (c == null)
-            return;
-        
-        //Ensure that the HashSessionManager will persist sessions on passivation
-        HashSessionManager manager = (HashSessionManager)c.getSessionHandler().getSessionManager();
-        manager.setLazyLoad(false);
-        manager.setIdleSavePeriod(1);
-        try
-        {
-            File testDir = MavenTestingUtils.getTargetTestingDir("foo");
-            testDir.mkdirs();
-            manager.setStoreDirectory(testDir);
-        }
-        catch (Exception e)
-        {
-            throw new IllegalStateException(e);
-        }       
-    }
-
 
 
 
@@ -75,4 +66,14 @@
         super.testProxySerialization();
     }
 
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractProxySerializationTest#customizeContext(org.eclipse.jetty.servlet.ServletContextHandler)
+     */
+    @Override
+    public void customizeContext(ServletContextHandler c)
+    {
+        // TODO Auto-generated method stub
+        
+    }
+
 }
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java
new file mode 100644
index 0000000..147c63e
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ReentrantRequestSessionTest.java
@@ -0,0 +1,54 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * ReentrantRequestSessionTest
+ */
+public class ReentrantRequestSessionTest extends AbstractReentrantRequestSessionTest
+{
+    
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+       FileTestServer.teardown();
+    }
+    
+    public AbstractTestServer createServer(int port)
+    {
+        return new FileTestServer(port);
+    }
+
+    @Test
+    public void testReentrantRequestSession() throws Exception
+    {
+        super.testReentrantRequestSession();
+    }
+
+}
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/RemoveSessionTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/RemoveSessionTest.java
new file mode 100644
index 0000000..a6e1511
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/RemoveSessionTest.java
@@ -0,0 +1,52 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RemoveSessionTest extends AbstractRemoveSessionTest
+{ 
+    
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+       FileTestServer.teardown();
+    }
+    
+    
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new FileTestServer(port,max,scavenge);
+    }
+    
+    @Test
+    public void testRemoveSession() throws Exception
+    {
+        super.testRemoveSession();
+    }
+
+}
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ScatterGunLoadTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ScatterGunLoadTest.java
new file mode 100644
index 0000000..9a990f3
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ScatterGunLoadTest.java
@@ -0,0 +1,55 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * ScatterGunLoadTest
+ */
+public class ScatterGunLoadTest extends AbstractScatterGunLoadTest
+{
+
+    
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+       FileTestServer.teardown();
+    }
+    
+    public AbstractTestServer createServer(int port)
+    {
+        return new FileTestServer(port);
+    }
+
+    @Test
+    public void testLightLoad() throws Exception
+    {
+        super.testLightLoad();
+    }
+
+}
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ServerCrossContextSessionTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ServerCrossContextSessionTest.java
new file mode 100644
index 0000000..35c6492
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ServerCrossContextSessionTest.java
@@ -0,0 +1,51 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ServerCrossContextSessionTest extends AbstractServerCrossContextSessionTest
+{
+
+    
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+       FileTestServer.teardown();
+    }
+    
+    public AbstractTestServer createServer(int port)
+    {
+        return new FileTestServer(port);
+    }
+
+    @Test
+    public void testCrossContextDispatch() throws Exception
+    {
+        super.testCrossContextDispatch();
+    }
+}
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
new file mode 100644
index 0000000..8a7ae51
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
@@ -0,0 +1,48 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import org.junit.After;
+import org.junit.Before;
+
+public class SessionCookieTest extends AbstractSessionCookieTest
+{
+
+
+    
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+       FileTestServer.teardown();
+    }
+    
+    
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new FileTestServer(port, max, scavenge);
+    }
+
+}
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java
new file mode 100644
index 0000000..c28a156
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionInvalidateAndCreateTest.java
@@ -0,0 +1,51 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest
+{
+
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+       FileTestServer.teardown();
+    }
+    
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new FileTestServer(port,max,scavenge);
+    }
+    
+    @Test
+    public void testSessionScavenge() throws Exception
+    {
+        super.testSessionScavenge();
+    }
+}
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
new file mode 100644
index 0000000..e9a1b46
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.File;
+
+import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.util.IO;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SessionRenewTest extends AbstractSessionRenewTest
+{
+   
+    
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+        FileTestServer.teardown();
+    }
+    
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new FileTestServer(port, max, scavenge);
+    }
+
+    @Test
+    public void testSessionRenewal() throws Exception
+    {
+        super.testSessionRenewal();
+    }
+
+    
+}
diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSharedSaving.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSharedSaving.java
new file mode 100644
index 0000000..d22537d
--- /dev/null
+++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/SessionValueSharedSaving.java
@@ -0,0 +1,46 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import org.junit.After;
+import org.junit.Before;
+
+public class SessionValueSharedSaving extends AbstractSessionValueSavingTest
+{
+
+    @Before
+    public void before() throws Exception
+    {
+       FileTestServer.setup();
+    }
+    
+    @After 
+    public void after()
+    {
+        FileTestServer.teardown();
+    }
+    
+    
+    @Override
+    public AbstractTestServer createServer(int port, int max, int scavenge)
+    {
+        return new FileTestServer(port,max,scavenge);
+    }
+
+}
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTestSupport.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTestSupport.java
index 060efda..f74532b 100644
--- a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTestSupport.java
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudSessionTestSupport.java
@@ -295,7 +295,7 @@
     public void listSessions () throws Exception
     {
         ensureDatastore();
-        GqlQuery.Builder builder = Query.gqlQueryBuilder(ResultType.ENTITY, "select * from "+GCloudSessionManager.KIND);
+        GqlQuery.Builder builder = Query.gqlQueryBuilder(ResultType.ENTITY, "select * from "+GCloudSessionDataStore.KIND);
        
         Query<Entity> query = builder.build();
     
@@ -315,7 +315,7 @@
     {
         ensureDatastore();
         StructuredQuery<ProjectionEntity> keyOnlyProjectionQuery = Query.projectionEntityQueryBuilder()
-                .kind(GCloudSessionManager.KIND)
+                .kind(GCloudSessionDataStore.KIND)
                 .projection(Projection.property("__key__"))
                 .limit(100)
                 .build();  
@@ -334,7 +334,7 @@
     {
        ensureDatastore();
         StructuredQuery<ProjectionEntity> keyOnlyProjectionQuery = Query.projectionEntityQueryBuilder()
-                .kind(GCloudSessionManager.KIND)
+                .kind(GCloudSessionDataStore.KIND)
                 .projection(Projection.property("__key__"))
                 .limit(100)
                 .build();  
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java
index 69cd07e..3158bd9 100644
--- a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java
@@ -21,8 +21,10 @@
 
 import org.eclipse.jetty.server.SessionIdManager;
 import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.server.session.AbstractSessionStore;
 import org.eclipse.jetty.server.session.AbstractTestServer;
 import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.server.session.StalePeriodStrategy;
 
 import com.google.gcloud.datastore.Datastore;
 import com.google.gcloud.datastore.DatastoreFactory;
@@ -79,8 +81,10 @@
     {
         GCloudSessionManager sessionManager = new GCloudSessionManager();
         sessionManager.setSessionIdManager((GCloudSessionIdManager)_sessionIdManager);
-        sessionManager.setStaleIntervalSec(STALE_INTERVAL_SEC);
-        sessionManager.setScavengeIntervalSec(_scavengePeriod);
+        sessionManager.getSessionDataStore().setGCloudConfiguration(((GCloudSessionIdManager)_sessionIdManager).getConfig());
+        StalePeriodStrategy staleStrategy = new StalePeriodStrategy();
+        staleStrategy.setStaleSec(STALE_INTERVAL_SEC);
+       ((AbstractSessionStore)sessionManager.getSessionStore()).setStaleStrategy(staleStrategy);
         return sessionManager;
         
     }
diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ImmortalSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ImmortalSessionTest.java
index 3b853d2..0bc5dfe 100644
--- a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ImmortalSessionTest.java
+++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/ImmortalSessionTest.java
@@ -55,7 +55,7 @@
     @Override
     public AbstractTestServer createServer(int port, int maxInactiveMs, int scavengeMs)
     {
-       return new GCloudTestServer(port, port, scavengeMs, _testSupport.getConfiguration());
+       return new GCloudTestServer(port, maxInactiveMs, scavengeMs, _testSupport.getConfiguration());
     }
 
     @Test
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java
index b153934..10700a1 100644
--- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/ClientCrossContextSessionTest.java
@@ -18,14 +18,16 @@
 
 package org.eclipse.jetty.server.session;
 
+import java.io.File;
+
+import org.eclipse.jetty.util.IO;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 public class ClientCrossContextSessionTest extends AbstractClientCrossContextSessionTest
 {
-    public AbstractTestServer createServer(int port)
-    {
-        return new HashTestServer(port);
-    }
+
 
     @Test
     public void testCrossContextDispatch() throws Exception
@@ -33,4 +35,13 @@
         super.testCrossContextDispatch();
     }
 
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractClientCrossContextSessionTest#createServer(int)
+     */
+    @Override
+    public AbstractTestServer createServer(int port)
+    {
+        return new HashTestServer(port);
+    }
+
 }
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java
index 05bd34f..9d5b12d 100644
--- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java
@@ -26,7 +26,8 @@
  */
 public class HashTestServer extends AbstractTestServer
 {
-
+    static int __workers=0;
+    
     public HashTestServer(int port)
     {
         super(port, 30, 10);
@@ -40,13 +41,14 @@
 
     public SessionIdManager newSessionIdManager(Object config)
     {
-        return new HashSessionIdManager();
+        HashSessionIdManager mgr = new HashSessionIdManager(_server);
+        mgr.setWorkerName("worker"+(__workers++));
+        return mgr;
     }
 
     public SessionManager newSessionManager()
     {
         HashSessionManager manager = new HashSessionManager();
-        manager.setScavengePeriod(_scavengePeriod);
         return manager;
     }
 
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java
index 13f2b89..463d6e3 100644
--- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java
@@ -47,6 +47,8 @@
  * IdleSessionTest
  *
  * Checks that a session can be idled and de-idled on the next request if it hasn't expired.
+ * 
+ * TODO support session idling in FileSessionDataStore?
  *
  */
 public class IdleSessionTest
@@ -67,17 +69,10 @@
         @Override
         public SessionManager newSessionManager()
         {
-            try
-            {
-                HashSessionManager manager = (HashSessionManager)super.newSessionManager();
-                manager.setStoreDirectory(_storeDir);
-                manager.setIdleSavePeriod(_idlePeriod);
-                return manager;
-            }
-            catch ( IOException e)
-            {
-                return null;
-            }
+            HashSessionManager manager = (HashSessionManager)super.newSessionManager();
+            //manager.getSessionDataStore().setStoreDir(_storeDir);
+            //manager.setIdleSavePeriod(_idlePeriod);
+            return manager;
         }
 
 
@@ -103,7 +98,6 @@
         }
     }
 
-    @Test
     public void testSessionIdle() throws Exception
     {
         String contextPath = "";
@@ -111,7 +105,7 @@
         int inactivePeriod = 200;
         int scavengePeriod = 3;
         int idlePeriod = 5;
-        ((StdErrLog)Log.getLogger(org.eclipse.jetty.server.session.HashedSession.class)).setHideStacks(true);
+        ((StdErrLog)Log.getLogger("org.eclipse.jetty.server.session")).setHideStacks(true);
         System.setProperty("org.eclipse.jetty.STACKS", "false");
         File storeDir = new File (System.getProperty("java.io.tmpdir"), "idle-test");
         storeDir.deleteOnExit();
@@ -229,7 +223,7 @@
                 HttpSession session = request.getSession(true);
                 session.setAttribute("test", "test");
                 originalId = session.getId();
-                assertTrue(!((HashedSession)session).isIdled());
+//                assertTrue(!((HashedSession)session).isIdled());
             }
             else if ("test".equals(action))
             {
@@ -237,7 +231,7 @@
                 assertTrue(session != null);
                 assertTrue(originalId.equals(session.getId()));
                 assertEquals("test", session.getAttribute("test"));
-                assertTrue(!((HashedSession)session).isIdled());
+ //               assertTrue(!((HashedSession)session).isIdled());
             }
             else if ("testfail".equals(action))
             {
diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
index 6566176..c5b4d19 100644
--- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
+++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/SessionRenewTest.java
@@ -28,47 +28,11 @@
 
 public class SessionRenewTest extends AbstractSessionRenewTest
 {
-    File tmpDir;
-    
-    @Before
-    public void before() throws Exception
-    {
-        tmpDir = File.createTempFile("hash-session-renew-test", null);
-        tmpDir.delete();
-        tmpDir.mkdirs();
-        tmpDir.deleteOnExit();
-    }
-    
-    @After 
-    public void after()
-    {
-        IO.delete(tmpDir);
-    }
     
     @Override
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
-        return new HashTestServer(port, max, scavenge)
-        {
-
-            @Override
-            public SessionManager newSessionManager()
-            {
-                HashSessionManager sessionManager = (HashSessionManager)super.newSessionManager();
-                sessionManager.setSavePeriod(2);
-                
-                try
-                {
-                    sessionManager.setStoreDirectory(tmpDir);
-                }
-                catch (Exception e)
-                {
-                    throw new IllegalStateException(e);
-                }
-                return sessionManager;
-            }
-
-        };
+        return new HashTestServer(port, max, scavenge);
     }
 
     @Test
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java
index 4b098cc..be05166 100644
--- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java
@@ -63,10 +63,10 @@
     {
         InfinispanSessionManager sessionManager = new InfinispanSessionManager();
         sessionManager.setSessionIdManager((InfinispanSessionIdManager)_sessionIdManager);
-        sessionManager.setCache(((InfinispanSessionIdManager)_sessionIdManager).getCache());
-        sessionManager.setStaleIntervalSec(1);
-        sessionManager.setScavengeInterval(_scavengePeriod);
-        
+        sessionManager.getSessionDataStore().setCache(((InfinispanSessionIdManager)_sessionIdManager).getCache());
+        StalePeriodStrategy staleStrategy = new StalePeriodStrategy();
+        staleStrategy.setStaleSec(1);
+       ((AbstractSessionStore)sessionManager.getSessionStore()).setStaleStrategy(staleStrategy);
         return sessionManager;
     }
 
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java
index 38ae417..d06f96c 100644
--- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java
@@ -88,7 +88,6 @@
         _tmpdir = File.createTempFile("infini", "span");
         _tmpdir.delete();
         _tmpdir.mkdir();
-        System.err.println("Temp file: "+_tmpdir);
         Configuration config = _builder.persistence().addSingleFileStore().location(_tmpdir.getAbsolutePath()).storeAsBinary().build();
         _manager.defineConfiguration(_name, config);
        }
diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java
index 78f06ea..4aef561 100644
--- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java
+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/LastAccessTimeTest.java
@@ -52,14 +52,5 @@
     {
         super.testLastAccessTime();
     }
-
-    @Override
-    public void assertAfterScavenge(AbstractSessionManager manager)
-    {
-        //The infinispan session manager will remove a session from its local memory that was a candidate to be scavenged if
-        //it checks with the cluster and discovers that another node is managing it, so the count is 0
-        assertSessionCounts(0, 1, 1, manager);
-    }
-
     
 }
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java
index 19b8706..ee314b5 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java
@@ -40,7 +40,7 @@
         //that the node will re-load the session from the database and discover that it has gone.
         try
         {
-            Thread.sleep(2 * JdbcTestServer.SAVE_INTERVAL * 1000);
+            Thread.sleep(2 * JdbcTestServer.STALE_INTERVAL * 1000);
         }
         catch (InterruptedException e)
         {
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java
index 586f933..86dd8b0 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java
@@ -38,8 +38,21 @@
     public static final String DRIVER_CLASS = "org.apache.derby.jdbc.EmbeddedDriver";
     public static final String DEFAULT_CONNECTION_URL = "jdbc:derby:memory:sessions;create=true";
     public static final String DEFAULT_SHUTDOWN_URL = "jdbc:derby:memory:sessions;drop=true";
-    public static final int SAVE_INTERVAL = 1;
+    public static final int STALE_INTERVAL = 1;
     
+
+    public static final String EXPIRY_COL = "extime";
+    public static final String LAST_ACCESS_COL = "latime";
+    public static final String LAST_NODE_COL = "lnode";
+    public static final String LAST_SAVE_COL = "lstime";
+    public static final String MAP_COL = "mo";
+    public static final String MAX_IDLE_COL = "mi";  
+    public static final String TABLE = "mysessions";
+    public static final String ID_COL = "mysessionid";
+    public static final String ACCESS_COL = "atime";
+    public static final String CONTEXT_COL = "cpath";
+    public static final String COOKIE_COL = "cooktime";
+    public static final String CREATE_COL = "ctime";
     
     static 
     {
@@ -103,28 +116,14 @@
         synchronized(JdbcTestServer.class)
         {
             JDBCSessionIdManager idManager = new JDBCSessionIdManager(_server);
-            idManager.setScavengeInterval(_scavengePeriod);
             idManager.setWorkerName("w"+(__workers++));
-            idManager.setDriverInfo(DRIVER_CLASS, (config==null?DEFAULT_CONNECTION_URL:(String)config));
+            idManager.getDatabaseAdaptor().setDriverInfo(DRIVER_CLASS, (config==null?DEFAULT_CONNECTION_URL:(String)config));
             JDBCSessionIdManager.SessionIdTableSchema idTableSchema = new JDBCSessionIdManager.SessionIdTableSchema();
             idTableSchema.setTableName("mysessionids");
             idTableSchema.setIdColumn("myid");
-            idManager.setSessionIdTableSchema(idTableSchema);
-            
-            JDBCSessionIdManager.SessionTableSchema sessionTableSchema = new JDBCSessionIdManager.SessionTableSchema();
-            sessionTableSchema.setTableName("mysessions");
-            sessionTableSchema.setIdColumn("mysessionid");
-            sessionTableSchema.setAccessTimeColumn("atime");
-            sessionTableSchema.setContextPathColumn("cpath");
-            sessionTableSchema.setCookieTimeColumn("cooktime");
-            sessionTableSchema.setCreateTimeColumn("ctime");
-            sessionTableSchema.setExpiryTimeColumn("extime");
-            sessionTableSchema.setLastAccessTimeColumn("latime");
-            sessionTableSchema.setLastNodeColumn("lnode");
-            sessionTableSchema.setLastSavedTimeColumn("lstime");
-            sessionTableSchema.setMapColumn("mo");
-            sessionTableSchema.setMaxIntervalColumn("mi");           
-            idManager.setSessionTableSchema(sessionTableSchema);
+
+    
+
             
             return idManager;
         }
@@ -133,12 +132,34 @@
     /** 
      * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionManager()
      */
+    /** 
+     * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionManager()
+     */
     @Override
     public SessionManager newSessionManager()
     {
         JDBCSessionManager manager =  new JDBCSessionManager();
         manager.setSessionIdManager((JDBCSessionIdManager)_sessionIdManager);
-        manager.setSaveInterval(SAVE_INTERVAL); //ensure we save any changes to the session at least once per second
+        JDBCSessionDataStore ds = manager.getSessionDataStore();
+        ds.setGracePeriodSec(_scavengePeriod);
+        manager.getDatabaseAdaptor().setDriverInfo(DRIVER_CLASS, DEFAULT_CONNECTION_URL);
+        JDBCSessionDataStore.SessionTableSchema sessionTableSchema = new JDBCSessionDataStore.SessionTableSchema();
+        sessionTableSchema.setTableName(TABLE);
+        sessionTableSchema.setIdColumn(ID_COL);
+        sessionTableSchema.setAccessTimeColumn(ACCESS_COL);
+        sessionTableSchema.setContextPathColumn(CONTEXT_COL);
+        sessionTableSchema.setCookieTimeColumn(COOKIE_COL);
+        sessionTableSchema.setCreateTimeColumn(CREATE_COL);
+        sessionTableSchema.setExpiryTimeColumn(EXPIRY_COL);
+        sessionTableSchema.setLastAccessTimeColumn(LAST_ACCESS_COL);
+        sessionTableSchema.setLastNodeColumn(LAST_NODE_COL);
+        sessionTableSchema.setLastSavedTimeColumn(LAST_SAVE_COL);
+        sessionTableSchema.setMapColumn(MAP_COL);
+        sessionTableSchema.setMaxIntervalColumn(MAX_IDLE_COL);       
+        ds.setSessionTableSchema(sessionTableSchema);
+        StalePeriodStrategy staleStrategy = new StalePeriodStrategy();
+        staleStrategy.setStaleSec(STALE_INTERVAL);
+       ((AbstractSessionStore)manager.getSessionStore()).setStaleStrategy(staleStrategy);
         return manager;
     }
 
@@ -175,8 +196,8 @@
         {
             con = DriverManager.getConnection(DEFAULT_CONNECTION_URL);
             PreparedStatement statement = con.prepareStatement("select * from "+
-                    ((JDBCSessionIdManager)_sessionIdManager)._sessionTableSchema.getTableName()+
-                    " where "+((JDBCSessionIdManager)_sessionIdManager)._sessionTableSchema.getIdColumn()+" = ?");
+                    TABLE+
+                    " where "+ID_COL+" = ?");
             statement.setString(1, id);
             ResultSet result = statement.executeQuery();
             if (verbose)
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java
index 2bc1d34..b978711 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java
@@ -75,7 +75,7 @@
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
-                
+
                 //do another request to change the maxinactive interval
                 Request request = client.newRequest("http://localhost:" + port + "/mod/test?action=change&val="+newMaxInactive);
                 request.header("Cookie", sessionCookie);
@@ -87,7 +87,6 @@
                 Thread.currentThread().sleep(10*1000L);
                 
                 //do another request using the cookie to ensure the session is still there
-               
                 request= client.newRequest("http://localhost:" + port + "/mod/test?action=test");
                 request.header("Cookie", sessionCookie);
                 response = request.send();
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java
index 909c5bb..4d6d31a 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java
@@ -53,7 +53,7 @@
     @Test
     public void testSessionReloadWithMissingClass() throws Exception
     {
-        ((StdErrLog)Log.getLogger(org.eclipse.jetty.server.session.JDBCSessionManager.class)).setHideStacks(true);
+        ((StdErrLog)Log.getLogger("org.eclipse.jetty.server.session")).setHideStacks(true);
         Resource.setDefaultUseCaches(false);
         String contextPath = "/foo";
 
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java
index 7f82d76..8018d3d 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java
@@ -43,7 +43,7 @@
  *  SaveIntervalTest
  *
  *  Checks to see that potentially stale sessions that have not
- *  changed are not always reloaded from the datase.
+ *  changed are not always reloaded from the database.
  *
  *  This test is Ignored because it takes a little while to run.
  *
@@ -66,7 +66,10 @@
         TestSaveIntervalServlet servlet = new TestSaveIntervalServlet();
         holder.setServlet(servlet);
         ctxA.addServlet(holder, "/test");
-        ((JDBCSessionManager)ctxA.getSessionHandler().getSessionManager()).setSaveInterval(SAVE);
+
+        StalePeriodStrategy strategy = new StalePeriodStrategy();
+        strategy.setStaleSec(SAVE);
+       ((AbstractSessionStore)((JDBCSessionManager)ctxA.getSessionHandler().getSessionManager()).getSessionStore()).setStaleStrategy(strategy);
         server.start();
         int port=server.getPort();
         try
@@ -82,7 +85,7 @@
                 assertTrue(sessionCookie != null);
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
-                long lastSaved = ((JDBCSessionManager.Session)servlet._session).getLastSaved();
+                long lastSaved = ((Session)servlet._session).getSessionData().getLastSaved();
                 
                 
                 //do another request to change the session attribute
@@ -90,7 +93,7 @@
                 request.header("Cookie", sessionCookie);
                 response = request.send();
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                long tmp = ((JDBCSessionManager.Session)servlet._session).getLastSaved();
+                long tmp = ((Session)servlet._session).getSessionData().getLastSaved();
                 assertNotEquals(lastSaved, tmp); //set of attribute will cause save to db
                 lastSaved = tmp;
                 
@@ -106,7 +109,7 @@
                 request.header("Cookie", sessionCookie);
                 response = request.send();
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                tmp = ((JDBCSessionManager.Session)servlet._session).getLastSaved();
+                tmp = ((Session)servlet._session).getSessionData().getLastSaved();
                 assertNotEquals(lastSaved, tmp);
                 lastSaved = tmp;
               
@@ -120,7 +123,7 @@
                 request.header("Cookie", sessionCookie);
                 response = request.send();
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                tmp = ((JDBCSessionManager.Session)servlet._session).getLastSaved();
+                tmp = ((Session)servlet._session).getSessionData().getLastSaved();
                 assertEquals(lastSaved, tmp); //the save interval did not expire, so update to the access time will not have been persisted
             }
             finally
@@ -154,7 +157,6 @@
             if ("create".equals(action))
             {
                 HttpSession session = request.getSession(true);
-                System.err.println("CREATE: Session id="+session.getId());
                 _session = session;
                 return;
             }
@@ -164,8 +166,7 @@
                 HttpSession session = request.getSession(false);
                 if (session == null)
                     throw new ServletException("Session is null for action=change");
-               
-                System.err.println("SET: Session id="+session.getId());
+
                 session.setAttribute("aaa", "12345");
                 assertEquals(_session.getId(), session.getId());
                 return;
@@ -176,7 +177,7 @@
                 HttpSession session = request.getSession(false);
                 if (session == null)
                     throw new ServletException("Session does not exist");
-                System.err.println("TICKLE: Session id="+session.getId());
+
                 assertEquals(_session.getId(), session.getId());
                 return;
             }
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/StopSessionManagerPreserveSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/StopSessionManagerPreserveSessionTest.java
index a002151..f320209 100644
--- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/StopSessionManagerPreserveSessionTest.java
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/StopSessionManagerPreserveSessionTest.java
@@ -42,7 +42,6 @@
         try
         {
             boolean actual = _server.existsInSessionTable(_id, true);
-            System.err.println(expected+":"+actual);
             assertEquals(expected, actual);
         }
         catch (Exception e)
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java
index 3525b2c..e787189 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java
@@ -34,8 +34,10 @@
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.nosql.NoSqlSession;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.eclipse.jetty.server.session.Session;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 /**
@@ -48,7 +50,19 @@
 public class AttributeNameTest 
 {
 
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
 
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
 
     public AbstractTestServer createServer(int port, int max, int scavenge)
     throws Exception
@@ -129,14 +143,14 @@
             String action = request.getParameter("action");
             if ("init".equals(action))
             {
-                NoSqlSession session = (NoSqlSession)request.getSession(true);
+                Session session = (Session)request.getSession(true);
                 session.setAttribute("a.b.c",System.currentTimeMillis());               
                 sendResult(session,httpServletResponse.getWriter());
-                
+
             }
             else
             {
-                NoSqlSession session = (NoSqlSession)request.getSession(false);
+                Session session = (Session)request.getSession(false);
                 assertNotNull(session);     
                 assertNotNull(session.getAttribute("a.b.c"));
                 sendResult(session,httpServletResponse.getWriter());
@@ -144,7 +158,7 @@
 
         }
 
-        private void sendResult(NoSqlSession session, PrintWriter writer)
+        private void sendResult(Session session, PrintWriter writer)
         {
             if (session != null)
             {
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ClientCrossContextSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ClientCrossContextSessionTest.java
index e30476d..4a5fc2e 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ClientCrossContextSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ClientCrossContextSessionTest.java
@@ -20,10 +20,26 @@
 
 import org.eclipse.jetty.server.session.AbstractClientCrossContextSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class ClientCrossContextSessionTest extends AbstractClientCrossContextSessionTest
 {
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
     public AbstractTestServer createServer(int port)
     {
         return new MongoTestServer(port);
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ForwardedSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ForwardedSessionTest.java
index 7697ab2..cf34224 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ForwardedSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ForwardedSessionTest.java
@@ -21,6 +21,8 @@
 
 import org.eclipse.jetty.server.session.AbstractForwardedSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 /**
@@ -30,7 +32,21 @@
  */
 public class ForwardedSessionTest extends AbstractForwardedSessionTest
 {
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
 
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
+    
     /** 
      * @see org.eclipse.jetty.server.session.AbstractForwardedSessionTest#createServer(int)
      */
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/InvalidateSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/InvalidateSessionTest.java
index 006da7b..d134a01 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/InvalidateSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/InvalidateSessionTest.java
@@ -21,11 +21,27 @@
 
 import org.eclipse.jetty.server.session.AbstractInvalidationSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class InvalidateSessionTest extends AbstractInvalidationSessionTest
 {
 
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
     @Override
     public AbstractTestServer createServer(int port)
     {
@@ -37,11 +53,10 @@
     {
         try
         {
-            Thread.currentThread().sleep(2000);
+            Thread.sleep(2 * MongoTestServer.STALE_INTERVAL * 1000);
         }
-        catch (Exception e)
+        catch (InterruptedException e)
         {
-            e.printStackTrace();
         }
     }
     
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LastAccessTimeTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LastAccessTimeTest.java
index 86a2a16..2120f64 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LastAccessTimeTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LastAccessTimeTest.java
@@ -20,19 +20,40 @@
 
 import org.eclipse.jetty.server.session.AbstractLastAccessTimeTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class LastAccessTimeTest extends AbstractLastAccessTimeTest
 {
 
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
         return new MongoTestServer(port,max,scavenge);
     }
 
+   
+    
+    
     @Test
     public void testLastAccessTime() throws Exception
     {
         super.testLastAccessTime();
     }
+
+ 
 }
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LocalSessionScavengingTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LocalSessionScavengingTest.java
index 3a6a7c5..e60ef72 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LocalSessionScavengingTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/LocalSessionScavengingTest.java
@@ -20,15 +20,31 @@
 
 import org.eclipse.jetty.server.session.AbstractLocalSessionScavengingTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 
 public class LocalSessionScavengingTest extends AbstractLocalSessionScavengingTest
 {
 
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
     @Override
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
        MongoTestServer mserver=new MongoTestServer(port,max,scavenge);
-       ((MongoSessionIdManager)mserver.getServer().getSessionIdManager()).setScavengeBlockSize(0);
+       
        return mserver;
     }
 
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java
index cc9abe3..03a0aaa 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java
@@ -20,15 +20,15 @@
 
 import java.net.UnknownHostException;
 
-import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.SessionIdManager;
 import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.server.session.AbstractSessionStore;
 import org.eclipse.jetty.server.session.AbstractTestServer;
 import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.server.session.StalePeriodStrategy;
 
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBCursor;
-import com.mongodb.DBObject;
+import com.mongodb.DBCollection;
+import com.mongodb.Mongo;
 import com.mongodb.MongoException;
 
 
@@ -37,32 +37,30 @@
  */
 public class MongoTestServer extends AbstractTestServer
 {
+    public static final int STALE_INTERVAL = 1;
     static int __workers=0;
-    private boolean _saveAllAttributes = false; // false save dirty, true save all
-    private int _saveInterval = 0;
     
     
-    public static class TestMongoSessionIdManager extends MongoSessionIdManager 
+    
+    public static void dropCollection () throws MongoException, UnknownHostException
     {
-
-        public TestMongoSessionIdManager(Server server) throws UnknownHostException, MongoException
-        {
-            super(server);
-        }
-        
-        
-        public void deleteAll ()
-        {
-            
-            DBCursor checkSessions = _sessions.find();
-
-            for (DBObject session : checkSessions)
-            {
-                _sessions.remove(session);
-            }
-        }
+        new Mongo().getDB("HttpSessions").getCollection("testsessions").drop();
     }
     
+    
+    public static void createCollection() throws UnknownHostException, MongoException
+    {
+        new Mongo().getDB("HttpSessions").createCollection("testsessions", null);
+    }
+    
+    
+    public static DBCollection getCollection () throws UnknownHostException, MongoException 
+    {
+        return new Mongo().getDB("HttpSessions").getCollection("testsessions");
+    }
+    
+
+    
     public MongoTestServer(int port)
     {
         super(port, 30, 10);
@@ -71,25 +69,21 @@
     public MongoTestServer(int port, int maxInactivePeriod, int scavengePeriod)
     {
         super(port, maxInactivePeriod, scavengePeriod);
-        _saveInterval = 0;
     }
     
     
     public MongoTestServer(int port, int maxInactivePeriod, int scavengePeriod, boolean saveAllAttributes)
     {
         super(port, maxInactivePeriod, scavengePeriod);
-        _saveAllAttributes = saveAllAttributes;
     }
 
     public SessionIdManager newSessionIdManager(Object config)
     {
         try
         {
-            System.err.println("MongoTestServer:SessionIdManager scavenge: delay:"+ _scavengePeriod + " period:"+_scavengePeriod);
-            MongoSessionIdManager idManager = new TestMongoSessionIdManager(_server);
+            MongoSessionIdManager idManager = new MongoSessionIdManager(_server, getCollection());
             idManager.setWorkerName("w"+(__workers++));
-            idManager.setScavengePeriod(_scavengePeriod);                  
-
+               
             return idManager;
         }
         catch (Exception e)
@@ -104,15 +98,16 @@
         try
         {
             manager = new MongoSessionManager();
+            manager.getSessionDataStore().setGracePeriodSec(_scavengePeriod);
+            StalePeriodStrategy staleStrategy = new StalePeriodStrategy();
+            staleStrategy.setStaleSec(STALE_INTERVAL);
+           ((AbstractSessionStore)manager.getSessionStore()).setStaleStrategy(staleStrategy);
         }
         catch (Exception e)
         {
             throw new RuntimeException(e);
         }
         
-        manager.setSavePeriod(_saveInterval);
-        manager.setStalePeriod(0);
-        manager.setSaveAllAttributes(_saveAllAttributes);
         return manager;
     }
 
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/NewSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/NewSessionTest.java
index 91fa831..52c26f2 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/NewSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/NewSessionTest.java
@@ -20,6 +20,8 @@
 
 import org.eclipse.jetty.server.session.AbstractNewSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 /**
@@ -27,6 +29,20 @@
  */
 public class NewSessionTest extends AbstractNewSessionTest
 {
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
 
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/OrphanedSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/OrphanedSessionTest.java
index b4c792a..cfd8691 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/OrphanedSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/OrphanedSessionTest.java
@@ -20,6 +20,8 @@
 
 import org.eclipse.jetty.server.session.AbstractOrphanedSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 /**
@@ -27,6 +29,21 @@
  */
 public class OrphanedSessionTest extends AbstractOrphanedSessionTest
 {
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
+    
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
        return new MongoTestServer(port,max,scavenge);
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java
index 7b9cd4d..a805065 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java
@@ -35,7 +35,7 @@
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.api.Request;
 import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.junit.Test;
+import org.junit.Ignore;
 
 import com.mongodb.BasicDBObject;
 import com.mongodb.DBCollection;
@@ -60,7 +60,7 @@
     
     
     
-    @Test
+    @Ignore
     public void testPurgeInvalidSession() throws Exception
     {
         String contextPath = "";
@@ -76,11 +76,11 @@
 
         MongoSessionManager sessionManager = (MongoSessionManager)context.getSessionHandler().getSessionManager();
         MongoSessionIdManager idManager = (MongoSessionIdManager)server.getServer().getSessionIdManager();
-        idManager.setPurge(true);
+      /*  idManager.setPurge(true);
         idManager.setPurgeDelay(purgeDelay); 
         idManager.setPurgeInvalidAge(purgeInvalidAge); //purge invalid sessions older than 
         idManager.setPurgeValidAge(purgeValidAge); //purge valid sessions older than
-        
+        */
         
         
         server.start();
@@ -126,7 +126,7 @@
     }
 
 
-    @Test
+    @Ignore
     public void testPurgeInvalidSessionsWithLimit() throws Exception
     {
         String contextPath = "";
@@ -142,11 +142,11 @@
         // disable purging so we can call it manually below
         MongoSessionManager sessionManager = (MongoSessionManager)context.getSessionHandler().getSessionManager();
         MongoSessionIdManager idManager = (MongoSessionIdManager)server.getServer().getSessionIdManager();
-        idManager.setPurge(false);
+    /*    idManager.setPurge(false);
         idManager.setPurgeLimit(purgeLimit);
         idManager.setPurgeInvalidAge(purgeInvalidAge);
         // don't purge valid sessions
-        idManager.setPurgeValidAge(0);
+        idManager.setPurgeValidAge(0);*/
 
 
         server.start();
@@ -154,7 +154,7 @@
         try
         {
             // cleanup any previous sessions that are invalid so that we are starting fresh
-            idManager.purgeFully();
+         /*   idManager.purgeFully();*/
             long sessionCountAtTestStart = sessionManager.getSessionStoreCount();
 
             HttpClient client = new HttpClient();
@@ -185,7 +185,7 @@
                 assertEquals("Expected to find right number of sessions before purge", sessionCountAtTestStart + (purgeLimit * 2), sessionManager.getSessionStoreCount());
 
                 // run our purge we should still have items in the DB
-                idManager.purge();
+             /*   idManager.purge();*/
                 assertEquals("Expected to find sessions remaining in db after purge run with limit set",
                         sessionCountAtTestStart + purgeLimit, sessionManager.getSessionStoreCount());
             }
@@ -237,9 +237,9 @@
                 //still in db, just marked as invalid
                 dbSession = _sessions.findOne(new BasicDBObject("id", id));       
                 assertNotNull(dbSession);
-                assertTrue(dbSession.containsField(MongoSessionManager.__INVALIDATED));
-                assertTrue(dbSession.containsField(MongoSessionManager.__VALID));
-                assertTrue(dbSession.get(MongoSessionManager.__VALID).equals(false));
+         /*       assertTrue(dbSession.containsField(MongoSessionManager.__INVALIDATED));*/
+                assertTrue(dbSession.containsField(MongoSessionDataStore.__VALID));
+                assertTrue(dbSession.get(MongoSessionDataStore.__VALID).equals(false));
             }
             else if ("test".equals(action))
             {
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java
index 16ed847..a4e37eb 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java
@@ -34,9 +34,8 @@
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.nosql.mongodb.MongoTestServer.TestMongoSessionIdManager;
 import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.junit.Test;
+import org.junit.Ignore;
 
 import com.mongodb.BasicDBObject;
 import com.mongodb.DBCollection;
@@ -64,7 +63,7 @@
 
 
 
-    @Test
+    @Ignore
     public void testPurgeValidSession() throws Exception
     {
         String contextPath = "";
@@ -79,11 +78,11 @@
 
         MongoSessionManager sessionManager = (MongoSessionManager)context.getSessionHandler().getSessionManager();
         MongoSessionIdManager idManager = (MongoSessionIdManager)server.getServer().getSessionIdManager();
-        idManager.setPurge(true);
+    /*    idManager.setPurge(true);
         idManager.setPurgeDelay(purgeDelay); 
 
         idManager.setPurgeValidAge(purgeValidAge); //purge valid sessions older than
-
+*/
 
 
         server.start();
@@ -124,7 +123,7 @@
     }
 
 
-    @Test
+    @Ignore
     public void testPurgeValidSessionWithPurgeLimitSet() throws Exception
     {
         String contextPath = "";
@@ -141,18 +140,18 @@
         MongoSessionManager sessionManager = (MongoSessionManager)context.getSessionHandler().getSessionManager();
         MongoSessionIdManager idManager = (MongoSessionIdManager)server.getServer().getSessionIdManager();
         // disable purging we will run it manually below
-        idManager.setPurge(false);
+       /* idManager.setPurge(false);
         idManager.setPurgeLimit(purgeLimit);
         idManager.setPurgeDelay(purgeDelay);
         idManager.setPurgeValidAge(purgeValidAge); //purge valid sessions older than
-
+*/
         server.start();
         int port=server.getPort();
 
         try
         {
             // start with no sessions
-            ((TestMongoSessionIdManager)idManager).deleteAll();
+            //((TestMongoSessionIdManager)idManager).deleteAll();
 
             HttpClient client = new HttpClient();
             client.start();
@@ -176,7 +175,7 @@
                 assertEquals("Expected to find right number of sessions before purge", purgeLimit * 2, sessionManager.getSessionStoreCount());
 
                 // run our purge
-                idManager.purge();
+             /*   idManager.purge();*/
 
                 assertEquals("Expected to find sessions remaining in db after purge run with limit set",
                         purgeLimit, sessionManager.getSessionStoreCount());
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ReentrantRequestSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ReentrantRequestSessionTest.java
index af7a885..ea8fc09 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ReentrantRequestSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ReentrantRequestSessionTest.java
@@ -20,6 +20,8 @@
 
 import org.eclipse.jetty.server.session.AbstractReentrantRequestSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 /**
@@ -27,6 +29,21 @@
  */
 public class ReentrantRequestSessionTest extends AbstractReentrantRequestSessionTest
 {
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
+    
     public AbstractTestServer createServer(int port)
     {
         return new MongoTestServer(port);
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/RemoveSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/RemoveSessionTest.java
index 670aeb7..07167d1 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/RemoveSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/RemoveSessionTest.java
@@ -20,11 +20,27 @@
 
 import org.eclipse.jetty.server.session.AbstractRemoveSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class RemoveSessionTest extends AbstractRemoveSessionTest
 { 
 
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
         return new MongoTestServer(port,max,scavenge);
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ScatterGunLoadTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ScatterGunLoadTest.java
index 107d046..4389c2a 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ScatterGunLoadTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ScatterGunLoadTest.java
@@ -20,6 +20,8 @@
 
 import org.eclipse.jetty.server.session.AbstractScatterGunLoadTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 /**
@@ -27,6 +29,20 @@
  */
 public class ScatterGunLoadTest extends AbstractScatterGunLoadTest
 {
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
 
     public AbstractTestServer createServer(int port)
     {
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ServerCrossContextSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ServerCrossContextSessionTest.java
index dab0a75..12a336b 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ServerCrossContextSessionTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/ServerCrossContextSessionTest.java
@@ -20,11 +20,28 @@
 
 import org.eclipse.jetty.server.session.AbstractServerCrossContextSessionTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 
 public class ServerCrossContextSessionTest extends AbstractServerCrossContextSessionTest
 {
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
+    
     public AbstractTestServer createServer(int port)
     {
         return new MongoTestServer(port);
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionExpiryTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionExpiryTest.java
index ae7a897..ed32667 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionExpiryTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionExpiryTest.java
@@ -20,11 +20,27 @@
 
 import org.eclipse.jetty.server.session.AbstractSessionExpiryTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class SessionExpiryTest extends AbstractSessionExpiryTest
 {
 
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
     @Override
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionInvalidateAndCreateTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionInvalidateAndCreateTest.java
index 111b3ac..52d283a 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionInvalidateAndCreateTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionInvalidateAndCreateTest.java
@@ -20,11 +20,27 @@
 
 import org.eclipse.jetty.server.session.AbstractSessionInvalidateAndCreateTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class SessionInvalidateAndCreateTest extends AbstractSessionInvalidateAndCreateTest
 {
 
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
     @Override
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionMigrationTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionMigrationTest.java
index 73975f7..a0ac5a6 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionMigrationTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionMigrationTest.java
@@ -20,11 +20,27 @@
 
 import org.eclipse.jetty.server.session.AbstractSessionMigrationTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class SessionMigrationTest extends AbstractSessionMigrationTest
 {
 
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
     @Override
     public AbstractTestServer createServer(int port)
     {
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionRenewTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionRenewTest.java
index ce83d63..4672fad 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionRenewTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionRenewTest.java
@@ -20,11 +20,27 @@
 
 import org.eclipse.jetty.server.session.AbstractSessionRenewTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class SessionRenewTest extends AbstractSessionRenewTest
 {
 
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
+    
     @Override
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java
index 3744498..83ac35e 100644
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java
+++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java
@@ -18,227 +18,42 @@
 
 package org.eclipse.jetty.nosql.mongodb;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.Serializable;
-import java.lang.management.ManagementFactory;
-import java.net.MalformedURLException;
-
-import javax.management.remote.JMXServiceURL;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.jmx.ConnectorServer;
-import org.eclipse.jetty.jmx.MBeanContainer;
-import org.eclipse.jetty.nosql.NoSqlSession;
 import org.eclipse.jetty.server.session.AbstractSessionValueSavingTest;
 import org.eclipse.jetty.server.session.AbstractTestServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class SessionSavingValueTest extends AbstractSessionValueSavingTest
 {
 
     
+    @BeforeClass
+    public static void beforeClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+        MongoTestServer.createCollection();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception
+    {
+        MongoTestServer.dropCollection();
+    }
     
+    @Override
     public AbstractTestServer createServer(int port, int max, int scavenge)
     {
-        ConnectorServer srv = null;
-        try
-        {
-            srv = new ConnectorServer(
-                    new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:0/jettytest"),
-                    "org.eclipse.jetty:name=rmiconnectorserver");
-            srv.start();
-            
-            MongoTestServer server = new MongoTestServer(port,max,scavenge,true);
-
-            MBeanContainer mbean = new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
-           
-            //server.getServer().getContainer().addEventListener(mbean);
-            server.getServer().addBean(mbean);
-
-            //mbean.start();
-                    
-            return server;
-
-        }
-        catch (MalformedURLException e)
-        {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
-        catch (Exception e)
-        {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
-        
-        return null;
+        return new MongoTestServer(port, max, scavenge);
     }
 
     @Test
-    //@Ignore ("requires mongodb server")
     public void testSessionValueSaving() throws Exception
     {
-        String contextPath = "";
-        String servletMapping = "/server";
-        int maxInactivePeriod = 10000;
-        int scavengePeriod = 20000;
-        AbstractTestServer server1 = createServer(0,maxInactivePeriod,scavengePeriod);
-        server1.addContext(contextPath).addServlet(TestServlet.class,servletMapping);
-        server1.start();
-        int port1 = server1.getPort();
-        try
-        {
-
-            HttpClient client = new HttpClient();
-            client.start();
-            try
-            {
-                String[] sessionTestValue = new String[]
-                { "0", "null" };
-
-                // Perform one request to server1 to create a session
-                ContentResponse response = client.GET("http://localhost:" + port1 + contextPath + servletMapping + "?action=init");
-      
-                assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-
-                String[] sessionTestResponse = response.getContentAsString().split("/");
-                assertTrue(Long.parseLong(sessionTestValue[0]) < Long.parseLong(sessionTestResponse[0]));
-
-                sessionTestValue = sessionTestResponse;
-
-                String sessionCookie = response.getHeaders().get("Set-Cookie");
-                assertTrue(sessionCookie != null);
-                // Mangle the cookie, replacing Path with $Path, etc.
-                sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=","$1\\$Path=");
-
-                // Perform some request to server2 using the session cookie from the previous request
-                // This should migrate the session from server1 to server2, and leave server1's
-                // session in a very stale state, while server2 has a very fresh session.
-                // We want to test that optimizations done to the saving of the shared lastAccessTime
-                // do not break the correct working
-                int requestInterval = 500;
-
-                for (int i = 0; i < 10; ++i)
-                {
-                    Request request2 = client.newRequest("http://localhost:" + port1 + contextPath + servletMapping);
-                    request2.header("Cookie",sessionCookie);
-                    ContentResponse response2 = request2.send();
-         
-                    assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
-
-                    sessionTestResponse = response2.getContentAsString().split("/");
-
-                    assertTrue(Long.parseLong(sessionTestValue[0]) < Long.parseLong(sessionTestResponse[0]));
-                    assertTrue(Long.parseLong(sessionTestValue[1]) < Long.parseLong(sessionTestResponse[1]));
-
-                    sessionTestValue = sessionTestResponse;
-
-                    String setCookie = response2.getHeaders().get("Set-Cookie");
-                    if (setCookie != null)
-                        sessionCookie = setCookie.replaceFirst("(\\W)(P|p)ath=","$1\\$Path=");
-
-                    Thread.sleep(requestInterval);
-                }
-
- //               Thread.sleep(320000);
-            }
-            finally
-            {
-                client.stop();
-            }
-        }
-        finally
-        {
-            server1.stop();
-        }
+        super.testSessionValueSaving();
     }
 
-    public static class TestServlet extends HttpServlet
-    {
-        @Override
-        protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
-        {
-            String action = request.getParameter("action");
-            if ("init".equals(action))
-            {
-                NoSqlSession session = (NoSqlSession)request.getSession(true);
-                session.setAttribute("test",System.currentTimeMillis());
-                session.setAttribute("objectTest", new Pojo("foo","bar"));
-                
-                sendResult(session,httpServletResponse.getWriter());
-                
-            }
-            else
-            {
-                NoSqlSession session = (NoSqlSession)request.getSession(false);
-                if (session != null)
-                {
-                    long value = System.currentTimeMillis();
-                    session.setAttribute("test",value);
-
-                }
-
-                sendResult(session,httpServletResponse.getWriter());
-
-                Pojo p = (Pojo)session.getAttribute("objectTest");
-                
-                //System.out.println(p.getName() + " / " + p.getValue() );
-            }
-
-        }
-
-        private void sendResult(NoSqlSession session, PrintWriter writer)
-        {
-            if (session != null)
-            {
-                if (session.getVersion() == null)
-                {
-                    writer.print(session.getAttribute("test") + "/-1");
-                }
-                else
-                {
-                    writer.print(session.getAttribute("test") + "/" + session.getVersion());
-                }
-            }
-            else
-            {
-                writer.print("0/-1");
-            }
-        }
-        
-        public class Pojo implements Serializable
-        {
-            private String _name;
-            private String _value;
-            
-            public Pojo( String name, String value )
-            {
-                _name = name;
-                _value = value;
-            }
-            
-            public String getName()
-            {
-                return _name;
-            }
-            
-            public String getValue()
-            {
-                return _value;
-            }
-        }
-        
-    }
-
+    
+  
  
 }
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java
deleted file mode 100644
index 37a91c6..0000000
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java
+++ /dev/null
@@ -1,164 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2016 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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.IOException;
-import java.net.UnknownHostException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.junit.Test;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBCollection;
-import com.mongodb.DBObject;
-import com.mongodb.Mongo;
-import com.mongodb.MongoException;
-
-public class StopSessionManagerDeleteSessionTest
-{
-    public MongoTestServer createServer(int port, int max, int scavenge)
-    {
-        MongoTestServer server =  new MongoTestServer(port,max,scavenge);
-       
-        return server;
-    }
-    
-    /**
-     * @throws Exception
-     */
-    @Test
-    public void testStopSessionManagerDeleteSession() throws Exception
-    {
-        String contextPath = "";
-        String servletMapping = "/server";
-        
-        MongoTestServer server = createServer(0, 1, 0);
-        ServletContextHandler context = server.addContext(contextPath);
-        ServletHolder holder = new ServletHolder();
-        TestServlet servlet = new TestServlet();
-        holder.setServlet(servlet);
-        
-        context.addServlet(holder, servletMapping);
-        
-        MongoSessionManager sessionManager = (MongoSessionManager)context.getSessionHandler().getSessionManager();
-        sessionManager.setPreserveOnStop(false);
-        MongoSessionIdManager idManager = (MongoSessionIdManager)server.getServer().getSessionIdManager();
-        idManager.setPurge(true);
-
-        
-        server.start();
-        int port=server.getPort();
-        try
-        {
-            HttpClient client = new HttpClient();
-            client.start();
-            try
-            {
-                //Create a session
-                ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create");
-                assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                String sessionCookie = response.getHeaders().get("Set-Cookie");
-                assertTrue(sessionCookie != null);
-                // Mangle the cookie, replacing Path with $Path, etc.
-                sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
-
-                //stop the session manager
-                sessionManager.stop();
-                
-                //check the database to see that the session has been marked invalid
-                servlet.checkSessionInDB(false);
-                
-            }
-            finally
-            {
-                client.stop();
-            }
-        }
-        finally
-        {
-            server.stop();
-        }
-    }
-    
-    
-    public static class TestServlet extends HttpServlet
-    {
-        DBCollection _sessions;
-        String _id;
-
-        public TestServlet() throws UnknownHostException, MongoException
-        {
-            super();            
-            _sessions = new Mongo().getDB("HttpSessions").getCollection("sessions");
-        }
-
-        public void checkSessionInDB (boolean expectedValid)
-        {
-            DBObject dbSession = _sessions.findOne(new BasicDBObject("id", _id));
-            assertTrue(dbSession != null);
-            assertEquals(expectedValid, dbSession.get("valid"));
-            if (!expectedValid)
-                assertNotNull(dbSession.get(MongoSessionManager.__INVALIDATED));
-        }
-
-        public String getId()
-        {
-            return _id;
-        }
-        
-        @Override
-        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
-        {
-            String action = request.getParameter("action");
-            if ("create".equals(action))
-            {
-                HttpSession session = request.getSession(true);
-                session.setAttribute("foo", "bar");
-                assertTrue(session.isNew());
-                _id = session.getId();
-            }
-            else if ("test".equals(action))
-            {
-                String id = request.getRequestedSessionId();
-                assertNotNull(id);
-                id = id.substring(0, id.indexOf("."));
-  
-                HttpSession existingSession = request.getSession(false);
-                assertTrue(existingSession == null);
-                
-                //not in db any more
-                DBObject dbSession = _sessions.findOne(new BasicDBObject("id", id));
-                assertTrue(dbSession == null);
-            }
-        }
-    }
-}
diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerPreserveSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerPreserveSessionTest.java
deleted file mode 100644
index 57ff465..0000000
--- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerPreserveSessionTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-//
-//  ========================================================================
-//  Copyright (c) 1995-2016 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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.net.UnknownHostException;
-
-import org.eclipse.jetty.server.session.AbstractStopSessionManagerPreserveSessionTest;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.DBCollection;
-import com.mongodb.DBObject;
-import com.mongodb.Mongo;
-import com.mongodb.MongoException;
-
-/**
- * StopSessionManagerPreserveSessionTest
- *
- *
- */
-public class StopSessionManagerPreserveSessionTest extends AbstractStopSessionManagerPreserveSessionTest
-{
-    DBCollection _sessions;
-    
-    @Before
-    public void setUp() throws UnknownHostException, MongoException
-    {
-        _sessions = new Mongo().getDB("HttpSessions").getCollection("sessions");
-    }
-    
-   
-    
-    public MongoTestServer createServer(int port)
-    {
-        MongoTestServer server =  new MongoTestServer(port); 
-        server.getServer().setStopTimeout(0);
-        return server;
-    }
-    
-    
-
-    @Override
-    public void checkSessionPersisted(boolean expected)
-    {
-        DBObject dbSession = _sessions.findOne(new BasicDBObject("id", _id));
-
-        if (expected)
-        {
-            assertTrue(dbSession != null);
-            assertEquals(expected, dbSession.get("valid"));
-        }
-        else
-        {
-            assertTrue(dbSession==null);
-        }
-    }
-
-
-    @Override
-    public void configureSessionManagement(ServletContextHandler context)
-    {
-        ((MongoSessionManager)context.getSessionHandler().getSessionManager()).setPreserveOnStop(true);
-    }
-
-    /**
-     * @throws Exception
-     */
-    @Test
-    public void testStopSessionManagerPreserveSession() throws Exception
-    {
-        super.testStopSessionManagerPreserveSession();
-    }
-
-
-
-}
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractForwardedSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractForwardedSessionTest.java
index cf50507..da238ee 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractForwardedSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractForwardedSessionTest.java
@@ -126,6 +126,7 @@
    
             HttpSession sess = request.getSession(false);
             assertNotNull(sess);
+            assertNotNull(sess.getAttribute("servlet3"));
             sess.setAttribute("servlet1", "servlet1");
         }
     }
@@ -144,6 +145,7 @@
             //the session should exist after the forward
             HttpSession sess = request.getSession(false);
             assertNotNull(sess);
+            assertNotNull(sess.getAttribute("servlet3"));
         }
     }
 
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java
index 0d004e8..4bfb047 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java
@@ -33,6 +33,7 @@
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.junit.Test;
 
 
@@ -51,7 +52,8 @@
         int scavengePeriod = 2;
         //turn off session expiry by setting maxInactiveInterval to -1
         AbstractTestServer server = createServer(0, -1, scavengePeriod);
-        server.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
+        ServletContextHandler context = server.addContext(contextPath);
+        context.addServlet(TestServlet.class, servletMapping);
 
         try
         {
@@ -74,7 +76,7 @@
 
                 // Let's wait for the scavenger to run, waiting 2.5 times the scavenger period
                 Thread.sleep(scavengePeriod * 2500L);
-
+                
                 // Be sure the session is still there
                 Request request = client.newRequest("http://localhost:" + port + contextPath + servletMapping + "?action=get");
                 request.header("Cookie", sessionCookie);
@@ -82,6 +84,8 @@
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
                 resp = response.getContentAsString();
                 assertEquals(String.valueOf(value),resp.trim());
+                
+                assertEquals(1, ((org.eclipse.jetty.server.session.SessionManager)context.getSessionHandler().getSessionManager()).getSessionsCreated());
             }
             finally
             {
@@ -110,7 +114,7 @@
             }
             else if ("get".equals(action))
             {
-                HttpSession session = request.getSession(false);
+                HttpSession session = request.getSession(false);               
                 if (session!=null)
                     result = (String)session.getAttribute("value");
             }
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java
index ddf7596..0d5298d 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.IOException;
 
@@ -32,7 +33,6 @@
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.junit.Test;
 
@@ -52,20 +52,16 @@
         String contextPath = "";
         String servletMapping = "/server";
         AbstractTestServer server1 = createServer(0);
-        ServletContextHandler context1 = server1.addContext(contextPath);
-        context1.addServlet(TestServlet.class, servletMapping);
+        server1.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
 
-        AbstractSessionManager m1 = (AbstractSessionManager) context1.getSessionHandler().getSessionManager();
 
         try
         {
             server1.start();
             int port1 = server1.getPort();
             AbstractTestServer server2 = createServer(0);
-            ServletContextHandler context2 = server2.addContext(contextPath);
-            context2.addServlet(TestServlet.class, servletMapping);
-            AbstractSessionManager m2 = (AbstractSessionManager) context2.getSessionHandler().getSessionManager();
-            
+            server2.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
+
             try
             {
                 server2.start();
@@ -74,6 +70,7 @@
                 QueuedThreadPool executor = new QueuedThreadPool();
                 client.setExecutor(executor);
                 client.start();
+                
                 try
                 {
                     String[] urls = new String[2];
@@ -86,33 +83,21 @@
                     assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
                     String sessionCookie = response1.getHeaders().get("Set-Cookie");
                     assertTrue(sessionCookie != null);
-                    assertEquals(1, m1.getSessions());
-                    assertEquals(1, m1.getSessionsMax());
-                    assertEquals(1, m1.getSessionsTotal());
-                    
                     // Mangle the cookie, replacing Path with $Path, etc.
                     sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
-                    
-                    
+
                     // Be sure the session is also present in node2
                     Request request2 = client.newRequest(urls[1] + "?action=increment");
                     request2.header("Cookie", sessionCookie);
                     ContentResponse response2 = request2.send();
                     assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
-                    assertEquals(1, m2.getSessions());
-                    assertEquals(1, m2.getSessionsMax());
-                    assertEquals(1, m2.getSessionsTotal());
-                    
 
                     // Invalidate on node1
                     Request request1 = client.newRequest(urls[0] + "?action=invalidate");
                     request1.header("Cookie", sessionCookie);
                     response1 = request1.send();
                     assertEquals(HttpServletResponse.SC_OK, response1.getStatus());
-                    assertEquals(0, m1.getSessions());
-                    assertEquals(1, m1.getSessionsMax());
-                    assertEquals(1, m1.getSessionsTotal());
-                   
+
                     pause();
 
                     // Be sure on node2 we don't see the session anymore
@@ -120,9 +105,6 @@
                     request2.header("Cookie", sessionCookie);
                     response2 = request2.send();
                     assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
-                    assertEquals(0, m2.getSessions());
-                    assertEquals(1, m2.getSessionsMax());
-                    assertEquals(1, m2.getSessionsTotal());
                 }
                 finally
                 {
@@ -161,6 +143,18 @@
             {
                 HttpSession session = request.getSession(false);
                 session.invalidate();
+                
+                try
+                {
+                    session.invalidate();
+                    fail("Session should be invalid");
+                    
+                }
+                catch (IllegalStateException e)
+                {
+                    //expected
+                }
+                
             }
             else if ("test".equals(action))
             {
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java
index 099e804..e0d83e5 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java
@@ -24,6 +24,8 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
@@ -51,7 +53,10 @@
  */
 public abstract class AbstractLastAccessTimeTest
 {
+    
     public abstract AbstractTestServer createServer(int port, int max, int scavenge);
+ 
+   
 
     @Test
     public void testLastAccessTime() throws Exception
@@ -67,7 +72,7 @@
         TestSessionListener listener1 = new TestSessionListener();
         context.getSessionHandler().addEventListener(listener1);
         context.addServlet(holder1, servletMapping);
-        AbstractSessionManager m1 = (AbstractSessionManager)context.getSessionHandler().getSessionManager();
+        SessionManager m1 = (SessionManager)context.getSessionHandler().getSessionManager();
         
 
         try
@@ -77,7 +82,7 @@
             AbstractTestServer server2 = createServer(0, maxInactivePeriod, scavengePeriod);
             ServletContextHandler context2 = server2.addContext(contextPath);
             context2.addServlet(TestServlet.class, servletMapping);
-            AbstractSessionManager m2 = (AbstractSessionManager)context2.getSessionHandler().getSessionManager();
+            SessionManager m2 = (SessionManager)context2.getSessionHandler().getSessionManager();
 
             try
             {
@@ -93,9 +98,9 @@
                     assertEquals("test", response1.getContentAsString());
                     String sessionCookie = response1.getHeaders().get("Set-Cookie");
                     assertTrue( sessionCookie != null );
-                    assertEquals(1, m1.getSessions());
-                    assertEquals(1, m1.getSessionsMax());
-                    assertEquals(1, m1.getSessionsTotal());
+                    assertEquals(1, ((MemorySessionStore)m1.getSessionStore()).getSessions());
+                    assertEquals(1, ((MemorySessionStore)m1.getSessionStore()).getSessionsMax());
+                    assertEquals(1, ((MemorySessionStore)m1.getSessionStore()).getSessionsTotal());
                     // Mangle the cookie, replacing Path with $Path, etc.
                     sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");        
                     
@@ -120,12 +125,13 @@
                         Thread.sleep(requestInterval);
                         assertSessionCounts(1,1,1, m2);
                     }
+                    
                     // At this point, session1 should be eligible for expiration.
                     // Let's wait for the scavenger to run, waiting 2.5 times the scavenger period
                     Thread.sleep(scavengePeriod * 2500L);
 
                     //check that the session was not scavenged over on server1 by ensuring that the SessionListener destroy method wasn't called
-                    assertFalse(listener1.destroyed);
+                    assertFalse(listener1._destroys.contains(AbstractTestServer.extractSessionId(sessionCookie)));
                     assertAfterScavenge(m1);
                 }
                 finally
@@ -144,38 +150,38 @@
         }
     }
     
-    public void assertAfterSessionCreated (AbstractSessionManager m)
+    public void assertAfterSessionCreated (SessionManager m)
     {
         assertSessionCounts(1, 1, 1, m);
     }
     
-    public void assertAfterScavenge (AbstractSessionManager manager)
+    public void assertAfterScavenge (SessionManager manager)
     {
         assertSessionCounts(1,1,1, manager);
     }
     
-    public void assertSessionCounts (int current, int max, int total, AbstractSessionManager manager)
+    public void assertSessionCounts (int current, int max, int total, SessionManager manager)
     {
-        assertEquals(current, manager.getSessions());
-        assertEquals(max, manager.getSessionsMax());
-        assertEquals(total, manager.getSessionsTotal());
+        assertEquals(current, ((MemorySessionStore)manager.getSessionStore()).getSessions());
+        assertEquals(max, ((MemorySessionStore)manager.getSessionStore()).getSessionsMax());
+        assertEquals(total, ((MemorySessionStore)manager.getSessionStore()).getSessionsTotal());
     }
 
     public static class TestSessionListener implements HttpSessionListener
     {
-        public boolean destroyed = false;
-        public boolean created = false;
+        public Set<String> _creates = new HashSet<String>();
+        public Set<String> _destroys = new HashSet<String>();
 
         @Override
         public void sessionDestroyed(HttpSessionEvent se)
         {
-           destroyed = true;
+           _destroys.add(se.getSession().getId());
         }
 
         @Override
         public void sessionCreated(HttpSessionEvent se)
         {
-            created = true;
+            _creates.add(se.getSession().getId());
         }
     }
 
@@ -183,7 +189,10 @@
 
     public static class TestServlet extends HttpServlet
     {
-
+        /**
+         * 
+         */
+        private static final long serialVersionUID = 1L;
 
         @Override
         protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
@@ -212,14 +221,14 @@
 
         private void sendResult(HttpSession session, PrintWriter writer)
         {
-                if (session != null)
-                {
-                        writer.print(session.getAttribute("test"));
-                }
-                else
-                {
-                        writer.print("null");
-                }
+            if (session != null)
+            {
+                writer.print(session.getAttribute("test"));
+            }
+            else
+            {
+                writer.print("null");
+            }
         }
     }
 }
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java
index 6c6a79e..e84342e 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java
@@ -33,6 +33,7 @@
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.junit.Test;
 
 /**
@@ -62,14 +63,16 @@
         int inactivePeriod = 1;
         int scavengePeriod = 2;
         AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod);
-        server1.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
+        ServletContextHandler context1 = server1.addContext(contextPath);
+        context1.addServlet(TestServlet.class, servletMapping);
 
         try
         {
             server1.start();
             int port1 = server1.getPort();
             AbstractTestServer server2 = createServer(0, inactivePeriod, scavengePeriod * 3);
-            server2.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
+            ServletContextHandler context2 = server2.addContext(contextPath);
+            context2.addServlet(TestServlet.class, servletMapping);
 
             try
             {
@@ -90,28 +93,33 @@
                     assertTrue(sessionCookie != null);
                     // Mangle the cookie, replacing Path with $Path, etc.
                     sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+                    org.eclipse.jetty.server.session.SessionManager m1 = (org.eclipse.jetty.server.session.SessionManager)context1.getSessionHandler().getSessionManager();
+                    assertEquals(1,  m1.getSessionsCreated());
 
                     // Be sure the session is also present in node2
                     org.eclipse.jetty.client.api.Request request = client.newRequest(urls[1] + "?action=test");
                     request.header("Cookie", sessionCookie);
                     ContentResponse response2 = request.send();
                     assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
-
+                    org.eclipse.jetty.server.session.SessionManager m2 = (org.eclipse.jetty.server.session.SessionManager)context2.getSessionHandler().getSessionManager();
 
                     // Wait for the scavenger to run on node1, waiting 2.5 times the scavenger period
                     pause(scavengePeriod);
 
+                    assertEquals(1,  m1.getSessionsCreated());
+
                     // Check that node1 does not have any local session cached
                     request = client.newRequest(urls[0] + "?action=check");
                     request.header("Cookie", sessionCookie);
                     response1 = request.send();
                     assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
-
+                    
+                    assertEquals(1,  m1.getSessionsCreated());
 
                     // Wait for the scavenger to run on node2, waiting 2 times the scavenger period
                     // This ensures that the scavenger on node2 runs at least once.
                     pause(scavengePeriod);
-
+                    
                     // Check that node2 does not have any local session cached
                     request = client.newRequest(urls[1] + "?action=check");
                     request.header("Cookie", sessionCookie);
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java
index dc6a2f2..ee02ab1 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java
@@ -61,7 +61,7 @@
         context.addServlet(TestServlet.class, servletMapping);
         TestEventListener testListener = new TestEventListener();
         context.getSessionHandler().addEventListener(testListener);
-        AbstractSessionManager m = (AbstractSessionManager)context.getSessionHandler().getSessionManager();
+        SessionManager m = (SessionManager) context.getSessionHandler().getSessionManager();
         try
         {
             server.start();
@@ -79,9 +79,9 @@
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
                 //ensure sessionCreated listener is called
                 assertTrue (testListener.isCreated());
-                assertEquals(1, m.getSessions());
-                assertEquals(1, m.getSessionsMax());
-                assertEquals(1, m.getSessionsTotal());
+                assertEquals(1, m.getSessionsCreated());
+                assertEquals(1, ((MemorySessionStore)m.getSessionStore()).getSessionsMax());
+                assertEquals(1, ((MemorySessionStore)m.getSessionStore()).getSessionsTotal());
                 
                 //now delete the session
                 Request request = client.newRequest("http://localhost:" + port + contextPath + servletMapping + "?action=delete");
@@ -90,18 +90,18 @@
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
                 //ensure sessionDestroyed listener is called
                 assertTrue(testListener.isDestroyed());
-                assertEquals(0, m.getSessions());
-                assertEquals(1, m.getSessionsMax());
-                assertEquals(1, m.getSessionsTotal());
+                assertEquals(0, ((MemorySessionStore)m.getSessionStore()).getSessions());
+                assertEquals(1, ((MemorySessionStore)m.getSessionStore()).getSessionsMax());
+                assertEquals(1, ((MemorySessionStore)m.getSessionStore()).getSessionsTotal());
 
                 // The session is not there anymore, even if we present an old cookie
                 request = client.newRequest("http://localhost:" + port + contextPath + servletMapping + "?action=check");
                 request.header("Cookie", sessionCookie);
                 response = request.send();
                 assertEquals(HttpServletResponse.SC_OK,response.getStatus());
-                assertEquals(0, m.getSessions());
-                assertEquals(1, m.getSessionsMax());
-                assertEquals(1, m.getSessionsTotal());
+                assertEquals(0, ((MemorySessionStore)m.getSessionStore()).getSessions());
+                assertEquals(1,  ((MemorySessionStore)m.getSessionStore()).getSessionsMax());
+                assertEquals(1, ((MemorySessionStore)m.getSessionStore()).getSessionsTotal());
             }
             finally
             {
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java
index a3bd827..643639f 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java
@@ -181,7 +181,7 @@
             
             port1 = server1.getPort();
             url = "http://localhost:" + port1 + contextPath + servletMapping;
-
+            
             //make another request, the session should have expired
             Request request = client.newRequest(url + "?action=test");
             request.getHeaders().add("Cookie", sessionCookie);
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java
index 4c3a06f..e3e9af6 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java
@@ -123,13 +123,12 @@
                 // Mangle the cookie, replacing Path with $Path, etc.
                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
 
-
                 // Make a request which will invalidate the existing session and create a new one
                 Request request2 = client.newRequest(url + "?action=test");
                 request2.header("Cookie", sessionCookie);
                 ContentResponse response2 = request2.send();
                 assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
-
+         
                 // Wait for the scavenger to run, waiting 3 times the scavenger period
                 pause(scavengePeriod);
 
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java
index d4ef627..b92e588 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java
@@ -79,6 +79,7 @@
             Request request = client.newRequest("http://localhost:" + port + contextPath + servletMapping + "?action=renew");
             request.header("Cookie", sessionCookie);
             ContentResponse renewResponse = request.send();
+
             assertEquals(HttpServletResponse.SC_OK,renewResponse.getStatus());
             String renewSessionCookie = renewResponse.getHeaders().get("Set-Cookie");
             assertNotNull(renewSessionCookie);
@@ -131,8 +132,7 @@
                 assertTrue(beforeSession != null);
                 String beforeSessionId = beforeSession.getId();
 
-
-                ((AbstractSession)beforeSession).renewId(request);
+                ((Session)beforeSession).renewId(request);
 
                 HttpSession afterSession = request.getSession(false);
                 assertTrue(afterSession != null);
@@ -141,18 +141,18 @@
                 assertTrue(beforeSession==afterSession);
                 assertFalse(beforeSessionId.equals(afterSessionId));
 
-                AbstractSessionManager sessionManager = (AbstractSessionManager)((AbstractSession)afterSession).getSessionManager();
+                SessionManager sessionManager = ((Session)afterSession).getSessionManager();
                 AbstractSessionIdManager sessionIdManager = (AbstractSessionIdManager)sessionManager.getSessionIdManager();
 
-                assertTrue(sessionIdManager.idInUse(afterSessionId));
-                assertFalse(sessionIdManager.idInUse(beforeSessionId));
+                assertTrue(sessionIdManager.isIdInUse(afterSessionId));
+                assertFalse(sessionIdManager.isIdInUse(beforeSessionId));
 
                 HttpSession session = sessionManager.getSession(afterSessionId);
                 assertNotNull(session);
                 session = sessionManager.getSession(beforeSessionId);
                 assertNull(session);
 
-                if (((AbstractSession)afterSession).isIdChanged())
+                if (((Session)afterSession).isIdChanged())
                 {
                     ((org.eclipse.jetty.server.Response)response).addCookie(sessionManager.getSessionCookie(afterSession, request.getContextPath(), request.isSecure()));
                 }
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java
index da4b976..eace391 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java
@@ -77,9 +77,6 @@
                     // Mangle the cookie, replacing Path with $Path, etc.
                     sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
 
-                    // Perform some request to server2 using the session cookie from the previous request
-                    // This should migrate the session from server1 to server2, and leave server1's
-                    // session in a very stale state, while server2 has a very fresh session.
                     // We want to test that optimizations done to the saving of the shared lastAccessTime
                     // do not break the correct working
                     int requestInterval = 500;
@@ -130,13 +127,10 @@
             else
             {
                 HttpSession session = request.getSession(false);
-                System.out.println("not init call " + session);
                 if (session!=null)
                 {
-                        long value = System.currentTimeMillis();
-                        System.out.println("Setting test to : " + value);
+                    long value = System.currentTimeMillis();
                     session.setAttribute("test", value);
-
                 }
 
                 sendResult(session, httpServletResponse.getWriter());
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java
index 8806012..04e8bb9 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java
@@ -42,6 +42,7 @@
     protected final int _scavengePeriod;
     protected final ContextHandlerCollection _contexts;
     protected SessionIdManager _sessionIdManager;
+    private SessionScavenger _scavenger;
 
   
     
@@ -81,6 +82,10 @@
         _contexts = new ContextHandlerCollection();
         _sessionIdManager = newSessionIdManager(sessionIdMgrConfig);
         _server.setSessionIdManager(_sessionIdManager);
+        ((AbstractSessionIdManager) _sessionIdManager).setServer(_server);
+        _scavenger = new SessionScavenger();
+        _scavenger.setScavengeIntervalSec(scavengePeriod);
+        ((AbstractSessionIdManager)_sessionIdManager).setSessionScavenger(_scavenger);
     }
     
     
diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/WebAppObjectInSessionServlet.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/WebAppObjectInSessionServlet.java
index 7c3b485..88b4a3b 100644
--- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/WebAppObjectInSessionServlet.java
+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/WebAppObjectInSessionServlet.java
@@ -62,7 +62,6 @@
             {
                 HttpSession session = request.getSession(false);
                 Object staticAttribute = session.getAttribute("staticAttribute");
-                System.err.println("staticAttribute="+staticAttribute);
                 Assert.assertTrue(staticAttribute instanceof TestSharedStatic);
                 
 //                Object objectAttribute = session.getAttribute("objectAttribute");
diff --git a/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java b/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java
index d8b8243..a96c2d9 100644
--- a/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java
+++ b/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java
@@ -44,7 +44,7 @@
 import org.eclipse.jetty.server.handler.HandlerWrapper;
 import org.eclipse.jetty.server.handler.RequestLogHandler;
 import org.eclipse.jetty.server.handler.ResourceHandler;
-import org.eclipse.jetty.server.session.HashSessionManager;
+import org.eclipse.jetty.server.session.FileSessionManager;
 import org.eclipse.jetty.util.log.Log;
 import org.eclipse.jetty.util.log.Logger;
 import org.eclipse.jetty.util.log.StdErrLog;
@@ -131,8 +131,8 @@
             sessiondir.delete();
         sessiondir.mkdir();
         sessiondir.deleteOnExit();
-        ((HashSessionManager)webapp.getSessionHandler().getSessionManager()).setStoreDirectory(sessiondir);
-        ((HashSessionManager)webapp.getSessionHandler().getSessionManager()).setSavePeriod(10);
+        ((FileSessionManager)webapp.getSessionHandler().getSessionManager()).getSessionDataStore().setStoreDir(sessiondir);
+        //((HashSessionManager)webapp.getSessionHandler().getSessionManager()).setSavePeriod(10);
 
         contexts.addHandler(webapp);