103928aee4356845252ac6b662d5c72c29903813eJake Slack//
203928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
303928aee4356845252ac6b662d5c72c29903813eJake Slack//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
403928aee4356845252ac6b662d5c72c29903813eJake Slack//  ------------------------------------------------------------------------
503928aee4356845252ac6b662d5c72c29903813eJake Slack//  All rights reserved. This program and the accompanying materials
603928aee4356845252ac6b662d5c72c29903813eJake Slack//  are made available under the terms of the Eclipse Public License v1.0
703928aee4356845252ac6b662d5c72c29903813eJake Slack//  and Apache License v2.0 which accompanies this distribution.
803928aee4356845252ac6b662d5c72c29903813eJake Slack//
903928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Eclipse Public License is available at
1003928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.eclipse.org/legal/epl-v10.html
1103928aee4356845252ac6b662d5c72c29903813eJake Slack//
1203928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Apache License v2.0 is available at
1303928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.opensource.org/licenses/apache2.0.php
1403928aee4356845252ac6b662d5c72c29903813eJake Slack//
1503928aee4356845252ac6b662d5c72c29903813eJake Slack//  You may elect to redistribute this code under either of these licenses.
1603928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
1703928aee4356845252ac6b662d5c72c29903813eJake Slack//
1803928aee4356845252ac6b662d5c72c29903813eJake Slack
1903928aee4356845252ac6b662d5c72c29903813eJake Slack
2003928aee4356845252ac6b662d5c72c29903813eJake Slackpackage org.eclipse.jetty.server.session;
2103928aee4356845252ac6b662d5c72c29903813eJake Slack
2203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.ByteArrayInputStream;
2303928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.ByteArrayOutputStream;
2403928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.IOException;
2503928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.InputStream;
2603928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.ObjectInputStream;
2703928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.ObjectOutputStream;
2803928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.sql.Connection;
2903928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.sql.PreparedStatement;
3003928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.sql.ResultSet;
3103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.sql.SQLException;
3203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.sql.Statement;
3303928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.HashMap;
3403928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.List;
3503928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.ListIterator;
3603928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Map;
3703928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Set;
3803928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.concurrent.ConcurrentHashMap;
3903928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.concurrent.atomic.AtomicReference;
4003928aee4356845252ac6b662d5c72c29903813eJake Slack
4103928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.SessionTrackingMode;
4203928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.http.HttpServletRequest;
4303928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.http.HttpSessionEvent;
4403928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.http.HttpSessionListener;
4503928aee4356845252ac6b662d5c72c29903813eJake Slack
4603928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.server.SessionIdManager;
4703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.server.handler.ContextHandler;
4803928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Log;
4903928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Logger;
5003928aee4356845252ac6b662d5c72c29903813eJake Slack
5103928aee4356845252ac6b662d5c72c29903813eJake Slack/**
5203928aee4356845252ac6b662d5c72c29903813eJake Slack * JDBCSessionManager
5303928aee4356845252ac6b662d5c72c29903813eJake Slack *
5403928aee4356845252ac6b662d5c72c29903813eJake Slack * SessionManager that persists sessions to a database to enable clustering.
5503928aee4356845252ac6b662d5c72c29903813eJake Slack *
5603928aee4356845252ac6b662d5c72c29903813eJake Slack * Session data is persisted to the JettySessions table:
5703928aee4356845252ac6b662d5c72c29903813eJake Slack *
5803928aee4356845252ac6b662d5c72c29903813eJake Slack * rowId (unique in cluster: webapp name/path + virtualhost + sessionId)
5903928aee4356845252ac6b662d5c72c29903813eJake Slack * contextPath (of the context owning the session)
6003928aee4356845252ac6b662d5c72c29903813eJake Slack * sessionId (unique in a context)
6103928aee4356845252ac6b662d5c72c29903813eJake Slack * lastNode (name of node last handled session)
6203928aee4356845252ac6b662d5c72c29903813eJake Slack * accessTime (time in milliseconds session was accessed)
6303928aee4356845252ac6b662d5c72c29903813eJake Slack * lastAccessTime (previous time in milliseconds session was accessed)
6403928aee4356845252ac6b662d5c72c29903813eJake Slack * createTime (time in milliseconds session created)
6503928aee4356845252ac6b662d5c72c29903813eJake Slack * cookieTime (time in milliseconds session cookie created)
6603928aee4356845252ac6b662d5c72c29903813eJake Slack * lastSavedTime (last time in milliseconds session access times were saved)
6703928aee4356845252ac6b662d5c72c29903813eJake Slack * expiryTime (time in milliseconds that the session is due to expire)
6803928aee4356845252ac6b662d5c72c29903813eJake Slack * map (attribute map)
6903928aee4356845252ac6b662d5c72c29903813eJake Slack *
7003928aee4356845252ac6b662d5c72c29903813eJake Slack * As an optimization, to prevent thrashing the database, we do not persist
7103928aee4356845252ac6b662d5c72c29903813eJake Slack * the accessTime and lastAccessTime every time the session is accessed. Rather,
7203928aee4356845252ac6b662d5c72c29903813eJake Slack * we write it out every so often. The frequency is controlled by the saveIntervalSec
7303928aee4356845252ac6b662d5c72c29903813eJake Slack * field.
7403928aee4356845252ac6b662d5c72c29903813eJake Slack */
7503928aee4356845252ac6b662d5c72c29903813eJake Slackpublic class JDBCSessionManager extends AbstractSessionManager
7603928aee4356845252ac6b662d5c72c29903813eJake Slack{
7703928aee4356845252ac6b662d5c72c29903813eJake Slack    private static final Logger LOG = Log.getLogger(JDBCSessionManager.class);
7803928aee4356845252ac6b662d5c72c29903813eJake Slack
7903928aee4356845252ac6b662d5c72c29903813eJake Slack    private ConcurrentHashMap<String, AbstractSession> _sessions;
8003928aee4356845252ac6b662d5c72c29903813eJake Slack    protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
8103928aee4356845252ac6b662d5c72c29903813eJake Slack    protected long _saveIntervalSec = 60; //only persist changes to session access times every 60 secs
8203928aee4356845252ac6b662d5c72c29903813eJake Slack
8303928aee4356845252ac6b662d5c72c29903813eJake Slack
8403928aee4356845252ac6b662d5c72c29903813eJake Slack
8503928aee4356845252ac6b662d5c72c29903813eJake Slack
8603928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
8703928aee4356845252ac6b662d5c72c29903813eJake Slack     * Session
8803928aee4356845252ac6b662d5c72c29903813eJake Slack     *
8903928aee4356845252ac6b662d5c72c29903813eJake Slack     * Session instance.
9003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
9103928aee4356845252ac6b662d5c72c29903813eJake Slack    public class Session extends AbstractSession
9203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
9303928aee4356845252ac6b662d5c72c29903813eJake Slack        private static final long serialVersionUID = 5208464051134226143L;
9403928aee4356845252ac6b662d5c72c29903813eJake Slack
9503928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
9603928aee4356845252ac6b662d5c72c29903813eJake Slack         * If dirty, session needs to be (re)persisted
9703928aee4356845252ac6b662d5c72c29903813eJake Slack         */
9803928aee4356845252ac6b662d5c72c29903813eJake Slack        private boolean _dirty=false;
9903928aee4356845252ac6b662d5c72c29903813eJake Slack
10003928aee4356845252ac6b662d5c72c29903813eJake Slack
10103928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
10203928aee4356845252ac6b662d5c72c29903813eJake Slack         * Time in msec since the epoch that a session cookie was set for this session
10303928aee4356845252ac6b662d5c72c29903813eJake Slack         */
10403928aee4356845252ac6b662d5c72c29903813eJake Slack        private long _cookieSet;
10503928aee4356845252ac6b662d5c72c29903813eJake Slack
10603928aee4356845252ac6b662d5c72c29903813eJake Slack
10703928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
10803928aee4356845252ac6b662d5c72c29903813eJake Slack         * Time in msec since the epoch that the session will expire
10903928aee4356845252ac6b662d5c72c29903813eJake Slack         */
11003928aee4356845252ac6b662d5c72c29903813eJake Slack        private long _expiryTime;
11103928aee4356845252ac6b662d5c72c29903813eJake Slack
11203928aee4356845252ac6b662d5c72c29903813eJake Slack
11303928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
11403928aee4356845252ac6b662d5c72c29903813eJake Slack         * Time in msec since the epoch that the session was last persisted
11503928aee4356845252ac6b662d5c72c29903813eJake Slack         */
11603928aee4356845252ac6b662d5c72c29903813eJake Slack        private long _lastSaved;
11703928aee4356845252ac6b662d5c72c29903813eJake Slack
11803928aee4356845252ac6b662d5c72c29903813eJake Slack
11903928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
12003928aee4356845252ac6b662d5c72c29903813eJake Slack         * Unique identifier of the last node to host the session
12103928aee4356845252ac6b662d5c72c29903813eJake Slack         */
12203928aee4356845252ac6b662d5c72c29903813eJake Slack        private String _lastNode;
12303928aee4356845252ac6b662d5c72c29903813eJake Slack
12403928aee4356845252ac6b662d5c72c29903813eJake Slack
12503928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
12603928aee4356845252ac6b662d5c72c29903813eJake Slack         * Virtual host for context (used to help distinguish 2 sessions with same id on different contexts)
12703928aee4356845252ac6b662d5c72c29903813eJake Slack         */
12803928aee4356845252ac6b662d5c72c29903813eJake Slack        private String _virtualHost;
12903928aee4356845252ac6b662d5c72c29903813eJake Slack
13003928aee4356845252ac6b662d5c72c29903813eJake Slack
13103928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
13203928aee4356845252ac6b662d5c72c29903813eJake Slack         * Unique row in db for session
13303928aee4356845252ac6b662d5c72c29903813eJake Slack         */
13403928aee4356845252ac6b662d5c72c29903813eJake Slack        private String _rowId;
13503928aee4356845252ac6b662d5c72c29903813eJake Slack
13603928aee4356845252ac6b662d5c72c29903813eJake Slack
13703928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
13803928aee4356845252ac6b662d5c72c29903813eJake Slack         * Mangled context name (used to help distinguish 2 sessions with same id on different contexts)
13903928aee4356845252ac6b662d5c72c29903813eJake Slack         */
14003928aee4356845252ac6b662d5c72c29903813eJake Slack        private String _canonicalContext;
14103928aee4356845252ac6b662d5c72c29903813eJake Slack
14203928aee4356845252ac6b662d5c72c29903813eJake Slack
14303928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
14403928aee4356845252ac6b662d5c72c29903813eJake Slack         * Session from a request.
14503928aee4356845252ac6b662d5c72c29903813eJake Slack         *
14603928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param request
14703928aee4356845252ac6b662d5c72c29903813eJake Slack         */
14803928aee4356845252ac6b662d5c72c29903813eJake Slack        protected Session (HttpServletRequest request)
14903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
15003928aee4356845252ac6b662d5c72c29903813eJake Slack            super(JDBCSessionManager.this,request);
15103928aee4356845252ac6b662d5c72c29903813eJake Slack            int maxInterval=getMaxInactiveInterval();
15203928aee4356845252ac6b662d5c72c29903813eJake Slack            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
15303928aee4356845252ac6b662d5c72c29903813eJake Slack            _virtualHost = JDBCSessionManager.getVirtualHost(_context);
15403928aee4356845252ac6b662d5c72c29903813eJake Slack            _canonicalContext = canonicalize(_context.getContextPath());
15503928aee4356845252ac6b662d5c72c29903813eJake Slack            _lastNode = getSessionIdManager().getWorkerName();
15603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
15703928aee4356845252ac6b662d5c72c29903813eJake Slack
15803928aee4356845252ac6b662d5c72c29903813eJake Slack
15903928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
16003928aee4356845252ac6b662d5c72c29903813eJake Slack         * Session restored from database
16103928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param sessionId
16203928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param rowId
16303928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param created
16403928aee4356845252ac6b662d5c72c29903813eJake Slack         * @param accessed
16503928aee4356845252ac6b662d5c72c29903813eJake Slack         */
16603928aee4356845252ac6b662d5c72c29903813eJake Slack        protected Session (String sessionId, String rowId, long created, long accessed)
16703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
16803928aee4356845252ac6b662d5c72c29903813eJake Slack            super(JDBCSessionManager.this, created, accessed, sessionId);
16903928aee4356845252ac6b662d5c72c29903813eJake Slack            _rowId = rowId;
17003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
17103928aee4356845252ac6b662d5c72c29903813eJake Slack
17203928aee4356845252ac6b662d5c72c29903813eJake Slack
17303928aee4356845252ac6b662d5c72c29903813eJake Slack        protected synchronized String getRowId()
17403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
17503928aee4356845252ac6b662d5c72c29903813eJake Slack            return _rowId;
17603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
17703928aee4356845252ac6b662d5c72c29903813eJake Slack
17803928aee4356845252ac6b662d5c72c29903813eJake Slack        protected synchronized void setRowId(String rowId)
17903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
18003928aee4356845252ac6b662d5c72c29903813eJake Slack            _rowId = rowId;
18103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
18203928aee4356845252ac6b662d5c72c29903813eJake Slack
18303928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized void setVirtualHost (String vhost)
18403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
18503928aee4356845252ac6b662d5c72c29903813eJake Slack            _virtualHost=vhost;
18603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
18703928aee4356845252ac6b662d5c72c29903813eJake Slack
18803928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized String getVirtualHost ()
18903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
19003928aee4356845252ac6b662d5c72c29903813eJake Slack            return _virtualHost;
19103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
19203928aee4356845252ac6b662d5c72c29903813eJake Slack
19303928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized long getLastSaved ()
19403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
19503928aee4356845252ac6b662d5c72c29903813eJake Slack            return _lastSaved;
19603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
19703928aee4356845252ac6b662d5c72c29903813eJake Slack
19803928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized void setLastSaved (long time)
19903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
20003928aee4356845252ac6b662d5c72c29903813eJake Slack            _lastSaved=time;
20103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
20203928aee4356845252ac6b662d5c72c29903813eJake Slack
20303928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized void setExpiryTime (long time)
20403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
20503928aee4356845252ac6b662d5c72c29903813eJake Slack            _expiryTime=time;
20603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
20703928aee4356845252ac6b662d5c72c29903813eJake Slack
20803928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized long getExpiryTime ()
20903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
21003928aee4356845252ac6b662d5c72c29903813eJake Slack            return _expiryTime;
21103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
21203928aee4356845252ac6b662d5c72c29903813eJake Slack
21303928aee4356845252ac6b662d5c72c29903813eJake Slack
21403928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized void setCanonicalContext(String str)
21503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
21603928aee4356845252ac6b662d5c72c29903813eJake Slack            _canonicalContext=str;
21703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
21803928aee4356845252ac6b662d5c72c29903813eJake Slack
21903928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized String getCanonicalContext ()
22003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
22103928aee4356845252ac6b662d5c72c29903813eJake Slack            return _canonicalContext;
22203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
22303928aee4356845252ac6b662d5c72c29903813eJake Slack
22403928aee4356845252ac6b662d5c72c29903813eJake Slack        public void setCookieSet (long ms)
22503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
22603928aee4356845252ac6b662d5c72c29903813eJake Slack            _cookieSet = ms;
22703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
22803928aee4356845252ac6b662d5c72c29903813eJake Slack
22903928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized long getCookieSet ()
23003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
23103928aee4356845252ac6b662d5c72c29903813eJake Slack            return _cookieSet;
23203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
23303928aee4356845252ac6b662d5c72c29903813eJake Slack
23403928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized void setLastNode (String node)
23503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
23603928aee4356845252ac6b662d5c72c29903813eJake Slack            _lastNode=node;
23703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
23803928aee4356845252ac6b662d5c72c29903813eJake Slack
23903928aee4356845252ac6b662d5c72c29903813eJake Slack        public synchronized String getLastNode ()
24003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
24103928aee4356845252ac6b662d5c72c29903813eJake Slack            return _lastNode;
24203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
24303928aee4356845252ac6b662d5c72c29903813eJake Slack
24403928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
24503928aee4356845252ac6b662d5c72c29903813eJake Slack        public void setAttribute (String name, Object value)
24603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
24703928aee4356845252ac6b662d5c72c29903813eJake Slack            super.setAttribute(name, value);
24803928aee4356845252ac6b662d5c72c29903813eJake Slack            _dirty=true;
24903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
25003928aee4356845252ac6b662d5c72c29903813eJake Slack
25103928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
25203928aee4356845252ac6b662d5c72c29903813eJake Slack        public void removeAttribute (String name)
25303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
25403928aee4356845252ac6b662d5c72c29903813eJake Slack            super.removeAttribute(name);
25503928aee4356845252ac6b662d5c72c29903813eJake Slack            _dirty=true;
25603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
25703928aee4356845252ac6b662d5c72c29903813eJake Slack
25803928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
25903928aee4356845252ac6b662d5c72c29903813eJake Slack        protected void cookieSet()
26003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
26103928aee4356845252ac6b662d5c72c29903813eJake Slack            _cookieSet = getAccessed();
26203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
26303928aee4356845252ac6b662d5c72c29903813eJake Slack
26403928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
26503928aee4356845252ac6b662d5c72c29903813eJake Slack         * Entry to session.
26603928aee4356845252ac6b662d5c72c29903813eJake Slack         * Called by SessionHandler on inbound request and the session already exists in this node's memory.
26703928aee4356845252ac6b662d5c72c29903813eJake Slack         *
26803928aee4356845252ac6b662d5c72c29903813eJake Slack         * @see org.eclipse.jetty.server.session.AbstractSession#access(long)
26903928aee4356845252ac6b662d5c72c29903813eJake Slack         */
27003928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
27103928aee4356845252ac6b662d5c72c29903813eJake Slack        protected boolean access(long time)
27203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
27303928aee4356845252ac6b662d5c72c29903813eJake Slack            synchronized (this)
27403928aee4356845252ac6b662d5c72c29903813eJake Slack            {
27503928aee4356845252ac6b662d5c72c29903813eJake Slack                if (super.access(time))
27603928aee4356845252ac6b662d5c72c29903813eJake Slack                {
27703928aee4356845252ac6b662d5c72c29903813eJake Slack                    int maxInterval=getMaxInactiveInterval();
27803928aee4356845252ac6b662d5c72c29903813eJake Slack                    _expiryTime = (maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
27903928aee4356845252ac6b662d5c72c29903813eJake Slack                    return true;
28003928aee4356845252ac6b662d5c72c29903813eJake Slack                }
28103928aee4356845252ac6b662d5c72c29903813eJake Slack                return false;
28203928aee4356845252ac6b662d5c72c29903813eJake Slack            }
28303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
28403928aee4356845252ac6b662d5c72c29903813eJake Slack
28503928aee4356845252ac6b662d5c72c29903813eJake Slack
28603928aee4356845252ac6b662d5c72c29903813eJake Slack
28703928aee4356845252ac6b662d5c72c29903813eJake Slack        /**
28803928aee4356845252ac6b662d5c72c29903813eJake Slack         * Exit from session
28903928aee4356845252ac6b662d5c72c29903813eJake Slack         * @see org.eclipse.jetty.server.session.AbstractSession#complete()
29003928aee4356845252ac6b662d5c72c29903813eJake Slack         */
29103928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
29203928aee4356845252ac6b662d5c72c29903813eJake Slack        protected void complete()
29303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
29403928aee4356845252ac6b662d5c72c29903813eJake Slack            synchronized (this)
29503928aee4356845252ac6b662d5c72c29903813eJake Slack            {
29603928aee4356845252ac6b662d5c72c29903813eJake Slack                super.complete();
29703928aee4356845252ac6b662d5c72c29903813eJake Slack                try
29803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
29903928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (isValid())
30003928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
30103928aee4356845252ac6b662d5c72c29903813eJake Slack                        if (_dirty)
30203928aee4356845252ac6b662d5c72c29903813eJake Slack                        {
30303928aee4356845252ac6b662d5c72c29903813eJake Slack                            //The session attributes have changed, write to the db, ensuring
30403928aee4356845252ac6b662d5c72c29903813eJake Slack                            //http passivation/activation listeners called
30503928aee4356845252ac6b662d5c72c29903813eJake Slack                            willPassivate();
30603928aee4356845252ac6b662d5c72c29903813eJake Slack                            updateSession(this);
30703928aee4356845252ac6b662d5c72c29903813eJake Slack                            didActivate();
30803928aee4356845252ac6b662d5c72c29903813eJake Slack                        }
30903928aee4356845252ac6b662d5c72c29903813eJake Slack                        else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
31003928aee4356845252ac6b662d5c72c29903813eJake Slack                        {
31103928aee4356845252ac6b662d5c72c29903813eJake Slack                            updateSessionAccessTime(this);
31203928aee4356845252ac6b662d5c72c29903813eJake Slack                        }
31303928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
31403928aee4356845252ac6b662d5c72c29903813eJake Slack                }
31503928aee4356845252ac6b662d5c72c29903813eJake Slack                catch (Exception e)
31603928aee4356845252ac6b662d5c72c29903813eJake Slack                {
31703928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.warn("Problem persisting changed session data id="+getId(), e);
31803928aee4356845252ac6b662d5c72c29903813eJake Slack                }
31903928aee4356845252ac6b662d5c72c29903813eJake Slack                finally
32003928aee4356845252ac6b662d5c72c29903813eJake Slack                {
32103928aee4356845252ac6b662d5c72c29903813eJake Slack                    _dirty=false;
32203928aee4356845252ac6b662d5c72c29903813eJake Slack                }
32303928aee4356845252ac6b662d5c72c29903813eJake Slack            }
32403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
32503928aee4356845252ac6b662d5c72c29903813eJake Slack
32603928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
32703928aee4356845252ac6b662d5c72c29903813eJake Slack        protected void timeout() throws IllegalStateException
32803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
32903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
33003928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("Timing out session id="+getClusterId());
33103928aee4356845252ac6b662d5c72c29903813eJake Slack            super.timeout();
33203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
33303928aee4356845252ac6b662d5c72c29903813eJake Slack
33403928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
33503928aee4356845252ac6b662d5c72c29903813eJake Slack        public String toString ()
33603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
33703928aee4356845252ac6b662d5c72c29903813eJake Slack            return "Session rowId="+_rowId+",id="+getId()+",lastNode="+_lastNode+
33803928aee4356845252ac6b662d5c72c29903813eJake Slack                            ",created="+getCreationTime()+",accessed="+getAccessed()+
33903928aee4356845252ac6b662d5c72c29903813eJake Slack                            ",lastAccessed="+getLastAccessedTime()+",cookieSet="+_cookieSet+
34003928aee4356845252ac6b662d5c72c29903813eJake Slack                            ",lastSaved="+_lastSaved+",expiry="+_expiryTime;
34103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
34203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
34303928aee4356845252ac6b662d5c72c29903813eJake Slack
34403928aee4356845252ac6b662d5c72c29903813eJake Slack
34503928aee4356845252ac6b662d5c72c29903813eJake Slack
34603928aee4356845252ac6b662d5c72c29903813eJake Slack
34703928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
34803928aee4356845252ac6b662d5c72c29903813eJake Slack     * ClassLoadingObjectInputStream
34903928aee4356845252ac6b662d5c72c29903813eJake Slack     *
35003928aee4356845252ac6b662d5c72c29903813eJake Slack     * Used to persist the session attribute map
35103928aee4356845252ac6b662d5c72c29903813eJake Slack     */
35203928aee4356845252ac6b662d5c72c29903813eJake Slack    protected class ClassLoadingObjectInputStream extends ObjectInputStream
35303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
35403928aee4356845252ac6b662d5c72c29903813eJake Slack        public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
35503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
35603928aee4356845252ac6b662d5c72c29903813eJake Slack            super(in);
35703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
35803928aee4356845252ac6b662d5c72c29903813eJake Slack
35903928aee4356845252ac6b662d5c72c29903813eJake Slack        public ClassLoadingObjectInputStream () throws IOException
36003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
36103928aee4356845252ac6b662d5c72c29903813eJake Slack            super();
36203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
36303928aee4356845252ac6b662d5c72c29903813eJake Slack
36403928aee4356845252ac6b662d5c72c29903813eJake Slack        @Override
36503928aee4356845252ac6b662d5c72c29903813eJake Slack        public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
36603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
36703928aee4356845252ac6b662d5c72c29903813eJake Slack            try
36803928aee4356845252ac6b662d5c72c29903813eJake Slack            {
36903928aee4356845252ac6b662d5c72c29903813eJake Slack                return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
37003928aee4356845252ac6b662d5c72c29903813eJake Slack            }
37103928aee4356845252ac6b662d5c72c29903813eJake Slack            catch (ClassNotFoundException e)
37203928aee4356845252ac6b662d5c72c29903813eJake Slack            {
37303928aee4356845252ac6b662d5c72c29903813eJake Slack                return super.resolveClass(cl);
37403928aee4356845252ac6b662d5c72c29903813eJake Slack            }
37503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
37603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
37703928aee4356845252ac6b662d5c72c29903813eJake Slack
37803928aee4356845252ac6b662d5c72c29903813eJake Slack
37903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
38003928aee4356845252ac6b662d5c72c29903813eJake Slack     * Set the time in seconds which is the interval between
38103928aee4356845252ac6b662d5c72c29903813eJake Slack     * saving the session access time to the database.
38203928aee4356845252ac6b662d5c72c29903813eJake Slack     *
38303928aee4356845252ac6b662d5c72c29903813eJake Slack     * This is an optimization that prevents the database from
38403928aee4356845252ac6b662d5c72c29903813eJake Slack     * being overloaded when a session is accessed very frequently.
38503928aee4356845252ac6b662d5c72c29903813eJake Slack     *
38603928aee4356845252ac6b662d5c72c29903813eJake Slack     * On session exit, if the session attributes have NOT changed,
38703928aee4356845252ac6b662d5c72c29903813eJake Slack     * the time at which we last saved the accessed
38803928aee4356845252ac6b662d5c72c29903813eJake Slack     * time is compared to the current accessed time. If the interval
38903928aee4356845252ac6b662d5c72c29903813eJake Slack     * is at least saveIntervalSecs, then the access time will be
39003928aee4356845252ac6b662d5c72c29903813eJake Slack     * persisted to the database.
39103928aee4356845252ac6b662d5c72c29903813eJake Slack     *
39203928aee4356845252ac6b662d5c72c29903813eJake Slack     * If any session attribute does change, then the attributes and
39303928aee4356845252ac6b662d5c72c29903813eJake Slack     * the accessed time are persisted.
39403928aee4356845252ac6b662d5c72c29903813eJake Slack     *
39503928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param sec
39603928aee4356845252ac6b662d5c72c29903813eJake Slack     */
39703928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setSaveInterval (long sec)
39803928aee4356845252ac6b662d5c72c29903813eJake Slack    {
39903928aee4356845252ac6b662d5c72c29903813eJake Slack        _saveIntervalSec=sec;
40003928aee4356845252ac6b662d5c72c29903813eJake Slack    }
40103928aee4356845252ac6b662d5c72c29903813eJake Slack
40203928aee4356845252ac6b662d5c72c29903813eJake Slack    public long getSaveInterval ()
40303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
40403928aee4356845252ac6b662d5c72c29903813eJake Slack        return _saveIntervalSec;
40503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
40603928aee4356845252ac6b662d5c72c29903813eJake Slack
40703928aee4356845252ac6b662d5c72c29903813eJake Slack
40803928aee4356845252ac6b662d5c72c29903813eJake Slack
40903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
41003928aee4356845252ac6b662d5c72c29903813eJake Slack     * A method that can be implemented in subclasses to support
41103928aee4356845252ac6b662d5c72c29903813eJake Slack     * distributed caching of sessions. This method will be
41203928aee4356845252ac6b662d5c72c29903813eJake Slack     * called whenever the session is written to the database
41303928aee4356845252ac6b662d5c72c29903813eJake Slack     * because the session data has changed.
41403928aee4356845252ac6b662d5c72c29903813eJake Slack     *
41503928aee4356845252ac6b662d5c72c29903813eJake Slack     * This could be used eg with a JMS backplane to notify nodes
41603928aee4356845252ac6b662d5c72c29903813eJake Slack     * that the session has changed and to delete the session from
41703928aee4356845252ac6b662d5c72c29903813eJake Slack     * the node's cache, and re-read it from the database.
41803928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param session
41903928aee4356845252ac6b662d5c72c29903813eJake Slack     */
42003928aee4356845252ac6b662d5c72c29903813eJake Slack    public void cacheInvalidate (Session session)
42103928aee4356845252ac6b662d5c72c29903813eJake Slack    {
42203928aee4356845252ac6b662d5c72c29903813eJake Slack
42303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
42403928aee4356845252ac6b662d5c72c29903813eJake Slack
42503928aee4356845252ac6b662d5c72c29903813eJake Slack
42603928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
42703928aee4356845252ac6b662d5c72c29903813eJake Slack     * A session has been requested by its id on this node.
42803928aee4356845252ac6b662d5c72c29903813eJake Slack     *
42903928aee4356845252ac6b662d5c72c29903813eJake Slack     * Load the session by id AND context path from the database.
43003928aee4356845252ac6b662d5c72c29903813eJake Slack     * Multiple contexts may share the same session id (due to dispatching)
43103928aee4356845252ac6b662d5c72c29903813eJake Slack     * but they CANNOT share the same contents.
43203928aee4356845252ac6b662d5c72c29903813eJake Slack     *
43303928aee4356845252ac6b662d5c72c29903813eJake Slack     * Check if last node id is my node id, if so, then the session we have
43403928aee4356845252ac6b662d5c72c29903813eJake Slack     * in memory cannot be stale. If another node used the session last, then
43503928aee4356845252ac6b662d5c72c29903813eJake Slack     * we need to refresh from the db.
43603928aee4356845252ac6b662d5c72c29903813eJake Slack     *
43703928aee4356845252ac6b662d5c72c29903813eJake Slack     * NOTE: this method will go to the database, so if you only want to check
43803928aee4356845252ac6b662d5c72c29903813eJake Slack     * for the existence of a Session in memory, use _sessions.get(id) instead.
43903928aee4356845252ac6b662d5c72c29903813eJake Slack     *
44003928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
44103928aee4356845252ac6b662d5c72c29903813eJake Slack     */
44203928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
44303928aee4356845252ac6b662d5c72c29903813eJake Slack    public Session getSession(String idInCluster)
44403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
44503928aee4356845252ac6b662d5c72c29903813eJake Slack        Session session = null;
44603928aee4356845252ac6b662d5c72c29903813eJake Slack        Session memSession = (Session)_sessions.get(idInCluster);
44703928aee4356845252ac6b662d5c72c29903813eJake Slack
44803928aee4356845252ac6b662d5c72c29903813eJake Slack        synchronized (this)
44903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
45003928aee4356845252ac6b662d5c72c29903813eJake Slack                //check if we need to reload the session -
45103928aee4356845252ac6b662d5c72c29903813eJake Slack                //as an optimization, don't reload on every access
45203928aee4356845252ac6b662d5c72c29903813eJake Slack                //to reduce the load on the database. This introduces a window of
45303928aee4356845252ac6b662d5c72c29903813eJake Slack                //possibility that the node may decide that the session is local to it,
45403928aee4356845252ac6b662d5c72c29903813eJake Slack                //when the session has actually been live on another node, and then
45503928aee4356845252ac6b662d5c72c29903813eJake Slack                //re-migrated to this node. This should be an extremely rare occurrence,
45603928aee4356845252ac6b662d5c72c29903813eJake Slack                //as load-balancers are generally well-behaved and consistently send
45703928aee4356845252ac6b662d5c72c29903813eJake Slack                //sessions to the same node, changing only iff that node fails.
45803928aee4356845252ac6b662d5c72c29903813eJake Slack                //Session data = null;
45903928aee4356845252ac6b662d5c72c29903813eJake Slack                long now = System.currentTimeMillis();
46003928aee4356845252ac6b662d5c72c29903813eJake Slack                if (LOG.isDebugEnabled())
46103928aee4356845252ac6b662d5c72c29903813eJake Slack                {
46203928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (memSession==null)
46303928aee4356845252ac6b662d5c72c29903813eJake Slack                        LOG.debug("getSession("+idInCluster+"): not in session map,"+
46403928aee4356845252ac6b662d5c72c29903813eJake Slack                                " now="+now+
46503928aee4356845252ac6b662d5c72c29903813eJake Slack                                " lastSaved="+(memSession==null?0:memSession._lastSaved)+
46603928aee4356845252ac6b662d5c72c29903813eJake Slack                                " interval="+(_saveIntervalSec * 1000L));
46703928aee4356845252ac6b662d5c72c29903813eJake Slack                    else
46803928aee4356845252ac6b662d5c72c29903813eJake Slack                        LOG.debug("getSession("+idInCluster+"): in session map, "+
46903928aee4356845252ac6b662d5c72c29903813eJake Slack                                " now="+now+
47003928aee4356845252ac6b662d5c72c29903813eJake Slack                                " lastSaved="+(memSession==null?0:memSession._lastSaved)+
47103928aee4356845252ac6b662d5c72c29903813eJake Slack                                " interval="+(_saveIntervalSec * 1000L)+
47203928aee4356845252ac6b662d5c72c29903813eJake Slack                                " lastNode="+memSession._lastNode+
47303928aee4356845252ac6b662d5c72c29903813eJake Slack                                " thisNode="+getSessionIdManager().getWorkerName()+
47403928aee4356845252ac6b662d5c72c29903813eJake Slack                                " difference="+(now - memSession._lastSaved));
47503928aee4356845252ac6b662d5c72c29903813eJake Slack                }
47603928aee4356845252ac6b662d5c72c29903813eJake Slack
47703928aee4356845252ac6b662d5c72c29903813eJake Slack                try
47803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
47903928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (memSession==null)
48003928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
48103928aee4356845252ac6b662d5c72c29903813eJake Slack                        LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
48203928aee4356845252ac6b662d5c72c29903813eJake Slack                        session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
48303928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
48403928aee4356845252ac6b662d5c72c29903813eJake Slack                    else if ((now - memSession._lastSaved) >= (_saveIntervalSec * 1000L))
48503928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
48603928aee4356845252ac6b662d5c72c29903813eJake Slack                        LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
48703928aee4356845252ac6b662d5c72c29903813eJake Slack                        session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
48803928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
48903928aee4356845252ac6b662d5c72c29903813eJake Slack                    else
49003928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
49103928aee4356845252ac6b662d5c72c29903813eJake Slack                        LOG.debug("getSession("+idInCluster+"): session in session map");
49203928aee4356845252ac6b662d5c72c29903813eJake Slack                        session = memSession;
49303928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
49403928aee4356845252ac6b662d5c72c29903813eJake Slack                }
49503928aee4356845252ac6b662d5c72c29903813eJake Slack                catch (Exception e)
49603928aee4356845252ac6b662d5c72c29903813eJake Slack                {
49703928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.warn("Unable to load session "+idInCluster, e);
49803928aee4356845252ac6b662d5c72c29903813eJake Slack                    return null;
49903928aee4356845252ac6b662d5c72c29903813eJake Slack                }
50003928aee4356845252ac6b662d5c72c29903813eJake Slack
50103928aee4356845252ac6b662d5c72c29903813eJake Slack
50203928aee4356845252ac6b662d5c72c29903813eJake Slack                //If we have a session
50303928aee4356845252ac6b662d5c72c29903813eJake Slack                if (session != null)
50403928aee4356845252ac6b662d5c72c29903813eJake Slack                {
50503928aee4356845252ac6b662d5c72c29903813eJake Slack                    //If the session was last used on a different node, or session doesn't exist on this node
50603928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (!session.getLastNode().equals(getSessionIdManager().getWorkerName()) || memSession==null)
50703928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
50803928aee4356845252ac6b662d5c72c29903813eJake Slack                        //if session doesn't expire, or has not already expired, update it and put it in this nodes' memory
50903928aee4356845252ac6b662d5c72c29903813eJake Slack                        if (session._expiryTime <= 0 || session._expiryTime > now)
51003928aee4356845252ac6b662d5c72c29903813eJake Slack                        {
51103928aee4356845252ac6b662d5c72c29903813eJake Slack                            if (LOG.isDebugEnabled())
51203928aee4356845252ac6b662d5c72c29903813eJake Slack                                LOG.debug("getSession("+idInCluster+"): lastNode="+session.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
51303928aee4356845252ac6b662d5c72c29903813eJake Slack
51403928aee4356845252ac6b662d5c72c29903813eJake Slack                            session.setLastNode(getSessionIdManager().getWorkerName());
51503928aee4356845252ac6b662d5c72c29903813eJake Slack                            _sessions.put(idInCluster, session);
51603928aee4356845252ac6b662d5c72c29903813eJake Slack
51703928aee4356845252ac6b662d5c72c29903813eJake Slack                            //update in db: if unable to update, session will be scavenged later
51803928aee4356845252ac6b662d5c72c29903813eJake Slack                            try
51903928aee4356845252ac6b662d5c72c29903813eJake Slack                            {
52003928aee4356845252ac6b662d5c72c29903813eJake Slack                                updateSessionNode(session);
52103928aee4356845252ac6b662d5c72c29903813eJake Slack                                session.didActivate();
52203928aee4356845252ac6b662d5c72c29903813eJake Slack                            }
52303928aee4356845252ac6b662d5c72c29903813eJake Slack                            catch (Exception e)
52403928aee4356845252ac6b662d5c72c29903813eJake Slack                            {
52503928aee4356845252ac6b662d5c72c29903813eJake Slack                                LOG.warn("Unable to update freshly loaded session "+idInCluster, e);
52603928aee4356845252ac6b662d5c72c29903813eJake Slack                                return null;
52703928aee4356845252ac6b662d5c72c29903813eJake Slack                            }
52803928aee4356845252ac6b662d5c72c29903813eJake Slack                        }
52903928aee4356845252ac6b662d5c72c29903813eJake Slack                        else
53003928aee4356845252ac6b662d5c72c29903813eJake Slack                        {
53103928aee4356845252ac6b662d5c72c29903813eJake Slack                            LOG.debug("getSession ({}): Session has expired", idInCluster);
53203928aee4356845252ac6b662d5c72c29903813eJake Slack                            session=null;
53303928aee4356845252ac6b662d5c72c29903813eJake Slack                        }
53403928aee4356845252ac6b662d5c72c29903813eJake Slack
53503928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
53603928aee4356845252ac6b662d5c72c29903813eJake Slack                    else
53703928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
53803928aee4356845252ac6b662d5c72c29903813eJake Slack                       //the session loaded from the db and the one in memory are the same, so keep using the one in memory
53903928aee4356845252ac6b662d5c72c29903813eJake Slack                       session = memSession;
54003928aee4356845252ac6b662d5c72c29903813eJake Slack                       LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
54103928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
54203928aee4356845252ac6b662d5c72c29903813eJake Slack                }
54303928aee4356845252ac6b662d5c72c29903813eJake Slack                else
54403928aee4356845252ac6b662d5c72c29903813eJake Slack                {
54503928aee4356845252ac6b662d5c72c29903813eJake Slack                    //No session in db with matching id and context path.
54603928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
54703928aee4356845252ac6b662d5c72c29903813eJake Slack                }
54803928aee4356845252ac6b662d5c72c29903813eJake Slack
54903928aee4356845252ac6b662d5c72c29903813eJake Slack                return session;
55003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
55103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
55203928aee4356845252ac6b662d5c72c29903813eJake Slack
55303928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
55403928aee4356845252ac6b662d5c72c29903813eJake Slack     * Get the number of sessions.
55503928aee4356845252ac6b662d5c72c29903813eJake Slack     *
55603928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSessions()
55703928aee4356845252ac6b662d5c72c29903813eJake Slack     */
55803928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
55903928aee4356845252ac6b662d5c72c29903813eJake Slack    public int getSessions()
56003928aee4356845252ac6b662d5c72c29903813eJake Slack    {
56103928aee4356845252ac6b662d5c72c29903813eJake Slack        int size = 0;
56203928aee4356845252ac6b662d5c72c29903813eJake Slack        synchronized (this)
56303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
56403928aee4356845252ac6b662d5c72c29903813eJake Slack            size = _sessions.size();
56503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
56603928aee4356845252ac6b662d5c72c29903813eJake Slack        return size;
56703928aee4356845252ac6b662d5c72c29903813eJake Slack    }
56803928aee4356845252ac6b662d5c72c29903813eJake Slack
56903928aee4356845252ac6b662d5c72c29903813eJake Slack
57003928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
57103928aee4356845252ac6b662d5c72c29903813eJake Slack     * Start the session manager.
57203928aee4356845252ac6b662d5c72c29903813eJake Slack     *
57303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
57403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
57503928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
57603928aee4356845252ac6b662d5c72c29903813eJake Slack    public void doStart() throws Exception
57703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
57803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_sessionIdManager==null)
57903928aee4356845252ac6b662d5c72c29903813eJake Slack            throw new IllegalStateException("No session id manager defined");
58003928aee4356845252ac6b662d5c72c29903813eJake Slack
58103928aee4356845252ac6b662d5c72c29903813eJake Slack        _jdbcSessionIdMgr = (JDBCSessionIdManager)_sessionIdManager;
58203928aee4356845252ac6b662d5c72c29903813eJake Slack
58303928aee4356845252ac6b662d5c72c29903813eJake Slack        _sessions = new ConcurrentHashMap<String, AbstractSession>();
58403928aee4356845252ac6b662d5c72c29903813eJake Slack
58503928aee4356845252ac6b662d5c72c29903813eJake Slack        super.doStart();
58603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
58703928aee4356845252ac6b662d5c72c29903813eJake Slack
58803928aee4356845252ac6b662d5c72c29903813eJake Slack
58903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
59003928aee4356845252ac6b662d5c72c29903813eJake Slack     * Stop the session manager.
59103928aee4356845252ac6b662d5c72c29903813eJake Slack     *
59203928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop()
59303928aee4356845252ac6b662d5c72c29903813eJake Slack     */
59403928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
59503928aee4356845252ac6b662d5c72c29903813eJake Slack    public void doStop() throws Exception
59603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
59703928aee4356845252ac6b662d5c72c29903813eJake Slack        _sessions.clear();
59803928aee4356845252ac6b662d5c72c29903813eJake Slack        _sessions = null;
59903928aee4356845252ac6b662d5c72c29903813eJake Slack
60003928aee4356845252ac6b662d5c72c29903813eJake Slack        super.doStop();
60103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
60203928aee4356845252ac6b662d5c72c29903813eJake Slack
60303928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
60403928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void invalidateSessions()
60503928aee4356845252ac6b662d5c72c29903813eJake Slack    {
60603928aee4356845252ac6b662d5c72c29903813eJake Slack        //Do nothing - we don't want to remove and
60703928aee4356845252ac6b662d5c72c29903813eJake Slack        //invalidate all the sessions because this
60803928aee4356845252ac6b662d5c72c29903813eJake Slack        //method is called from doStop(), and just
60903928aee4356845252ac6b662d5c72c29903813eJake Slack        //because this context is stopping does not
61003928aee4356845252ac6b662d5c72c29903813eJake Slack        //mean that we should remove the session from
61103928aee4356845252ac6b662d5c72c29903813eJake Slack        //any other nodes
61203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
61303928aee4356845252ac6b662d5c72c29903813eJake Slack
61403928aee4356845252ac6b662d5c72c29903813eJake Slack
61503928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
61603928aee4356845252ac6b662d5c72c29903813eJake Slack     * Invalidate a session.
61703928aee4356845252ac6b662d5c72c29903813eJake Slack     *
61803928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param idInCluster
61903928aee4356845252ac6b662d5c72c29903813eJake Slack     */
62003928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void invalidateSession (String idInCluster)
62103928aee4356845252ac6b662d5c72c29903813eJake Slack    {
62203928aee4356845252ac6b662d5c72c29903813eJake Slack        Session session = null;
62303928aee4356845252ac6b662d5c72c29903813eJake Slack        synchronized (this)
62403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
62503928aee4356845252ac6b662d5c72c29903813eJake Slack            session = (Session)_sessions.get(idInCluster);
62603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
62703928aee4356845252ac6b662d5c72c29903813eJake Slack
62803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (session != null)
62903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
63003928aee4356845252ac6b662d5c72c29903813eJake Slack            session.invalidate();
63103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
63203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
63303928aee4356845252ac6b662d5c72c29903813eJake Slack
63403928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
63503928aee4356845252ac6b662d5c72c29903813eJake Slack     * Delete an existing session, both from the in-memory map and
63603928aee4356845252ac6b662d5c72c29903813eJake Slack     * the database.
63703928aee4356845252ac6b662d5c72c29903813eJake Slack     *
63803928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
63903928aee4356845252ac6b662d5c72c29903813eJake Slack     */
64003928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
64103928aee4356845252ac6b662d5c72c29903813eJake Slack    protected boolean removeSession(String idInCluster)
64203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
64303928aee4356845252ac6b662d5c72c29903813eJake Slack        synchronized (this)
64403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
64503928aee4356845252ac6b662d5c72c29903813eJake Slack            Session session = (Session)_sessions.remove(idInCluster);
64603928aee4356845252ac6b662d5c72c29903813eJake Slack            try
64703928aee4356845252ac6b662d5c72c29903813eJake Slack            {
64803928aee4356845252ac6b662d5c72c29903813eJake Slack                if (session != null)
64903928aee4356845252ac6b662d5c72c29903813eJake Slack                    deleteSession(session);
65003928aee4356845252ac6b662d5c72c29903813eJake Slack            }
65103928aee4356845252ac6b662d5c72c29903813eJake Slack            catch (Exception e)
65203928aee4356845252ac6b662d5c72c29903813eJake Slack            {
65303928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.warn("Problem deleting session id="+idInCluster, e);
65403928aee4356845252ac6b662d5c72c29903813eJake Slack            }
65503928aee4356845252ac6b662d5c72c29903813eJake Slack            return session!=null;
65603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
65703928aee4356845252ac6b662d5c72c29903813eJake Slack    }
65803928aee4356845252ac6b662d5c72c29903813eJake Slack
65903928aee4356845252ac6b662d5c72c29903813eJake Slack
66003928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
66103928aee4356845252ac6b662d5c72c29903813eJake Slack     * Add a newly created session to our in-memory list for this node and persist it.
66203928aee4356845252ac6b662d5c72c29903813eJake Slack     *
66303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
66403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
66503928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
66603928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void addSession(AbstractSession session)
66703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
66803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (session==null)
66903928aee4356845252ac6b662d5c72c29903813eJake Slack            return;
67003928aee4356845252ac6b662d5c72c29903813eJake Slack
67103928aee4356845252ac6b662d5c72c29903813eJake Slack        synchronized (this)
67203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
67303928aee4356845252ac6b662d5c72c29903813eJake Slack            _sessions.put(session.getClusterId(), session);
67403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
67503928aee4356845252ac6b662d5c72c29903813eJake Slack
67603928aee4356845252ac6b662d5c72c29903813eJake Slack        //TODO or delay the store until exit out of session? If we crash before we store it
67703928aee4356845252ac6b662d5c72c29903813eJake Slack        //then session data will be lost.
67803928aee4356845252ac6b662d5c72c29903813eJake Slack        try
67903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
68003928aee4356845252ac6b662d5c72c29903813eJake Slack            synchronized (session)
68103928aee4356845252ac6b662d5c72c29903813eJake Slack            {
68203928aee4356845252ac6b662d5c72c29903813eJake Slack                session.willPassivate();
68303928aee4356845252ac6b662d5c72c29903813eJake Slack                storeSession(((JDBCSessionManager.Session)session));
68403928aee4356845252ac6b662d5c72c29903813eJake Slack                session.didActivate();
68503928aee4356845252ac6b662d5c72c29903813eJake Slack            }
68603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
68703928aee4356845252ac6b662d5c72c29903813eJake Slack        catch (Exception e)
68803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
68903928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.warn("Unable to store new session id="+session.getId() , e);
69003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
69103928aee4356845252ac6b662d5c72c29903813eJake Slack    }
69203928aee4356845252ac6b662d5c72c29903813eJake Slack
69303928aee4356845252ac6b662d5c72c29903813eJake Slack
69403928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
69503928aee4356845252ac6b662d5c72c29903813eJake Slack     * Make a new Session.
69603928aee4356845252ac6b662d5c72c29903813eJake Slack     *
69703928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.server.session.AbstractSessionManager#newSession(javax.servlet.http.HttpServletRequest)
69803928aee4356845252ac6b662d5c72c29903813eJake Slack     */
69903928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
70003928aee4356845252ac6b662d5c72c29903813eJake Slack    protected AbstractSession newSession(HttpServletRequest request)
70103928aee4356845252ac6b662d5c72c29903813eJake Slack    {
70203928aee4356845252ac6b662d5c72c29903813eJake Slack        return new Session(request);
70303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
70403928aee4356845252ac6b662d5c72c29903813eJake Slack
70503928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
70603928aee4356845252ac6b662d5c72c29903813eJake Slack    /** Remove session from manager
70703928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param session The session to remove
70803928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
70903928aee4356845252ac6b662d5c72c29903813eJake Slack     * {@link SessionIdManager#invalidateAll(String)} should be called.
71003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
71103928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
71203928aee4356845252ac6b662d5c72c29903813eJake Slack    public void removeSession(AbstractSession session, boolean invalidate)
71303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
71403928aee4356845252ac6b662d5c72c29903813eJake Slack        // Remove session from context and global maps
71503928aee4356845252ac6b662d5c72c29903813eJake Slack        boolean removed = false;
71603928aee4356845252ac6b662d5c72c29903813eJake Slack
71703928aee4356845252ac6b662d5c72c29903813eJake Slack        synchronized (this)
71803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
71903928aee4356845252ac6b662d5c72c29903813eJake Slack            //take this session out of the map of sessions for this context
72003928aee4356845252ac6b662d5c72c29903813eJake Slack            if (getSession(session.getClusterId()) != null)
72103928aee4356845252ac6b662d5c72c29903813eJake Slack            {
72203928aee4356845252ac6b662d5c72c29903813eJake Slack                removed = true;
72303928aee4356845252ac6b662d5c72c29903813eJake Slack                removeSession(session.getClusterId());
72403928aee4356845252ac6b662d5c72c29903813eJake Slack            }
72503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
72603928aee4356845252ac6b662d5c72c29903813eJake Slack
72703928aee4356845252ac6b662d5c72c29903813eJake Slack        if (removed)
72803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
72903928aee4356845252ac6b662d5c72c29903813eJake Slack            // Remove session from all context and global id maps
73003928aee4356845252ac6b662d5c72c29903813eJake Slack            _sessionIdManager.removeSession(session);
73103928aee4356845252ac6b662d5c72c29903813eJake Slack
73203928aee4356845252ac6b662d5c72c29903813eJake Slack            if (invalidate)
73303928aee4356845252ac6b662d5c72c29903813eJake Slack                _sessionIdManager.invalidateAll(session.getClusterId());
73403928aee4356845252ac6b662d5c72c29903813eJake Slack
73503928aee4356845252ac6b662d5c72c29903813eJake Slack            if (invalidate && !_sessionListeners.isEmpty())
73603928aee4356845252ac6b662d5c72c29903813eJake Slack            {
73703928aee4356845252ac6b662d5c72c29903813eJake Slack                HttpSessionEvent event=new HttpSessionEvent(session);
73803928aee4356845252ac6b662d5c72c29903813eJake Slack                for (HttpSessionListener l : _sessionListeners)
73903928aee4356845252ac6b662d5c72c29903813eJake Slack                    l.sessionDestroyed(event);
74003928aee4356845252ac6b662d5c72c29903813eJake Slack            }
74103928aee4356845252ac6b662d5c72c29903813eJake Slack            if (!invalidate)
74203928aee4356845252ac6b662d5c72c29903813eJake Slack            {
74303928aee4356845252ac6b662d5c72c29903813eJake Slack                session.willPassivate();
74403928aee4356845252ac6b662d5c72c29903813eJake Slack            }
74503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
74603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
74703928aee4356845252ac6b662d5c72c29903813eJake Slack
74803928aee4356845252ac6b662d5c72c29903813eJake Slack
74903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
75003928aee4356845252ac6b662d5c72c29903813eJake Slack     * Expire any Sessions we have in memory matching the list of
75103928aee4356845252ac6b662d5c72c29903813eJake Slack     * expired Session ids.
75203928aee4356845252ac6b662d5c72c29903813eJake Slack     *
75303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param sessionIds
75403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
75503928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void expire (List<?> sessionIds)
75603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
75703928aee4356845252ac6b662d5c72c29903813eJake Slack        //don't attempt to scavenge if we are shutting down
75803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (isStopping() || isStopped())
75903928aee4356845252ac6b662d5c72c29903813eJake Slack            return;
76003928aee4356845252ac6b662d5c72c29903813eJake Slack
76103928aee4356845252ac6b662d5c72c29903813eJake Slack        //Remove any sessions we already have in memory that match the ids
76203928aee4356845252ac6b662d5c72c29903813eJake Slack        Thread thread=Thread.currentThread();
76303928aee4356845252ac6b662d5c72c29903813eJake Slack        ClassLoader old_loader=thread.getContextClassLoader();
76403928aee4356845252ac6b662d5c72c29903813eJake Slack        ListIterator<?> itor = sessionIds.listIterator();
76503928aee4356845252ac6b662d5c72c29903813eJake Slack
76603928aee4356845252ac6b662d5c72c29903813eJake Slack        try
76703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
76803928aee4356845252ac6b662d5c72c29903813eJake Slack            while (itor.hasNext())
76903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
77003928aee4356845252ac6b662d5c72c29903813eJake Slack                String sessionId = (String)itor.next();
77103928aee4356845252ac6b662d5c72c29903813eJake Slack                if (LOG.isDebugEnabled())
77203928aee4356845252ac6b662d5c72c29903813eJake Slack                    LOG.debug("Expiring session id "+sessionId);
77303928aee4356845252ac6b662d5c72c29903813eJake Slack
77403928aee4356845252ac6b662d5c72c29903813eJake Slack                Session session = (Session)_sessions.get(sessionId);
77503928aee4356845252ac6b662d5c72c29903813eJake Slack                if (session != null)
77603928aee4356845252ac6b662d5c72c29903813eJake Slack                {
77703928aee4356845252ac6b662d5c72c29903813eJake Slack                    session.timeout();
77803928aee4356845252ac6b662d5c72c29903813eJake Slack                    itor.remove();
77903928aee4356845252ac6b662d5c72c29903813eJake Slack                }
78003928aee4356845252ac6b662d5c72c29903813eJake Slack                else
78103928aee4356845252ac6b662d5c72c29903813eJake Slack                {
78203928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (LOG.isDebugEnabled())
78303928aee4356845252ac6b662d5c72c29903813eJake Slack                        LOG.debug("Unrecognized session id="+sessionId);
78403928aee4356845252ac6b662d5c72c29903813eJake Slack                }
78503928aee4356845252ac6b662d5c72c29903813eJake Slack            }
78603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
78703928aee4356845252ac6b662d5c72c29903813eJake Slack        catch (Throwable t)
78803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
78903928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.warn("Problem expiring sessions", t);
79003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
79103928aee4356845252ac6b662d5c72c29903813eJake Slack        finally
79203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
79303928aee4356845252ac6b662d5c72c29903813eJake Slack            thread.setContextClassLoader(old_loader);
79403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
79503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
79603928aee4356845252ac6b662d5c72c29903813eJake Slack
79703928aee4356845252ac6b662d5c72c29903813eJake Slack
79803928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
79903928aee4356845252ac6b662d5c72c29903813eJake Slack     * Load a session from the database
80003928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param id
80103928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return the session data that was loaded
80203928aee4356845252ac6b662d5c72c29903813eJake Slack     * @throws Exception
80303928aee4356845252ac6b662d5c72c29903813eJake Slack     */
80403928aee4356845252ac6b662d5c72c29903813eJake Slack    protected Session loadSession (final String id, final String canonicalContextPath, final String vhost)
80503928aee4356845252ac6b662d5c72c29903813eJake Slack    throws Exception
80603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
80703928aee4356845252ac6b662d5c72c29903813eJake Slack        final AtomicReference<Session> _reference = new AtomicReference<Session>();
80803928aee4356845252ac6b662d5c72c29903813eJake Slack        final AtomicReference<Exception> _exception = new AtomicReference<Exception>();
80903928aee4356845252ac6b662d5c72c29903813eJake Slack        Runnable load = new Runnable()
81003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
81103928aee4356845252ac6b662d5c72c29903813eJake Slack            @SuppressWarnings("unchecked")
81203928aee4356845252ac6b662d5c72c29903813eJake Slack            public void run()
81303928aee4356845252ac6b662d5c72c29903813eJake Slack            {
81403928aee4356845252ac6b662d5c72c29903813eJake Slack                Session session = null;
81503928aee4356845252ac6b662d5c72c29903813eJake Slack                Connection connection=null;
81603928aee4356845252ac6b662d5c72c29903813eJake Slack                PreparedStatement statement = null;
81703928aee4356845252ac6b662d5c72c29903813eJake Slack                try
81803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
81903928aee4356845252ac6b662d5c72c29903813eJake Slack                    connection = getConnection();
82003928aee4356845252ac6b662d5c72c29903813eJake Slack                    statement = _jdbcSessionIdMgr._dbAdaptor.getLoadStatement(connection, id, canonicalContextPath, vhost);
82103928aee4356845252ac6b662d5c72c29903813eJake Slack                    ResultSet result = statement.executeQuery();
82203928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (result.next())
82303928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
82403928aee4356845252ac6b662d5c72c29903813eJake Slack                        session = new Session(id, result.getString(_jdbcSessionIdMgr._sessionTableRowId), result.getLong("createTime"), result.getLong("accessTime"));
82503928aee4356845252ac6b662d5c72c29903813eJake Slack                        session.setCookieSet(result.getLong("cookieTime"));
82603928aee4356845252ac6b662d5c72c29903813eJake Slack                        session.setLastAccessedTime(result.getLong("lastAccessTime"));
82703928aee4356845252ac6b662d5c72c29903813eJake Slack                        session.setLastNode(result.getString("lastNode"));
82803928aee4356845252ac6b662d5c72c29903813eJake Slack                        session.setLastSaved(result.getLong("lastSavedTime"));
82903928aee4356845252ac6b662d5c72c29903813eJake Slack                        session.setExpiryTime(result.getLong("expiryTime"));
83003928aee4356845252ac6b662d5c72c29903813eJake Slack                        session.setCanonicalContext(result.getString("contextPath"));
83103928aee4356845252ac6b662d5c72c29903813eJake Slack                        session.setVirtualHost(result.getString("virtualHost"));
83203928aee4356845252ac6b662d5c72c29903813eJake Slack
83303928aee4356845252ac6b662d5c72c29903813eJake Slack                        InputStream is = ((JDBCSessionIdManager)getSessionIdManager())._dbAdaptor.getBlobInputStream(result, "map");
83403928aee4356845252ac6b662d5c72c29903813eJake Slack                        ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream (is);
83503928aee4356845252ac6b662d5c72c29903813eJake Slack                        Object o = ois.readObject();
83603928aee4356845252ac6b662d5c72c29903813eJake Slack                        session.addAttributes((Map<String,Object>)o);
83703928aee4356845252ac6b662d5c72c29903813eJake Slack                        ois.close();
83803928aee4356845252ac6b662d5c72c29903813eJake Slack
83903928aee4356845252ac6b662d5c72c29903813eJake Slack                        if (LOG.isDebugEnabled())
84003928aee4356845252ac6b662d5c72c29903813eJake Slack                            LOG.debug("LOADED session "+session);
84103928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
84203928aee4356845252ac6b662d5c72c29903813eJake Slack                    _reference.set(session);
84303928aee4356845252ac6b662d5c72c29903813eJake Slack                }
84403928aee4356845252ac6b662d5c72c29903813eJake Slack                catch (Exception e)
84503928aee4356845252ac6b662d5c72c29903813eJake Slack                {
84603928aee4356845252ac6b662d5c72c29903813eJake Slack                    _exception.set(e);
84703928aee4356845252ac6b662d5c72c29903813eJake Slack                }
84803928aee4356845252ac6b662d5c72c29903813eJake Slack                finally
84903928aee4356845252ac6b662d5c72c29903813eJake Slack                {
85003928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (statement!=null)
85103928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
85203928aee4356845252ac6b662d5c72c29903813eJake Slack                        try { statement.close(); }
85303928aee4356845252ac6b662d5c72c29903813eJake Slack                        catch(Exception e) { LOG.warn(e); }
85403928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
85503928aee4356845252ac6b662d5c72c29903813eJake Slack
85603928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (connection!=null)
85703928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
85803928aee4356845252ac6b662d5c72c29903813eJake Slack                        try { connection.close();}
85903928aee4356845252ac6b662d5c72c29903813eJake Slack                        catch(Exception e) { LOG.warn(e); }
86003928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
86103928aee4356845252ac6b662d5c72c29903813eJake Slack                }
86203928aee4356845252ac6b662d5c72c29903813eJake Slack            }
86303928aee4356845252ac6b662d5c72c29903813eJake Slack        };
86403928aee4356845252ac6b662d5c72c29903813eJake Slack
86503928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_context==null)
86603928aee4356845252ac6b662d5c72c29903813eJake Slack            load.run();
86703928aee4356845252ac6b662d5c72c29903813eJake Slack        else
86803928aee4356845252ac6b662d5c72c29903813eJake Slack            _context.getContextHandler().handle(load);
86903928aee4356845252ac6b662d5c72c29903813eJake Slack
87003928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_exception.get()!=null)
87103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
87203928aee4356845252ac6b662d5c72c29903813eJake Slack            //if the session could not be restored, take its id out of the pool of currently-in-use
87303928aee4356845252ac6b662d5c72c29903813eJake Slack            //session ids
87403928aee4356845252ac6b662d5c72c29903813eJake Slack            _jdbcSessionIdMgr.removeSession(id);
87503928aee4356845252ac6b662d5c72c29903813eJake Slack            throw _exception.get();
87603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
87703928aee4356845252ac6b662d5c72c29903813eJake Slack
87803928aee4356845252ac6b662d5c72c29903813eJake Slack        return _reference.get();
87903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
88003928aee4356845252ac6b662d5c72c29903813eJake Slack
88103928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
88203928aee4356845252ac6b662d5c72c29903813eJake Slack     * Insert a session into the database.
88303928aee4356845252ac6b662d5c72c29903813eJake Slack     *
88403928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param data
88503928aee4356845252ac6b662d5c72c29903813eJake Slack     * @throws Exception
88603928aee4356845252ac6b662d5c72c29903813eJake Slack     */
88703928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void storeSession (Session session)
88803928aee4356845252ac6b662d5c72c29903813eJake Slack    throws Exception
88903928aee4356845252ac6b662d5c72c29903813eJake Slack    {
89003928aee4356845252ac6b662d5c72c29903813eJake Slack        if (session==null)
89103928aee4356845252ac6b662d5c72c29903813eJake Slack            return;
89203928aee4356845252ac6b662d5c72c29903813eJake Slack
89303928aee4356845252ac6b662d5c72c29903813eJake Slack        //put into the database
89403928aee4356845252ac6b662d5c72c29903813eJake Slack        Connection connection = getConnection();
89503928aee4356845252ac6b662d5c72c29903813eJake Slack        PreparedStatement statement = null;
89603928aee4356845252ac6b662d5c72c29903813eJake Slack        try
89703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
89803928aee4356845252ac6b662d5c72c29903813eJake Slack            String rowId = calculateRowId(session);
89903928aee4356845252ac6b662d5c72c29903813eJake Slack
90003928aee4356845252ac6b662d5c72c29903813eJake Slack            long now = System.currentTimeMillis();
90103928aee4356845252ac6b662d5c72c29903813eJake Slack            connection.setAutoCommit(true);
90203928aee4356845252ac6b662d5c72c29903813eJake Slack            statement = connection.prepareStatement(_jdbcSessionIdMgr._insertSession);
90303928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(1, rowId); //rowId
90403928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(2, session.getId()); //session id
90503928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(3, session.getCanonicalContext()); //context path
90603928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(4, session.getVirtualHost()); //first vhost
90703928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(5, getSessionIdManager().getWorkerName());//my node id
90803928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(6, session.getAccessed());//accessTime
90903928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(7, session.getLastAccessedTime()); //lastAccessTime
91003928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(8, session.getCreationTime()); //time created
91103928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(9, session.getCookieSet());//time cookie was set
91203928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(10, now); //last saved time
91303928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(11, session.getExpiryTime());
91403928aee4356845252ac6b662d5c72c29903813eJake Slack
91503928aee4356845252ac6b662d5c72c29903813eJake Slack            ByteArrayOutputStream baos = new ByteArrayOutputStream();
91603928aee4356845252ac6b662d5c72c29903813eJake Slack            ObjectOutputStream oos = new ObjectOutputStream(baos);
91703928aee4356845252ac6b662d5c72c29903813eJake Slack            oos.writeObject(session.getAttributeMap());
91803928aee4356845252ac6b662d5c72c29903813eJake Slack            byte[] bytes = baos.toByteArray();
91903928aee4356845252ac6b662d5c72c29903813eJake Slack
92003928aee4356845252ac6b662d5c72c29903813eJake Slack            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
92103928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setBinaryStream(12, bais, bytes.length);//attribute map as blob
92203928aee4356845252ac6b662d5c72c29903813eJake Slack
92303928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.executeUpdate();
92403928aee4356845252ac6b662d5c72c29903813eJake Slack            session.setRowId(rowId); //set it on the in-memory data as well as in db
92503928aee4356845252ac6b662d5c72c29903813eJake Slack            session.setLastSaved(now);
92603928aee4356845252ac6b662d5c72c29903813eJake Slack
92703928aee4356845252ac6b662d5c72c29903813eJake Slack
92803928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
92903928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("Stored session "+session);
93003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
93103928aee4356845252ac6b662d5c72c29903813eJake Slack        finally
93203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
93303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (statement!=null)
93403928aee4356845252ac6b662d5c72c29903813eJake Slack            {
93503928aee4356845252ac6b662d5c72c29903813eJake Slack                try { statement.close(); }
93603928aee4356845252ac6b662d5c72c29903813eJake Slack                catch(Exception e) { LOG.warn(e); }
93703928aee4356845252ac6b662d5c72c29903813eJake Slack            }
93803928aee4356845252ac6b662d5c72c29903813eJake Slack
93903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (connection!=null)
94003928aee4356845252ac6b662d5c72c29903813eJake Slack                connection.close();
94103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
94203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
94303928aee4356845252ac6b662d5c72c29903813eJake Slack
94403928aee4356845252ac6b662d5c72c29903813eJake Slack
94503928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
94603928aee4356845252ac6b662d5c72c29903813eJake Slack     * Update data on an existing persisted session.
94703928aee4356845252ac6b662d5c72c29903813eJake Slack     *
94803928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param data the session
94903928aee4356845252ac6b662d5c72c29903813eJake Slack     * @throws Exception
95003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
95103928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void updateSession (Session data)
95203928aee4356845252ac6b662d5c72c29903813eJake Slack    throws Exception
95303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
95403928aee4356845252ac6b662d5c72c29903813eJake Slack        if (data==null)
95503928aee4356845252ac6b662d5c72c29903813eJake Slack            return;
95603928aee4356845252ac6b662d5c72c29903813eJake Slack
95703928aee4356845252ac6b662d5c72c29903813eJake Slack        Connection connection = getConnection();
95803928aee4356845252ac6b662d5c72c29903813eJake Slack        PreparedStatement statement = null;
95903928aee4356845252ac6b662d5c72c29903813eJake Slack        try
96003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
96103928aee4356845252ac6b662d5c72c29903813eJake Slack            long now = System.currentTimeMillis();
96203928aee4356845252ac6b662d5c72c29903813eJake Slack            connection.setAutoCommit(true);
96303928aee4356845252ac6b662d5c72c29903813eJake Slack            statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSession);
96403928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(1, getSessionIdManager().getWorkerName());//my node id
96503928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(2, data.getAccessed());//accessTime
96603928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(3, data.getLastAccessedTime()); //lastAccessTime
96703928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(4, now); //last saved time
96803928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(5, data.getExpiryTime());
96903928aee4356845252ac6b662d5c72c29903813eJake Slack
97003928aee4356845252ac6b662d5c72c29903813eJake Slack            ByteArrayOutputStream baos = new ByteArrayOutputStream();
97103928aee4356845252ac6b662d5c72c29903813eJake Slack            ObjectOutputStream oos = new ObjectOutputStream(baos);
97203928aee4356845252ac6b662d5c72c29903813eJake Slack            oos.writeObject(data.getAttributeMap());
97303928aee4356845252ac6b662d5c72c29903813eJake Slack            byte[] bytes = baos.toByteArray();
97403928aee4356845252ac6b662d5c72c29903813eJake Slack            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
97503928aee4356845252ac6b662d5c72c29903813eJake Slack
97603928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setBinaryStream(6, bais, bytes.length);//attribute map as blob
97703928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(7, data.getRowId()); //rowId
97803928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.executeUpdate();
97903928aee4356845252ac6b662d5c72c29903813eJake Slack
98003928aee4356845252ac6b662d5c72c29903813eJake Slack            data.setLastSaved(now);
98103928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
98203928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("Updated session "+data);
98303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
98403928aee4356845252ac6b662d5c72c29903813eJake Slack        finally
98503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
98603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (statement!=null)
98703928aee4356845252ac6b662d5c72c29903813eJake Slack            {
98803928aee4356845252ac6b662d5c72c29903813eJake Slack                try { statement.close(); }
98903928aee4356845252ac6b662d5c72c29903813eJake Slack                catch(Exception e) { LOG.warn(e); }
99003928aee4356845252ac6b662d5c72c29903813eJake Slack            }
99103928aee4356845252ac6b662d5c72c29903813eJake Slack
99203928aee4356845252ac6b662d5c72c29903813eJake Slack            if (connection!=null)
99303928aee4356845252ac6b662d5c72c29903813eJake Slack                connection.close();
99403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
99503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
99603928aee4356845252ac6b662d5c72c29903813eJake Slack
99703928aee4356845252ac6b662d5c72c29903813eJake Slack
99803928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
99903928aee4356845252ac6b662d5c72c29903813eJake Slack     * Update the node on which the session was last seen to be my node.
100003928aee4356845252ac6b662d5c72c29903813eJake Slack     *
100103928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param data the session
100203928aee4356845252ac6b662d5c72c29903813eJake Slack     * @throws Exception
100303928aee4356845252ac6b662d5c72c29903813eJake Slack     */
100403928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void updateSessionNode (Session data)
100503928aee4356845252ac6b662d5c72c29903813eJake Slack    throws Exception
100603928aee4356845252ac6b662d5c72c29903813eJake Slack    {
100703928aee4356845252ac6b662d5c72c29903813eJake Slack        String nodeId = getSessionIdManager().getWorkerName();
100803928aee4356845252ac6b662d5c72c29903813eJake Slack        Connection connection = getConnection();
100903928aee4356845252ac6b662d5c72c29903813eJake Slack        PreparedStatement statement = null;
101003928aee4356845252ac6b662d5c72c29903813eJake Slack        try
101103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
101203928aee4356845252ac6b662d5c72c29903813eJake Slack            connection.setAutoCommit(true);
101303928aee4356845252ac6b662d5c72c29903813eJake Slack            statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionNode);
101403928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(1, nodeId);
101503928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(2, data.getRowId());
101603928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.executeUpdate();
101703928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.close();
101803928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
101903928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("Updated last node for session id="+data.getId()+", lastNode = "+nodeId);
102003928aee4356845252ac6b662d5c72c29903813eJake Slack        }
102103928aee4356845252ac6b662d5c72c29903813eJake Slack        finally
102203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
102303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (statement!=null)
102403928aee4356845252ac6b662d5c72c29903813eJake Slack            {
102503928aee4356845252ac6b662d5c72c29903813eJake Slack                try { statement.close(); }
102603928aee4356845252ac6b662d5c72c29903813eJake Slack                catch(Exception e) { LOG.warn(e); }
102703928aee4356845252ac6b662d5c72c29903813eJake Slack            }
102803928aee4356845252ac6b662d5c72c29903813eJake Slack
102903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (connection!=null)
103003928aee4356845252ac6b662d5c72c29903813eJake Slack                connection.close();
103103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
103203928aee4356845252ac6b662d5c72c29903813eJake Slack    }
103303928aee4356845252ac6b662d5c72c29903813eJake Slack
103403928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
103503928aee4356845252ac6b662d5c72c29903813eJake Slack     * Persist the time the session was last accessed.
103603928aee4356845252ac6b662d5c72c29903813eJake Slack     *
103703928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param data the session
103803928aee4356845252ac6b662d5c72c29903813eJake Slack     * @throws Exception
103903928aee4356845252ac6b662d5c72c29903813eJake Slack     */
104003928aee4356845252ac6b662d5c72c29903813eJake Slack    private void updateSessionAccessTime (Session data)
104103928aee4356845252ac6b662d5c72c29903813eJake Slack    throws Exception
104203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
104303928aee4356845252ac6b662d5c72c29903813eJake Slack        Connection connection = getConnection();
104403928aee4356845252ac6b662d5c72c29903813eJake Slack        PreparedStatement statement = null;
104503928aee4356845252ac6b662d5c72c29903813eJake Slack        try
104603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
104703928aee4356845252ac6b662d5c72c29903813eJake Slack            long now = System.currentTimeMillis();
104803928aee4356845252ac6b662d5c72c29903813eJake Slack            connection.setAutoCommit(true);
104903928aee4356845252ac6b662d5c72c29903813eJake Slack            statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionAccessTime);
105003928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(1, getSessionIdManager().getWorkerName());
105103928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(2, data.getAccessed());
105203928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(3, data.getLastAccessedTime());
105303928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(4, now);
105403928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setLong(5, data.getExpiryTime());
105503928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(6, data.getRowId());
105603928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.executeUpdate();
105703928aee4356845252ac6b662d5c72c29903813eJake Slack            data.setLastSaved(now);
105803928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.close();
105903928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
106003928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("Updated access time session id="+data.getId());
106103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
106203928aee4356845252ac6b662d5c72c29903813eJake Slack        finally
106303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
106403928aee4356845252ac6b662d5c72c29903813eJake Slack            if (statement!=null)
106503928aee4356845252ac6b662d5c72c29903813eJake Slack            {
106603928aee4356845252ac6b662d5c72c29903813eJake Slack                try { statement.close(); }
106703928aee4356845252ac6b662d5c72c29903813eJake Slack                catch(Exception e) { LOG.warn(e); }
106803928aee4356845252ac6b662d5c72c29903813eJake Slack            }
106903928aee4356845252ac6b662d5c72c29903813eJake Slack
107003928aee4356845252ac6b662d5c72c29903813eJake Slack            if (connection!=null)
107103928aee4356845252ac6b662d5c72c29903813eJake Slack                connection.close();
107203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
107303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
107403928aee4356845252ac6b662d5c72c29903813eJake Slack
107503928aee4356845252ac6b662d5c72c29903813eJake Slack
107603928aee4356845252ac6b662d5c72c29903813eJake Slack
107703928aee4356845252ac6b662d5c72c29903813eJake Slack
107803928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
107903928aee4356845252ac6b662d5c72c29903813eJake Slack     * Delete a session from the database. Should only be called
108003928aee4356845252ac6b662d5c72c29903813eJake Slack     * when the session has been invalidated.
108103928aee4356845252ac6b662d5c72c29903813eJake Slack     *
108203928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param data
108303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @throws Exception
108403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
108503928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void deleteSession (Session data)
108603928aee4356845252ac6b662d5c72c29903813eJake Slack    throws Exception
108703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
108803928aee4356845252ac6b662d5c72c29903813eJake Slack        Connection connection = getConnection();
108903928aee4356845252ac6b662d5c72c29903813eJake Slack        PreparedStatement statement = null;
109003928aee4356845252ac6b662d5c72c29903813eJake Slack        try
109103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
109203928aee4356845252ac6b662d5c72c29903813eJake Slack            connection.setAutoCommit(true);
109303928aee4356845252ac6b662d5c72c29903813eJake Slack            statement = connection.prepareStatement(_jdbcSessionIdMgr._deleteSession);
109403928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.setString(1, data.getRowId());
109503928aee4356845252ac6b662d5c72c29903813eJake Slack            statement.executeUpdate();
109603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (LOG.isDebugEnabled())
109703928aee4356845252ac6b662d5c72c29903813eJake Slack                LOG.debug("Deleted Session "+data);
109803928aee4356845252ac6b662d5c72c29903813eJake Slack        }
109903928aee4356845252ac6b662d5c72c29903813eJake Slack        finally
110003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
110103928aee4356845252ac6b662d5c72c29903813eJake Slack            if (statement!=null)
110203928aee4356845252ac6b662d5c72c29903813eJake Slack            {
110303928aee4356845252ac6b662d5c72c29903813eJake Slack                try { statement.close(); }
110403928aee4356845252ac6b662d5c72c29903813eJake Slack                catch(Exception e) { LOG.warn(e); }
110503928aee4356845252ac6b662d5c72c29903813eJake Slack            }
110603928aee4356845252ac6b662d5c72c29903813eJake Slack
110703928aee4356845252ac6b662d5c72c29903813eJake Slack            if (connection!=null)
110803928aee4356845252ac6b662d5c72c29903813eJake Slack                connection.close();
110903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
111003928aee4356845252ac6b662d5c72c29903813eJake Slack    }
111103928aee4356845252ac6b662d5c72c29903813eJake Slack
111203928aee4356845252ac6b662d5c72c29903813eJake Slack
111303928aee4356845252ac6b662d5c72c29903813eJake Slack
111403928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
111503928aee4356845252ac6b662d5c72c29903813eJake Slack     * Get a connection from the driver.
111603928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return
111703928aee4356845252ac6b662d5c72c29903813eJake Slack     * @throws SQLException
111803928aee4356845252ac6b662d5c72c29903813eJake Slack     */
111903928aee4356845252ac6b662d5c72c29903813eJake Slack    private Connection getConnection ()
112003928aee4356845252ac6b662d5c72c29903813eJake Slack    throws SQLException
112103928aee4356845252ac6b662d5c72c29903813eJake Slack    {
112203928aee4356845252ac6b662d5c72c29903813eJake Slack        return ((JDBCSessionIdManager)getSessionIdManager()).getConnection();
112303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
112403928aee4356845252ac6b662d5c72c29903813eJake Slack
112503928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
112603928aee4356845252ac6b662d5c72c29903813eJake Slack     * Calculate a unique id for this session across the cluster.
112703928aee4356845252ac6b662d5c72c29903813eJake Slack     *
112803928aee4356845252ac6b662d5c72c29903813eJake Slack     * Unique id is composed of: contextpath_virtualhost0_sessionid
112903928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param data
113003928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return
113103928aee4356845252ac6b662d5c72c29903813eJake Slack     */
113203928aee4356845252ac6b662d5c72c29903813eJake Slack    private String calculateRowId (Session data)
113303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
113403928aee4356845252ac6b662d5c72c29903813eJake Slack        String rowId = canonicalize(_context.getContextPath());
113503928aee4356845252ac6b662d5c72c29903813eJake Slack        rowId = rowId + "_" + getVirtualHost(_context);
113603928aee4356845252ac6b662d5c72c29903813eJake Slack        rowId = rowId+"_"+data.getId();
113703928aee4356845252ac6b662d5c72c29903813eJake Slack        return rowId;
113803928aee4356845252ac6b662d5c72c29903813eJake Slack    }
113903928aee4356845252ac6b662d5c72c29903813eJake Slack
114003928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
114103928aee4356845252ac6b662d5c72c29903813eJake Slack     * Get the first virtual host for the context.
114203928aee4356845252ac6b662d5c72c29903813eJake Slack     *
114303928aee4356845252ac6b662d5c72c29903813eJake Slack     * Used to help identify the exact session/contextPath.
114403928aee4356845252ac6b662d5c72c29903813eJake Slack     *
114503928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return 0.0.0.0 if no virtual host is defined
114603928aee4356845252ac6b662d5c72c29903813eJake Slack     */
114703928aee4356845252ac6b662d5c72c29903813eJake Slack    private static String getVirtualHost (ContextHandler.Context context)
114803928aee4356845252ac6b662d5c72c29903813eJake Slack    {
114903928aee4356845252ac6b662d5c72c29903813eJake Slack        String vhost = "0.0.0.0";
115003928aee4356845252ac6b662d5c72c29903813eJake Slack
115103928aee4356845252ac6b662d5c72c29903813eJake Slack        if (context==null)
115203928aee4356845252ac6b662d5c72c29903813eJake Slack            return vhost;
115303928aee4356845252ac6b662d5c72c29903813eJake Slack
115403928aee4356845252ac6b662d5c72c29903813eJake Slack        String [] vhosts = context.getContextHandler().getVirtualHosts();
115503928aee4356845252ac6b662d5c72c29903813eJake Slack        if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
115603928aee4356845252ac6b662d5c72c29903813eJake Slack            return vhost;
115703928aee4356845252ac6b662d5c72c29903813eJake Slack
115803928aee4356845252ac6b662d5c72c29903813eJake Slack        return vhosts[0];
115903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
116003928aee4356845252ac6b662d5c72c29903813eJake Slack
116103928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
116203928aee4356845252ac6b662d5c72c29903813eJake Slack     * Make an acceptable file name from a context path.
116303928aee4356845252ac6b662d5c72c29903813eJake Slack     *
116403928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param path
116503928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return
116603928aee4356845252ac6b662d5c72c29903813eJake Slack     */
116703928aee4356845252ac6b662d5c72c29903813eJake Slack    private static String canonicalize (String path)
116803928aee4356845252ac6b662d5c72c29903813eJake Slack    {
116903928aee4356845252ac6b662d5c72c29903813eJake Slack        if (path==null)
117003928aee4356845252ac6b662d5c72c29903813eJake Slack            return "";
117103928aee4356845252ac6b662d5c72c29903813eJake Slack
117203928aee4356845252ac6b662d5c72c29903813eJake Slack        return path.replace('/', '_').replace('.','_').replace('\\','_');
117303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
117403928aee4356845252ac6b662d5c72c29903813eJake Slack}
1175