2 // ========================================================================
3 // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
20 package org.eclipse.jetty.server.session;
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.InputStream;
25 import java.io.ObjectOutputStream;
26 import java.sql.Connection;
27 import java.sql.PreparedStatement;
28 import java.sql.ResultSet;
29 import java.sql.SQLException;
30 import java.util.ArrayList;
31 import java.util.HashSet;
32 import java.util.Iterator;
35 import java.util.concurrent.ConcurrentHashMap;
36 import java.util.concurrent.TimeUnit;
37 import java.util.concurrent.atomic.AtomicReference;
39 import javax.servlet.http.HttpServletRequest;
40 import org.eclipse.jetty.server.handler.ContextHandler;
41 import org.eclipse.jetty.server.session.JDBCSessionIdManager.SessionTableSchema;
42 import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
43 import org.eclipse.jetty.util.log.Log;
44 import org.eclipse.jetty.util.log.Logger;
49 * SessionManager that persists sessions to a database to enable clustering.
51 * Session data is persisted to the JettySessions table:
53 * rowId (unique in cluster: webapp name/path + virtualhost + sessionId)
54 * contextPath (of the context owning the session)
55 * sessionId (unique in a context)
56 * lastNode (name of node last handled session)
57 * accessTime (time in milliseconds session was accessed)
58 * lastAccessTime (previous time in milliseconds session was accessed)
59 * createTime (time in milliseconds session created)
60 * cookieTime (time in milliseconds session cookie created)
61 * lastSavedTime (last time in milliseconds session access times were saved)
62 * expiryTime (time in milliseconds that the session is due to expire)
65 * As an optimization, to prevent thrashing the database, we do not persist
66 * the accessTime and lastAccessTime every time the session is accessed. Rather,
67 * we write it out every so often. The frequency is controlled by the saveIntervalSec
70 public class JDBCSessionManager extends AbstractSessionManager
72 private static final Logger LOG = Log.getLogger(JDBCSessionManager.class);
74 private ConcurrentHashMap<String, Session> _sessions;
75 protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
76 protected long _saveIntervalSec = 60; //only persist changes to session access times every 60 secs
77 protected SessionTableSchema _sessionTableSchema;
87 public class Session extends MemSession
89 private static final long serialVersionUID = 5208464051134226143L;
92 * If dirty, session needs to be (re)persisted
94 protected boolean _dirty=false;
98 * Time in msec since the epoch that a session cookie was set for this session
100 protected long _cookieSet;
104 * Time in msec since the epoch that the session will expire
106 protected long _expiryTime;
110 * Time in msec since the epoch that the session was last persisted
112 protected long _lastSaved;
116 * Unique identifier of the last node to host the session
118 protected String _lastNode;
122 * Virtual host for context (used to help distinguish 2 sessions with same id on different contexts)
124 protected String _virtualHost;
128 * Unique row in db for session
130 protected String _rowId;
134 * Mangled context name (used to help distinguish 2 sessions with same id on different contexts)
136 protected String _canonicalContext;
140 * Session from a request.
144 protected Session (HttpServletRequest request)
146 super(JDBCSessionManager.this,request);
147 int maxInterval=getMaxInactiveInterval();
148 _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
149 _virtualHost = JDBCSessionManager.getVirtualHost(_context);
150 _canonicalContext = canonicalize(_context.getContextPath());
151 _lastNode = getSessionIdManager().getWorkerName();
156 * Session restored from database
162 protected Session (String sessionId, String rowId, long created, long accessed, long maxInterval)
164 super(JDBCSessionManager.this, created, accessed, sessionId);
166 super.setMaxInactiveInterval((int)maxInterval); //restore the session's previous inactivity interval setting
167 _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
171 protected synchronized String getRowId()
176 protected synchronized void setRowId(String rowId)
181 public synchronized void setVirtualHost (String vhost)
186 public synchronized String getVirtualHost ()
191 public synchronized long getLastSaved ()
196 public synchronized void setLastSaved (long time)
201 public synchronized void setExpiryTime (long time)
206 public synchronized long getExpiryTime ()
212 public synchronized void setCanonicalContext(String str)
214 _canonicalContext=str;
217 public synchronized String getCanonicalContext ()
219 return _canonicalContext;
222 public void setCookieSet (long ms)
227 public synchronized long getCookieSet ()
232 public synchronized void setLastNode (String node)
237 public synchronized String getLastNode ()
243 public void setAttribute (String name, Object value)
245 Object old = changeAttribute(name, value);
246 if (value == null && old == null)
247 return; //if same as remove attribute but attribute was already removed, no change
253 public void removeAttribute (String name)
255 Object old = changeAttribute(name, null);
256 if (old != null) //only dirty if there was a previous value
261 protected void cookieSet()
263 _cookieSet = getAccessed();
268 * Called by SessionHandler on inbound request and the session already exists in this node's memory.
270 * @see org.eclipse.jetty.server.session.AbstractSession#access(long)
273 protected boolean access(long time)
277 if (super.access(time))
279 int maxInterval=getMaxInactiveInterval();
280 _expiryTime = (maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
292 * Change the max idle time for this session. This recalculates the expiry time.
293 * @see org.eclipse.jetty.server.session.AbstractSession#setMaxInactiveInterval(int)
296 public void setMaxInactiveInterval(int secs)
300 super.setMaxInactiveInterval(secs);
301 int maxInterval=getMaxInactiveInterval();
302 _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
303 //force the session to be written out right now
306 updateSessionAccessTime(this);
310 LOG.warn("Problem saving changed max idle time for session "+ this, e);
318 * @see org.eclipse.jetty.server.session.AbstractSession#complete()
321 protected void complete()
332 //The session attributes have changed, write to the db, ensuring
333 //http passivation/activation listeners called
336 else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
338 updateSessionAccessTime(this);
344 LOG.warn("Problem persisting changed session data id="+getId(), e);
353 protected void save() throws Exception
368 protected void save (boolean reactivate) throws Exception
374 //The session attributes have changed, write to the db, ensuring
375 //http passivation/activation listeners called
386 protected void timeout() throws IllegalStateException
388 if (LOG.isDebugEnabled())
389 LOG.debug("Timing out session id="+getClusterId());
395 public String toString ()
397 return "Session rowId="+_rowId+",id="+getId()+",lastNode="+_lastNode+
398 ",created="+getCreationTime()+",accessed="+getAccessed()+
399 ",lastAccessed="+getLastAccessedTime()+",cookieSet="+_cookieSet+
400 ",maxInterval="+getMaxInactiveInterval()+",lastSaved="+_lastSaved+",expiry="+_expiryTime;
408 * Set the time in seconds which is the interval between
409 * saving the session access time to the database.
411 * This is an optimization that prevents the database from
412 * being overloaded when a session is accessed very frequently.
414 * On session exit, if the session attributes have NOT changed,
415 * the time at which we last saved the accessed
416 * time is compared to the current accessed time. If the interval
417 * is at least saveIntervalSecs, then the access time will be
418 * persisted to the database.
420 * If any session attribute does change, then the attributes and
421 * the accessed time are persisted.
425 public void setSaveInterval (long sec)
427 _saveIntervalSec=sec;
430 public long getSaveInterval ()
432 return _saveIntervalSec;
438 * A method that can be implemented in subclasses to support
439 * distributed caching of sessions. This method will be
440 * called whenever the session is written to the database
441 * because the session data has changed.
443 * This could be used eg with a JMS backplane to notify nodes
444 * that the session has changed and to delete the session from
445 * the node's cache, and re-read it from the database.
448 public void cacheInvalidate (Session session)
455 * A session has been requested by its id on this node.
457 * Load the session by id AND context path from the database.
458 * Multiple contexts may share the same session id (due to dispatching)
459 * but they CANNOT share the same contents.
461 * Check if last node id is my node id, if so, then the session we have
462 * in memory cannot be stale. If another node used the session last, then
463 * we need to refresh from the db.
465 * NOTE: this method will go to the database, so if you only want to check
466 * for the existence of a Session in memory, use _sessions.get(id) instead.
468 * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
471 public Session getSession(String idInCluster)
473 Session session = null;
477 Session memSession = (Session)_sessions.get(idInCluster);
479 //check if we need to reload the session -
480 //as an optimization, don't reload on every access
481 //to reduce the load on the database. This introduces a window of
482 //possibility that the node may decide that the session is local to it,
483 //when the session has actually been live on another node, and then
484 //re-migrated to this node. This should be an extremely rare occurrence,
485 //as load-balancers are generally well-behaved and consistently send
486 //sessions to the same node, changing only iff that node fails.
487 //Session data = null;
488 long now = System.currentTimeMillis();
489 if (LOG.isDebugEnabled())
491 if (memSession==null)
492 LOG.debug("getSession("+idInCluster+"): not in session map,"+
494 " lastSaved="+(memSession==null?0:memSession._lastSaved)+
495 " interval="+(_saveIntervalSec * 1000L));
497 LOG.debug("getSession("+idInCluster+"): in session map, "+
498 " hashcode="+memSession.hashCode()+
500 " lastSaved="+(memSession==null?0:memSession._lastSaved)+
501 " interval="+(_saveIntervalSec * 1000L)+
502 " lastNode="+memSession._lastNode+
503 " thisNode="+getSessionIdManager().getWorkerName()+
504 " difference="+(now - memSession._lastSaved));
509 if (memSession==null)
511 if (LOG.isDebugEnabled())
512 LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
513 session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
515 else if ((now - memSession._lastSaved) >= (_saveIntervalSec * 1000L))
517 if (LOG.isDebugEnabled())
518 LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
519 session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
523 if (LOG.isDebugEnabled())
524 LOG.debug("getSession("+idInCluster+"): session in session map");
525 session = memSession;
530 LOG.warn("Unable to load session "+idInCluster, e);
535 //If we have a session
538 //If the session was last used on a different node, or session doesn't exist on this node
539 if (!session.getLastNode().equals(getSessionIdManager().getWorkerName()) || memSession==null)
541 //if session doesn't expire, or has not already expired, update it and put it in this nodes' memory
542 if (session._expiryTime <= 0 || session._expiryTime > now)
544 if (LOG.isDebugEnabled())
545 LOG.debug("getSession("+idInCluster+"): lastNode="+session.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
547 session.setLastNode(getSessionIdManager().getWorkerName());
548 _sessions.put(idInCluster, session);
553 updateSessionNode(session);
554 session.didActivate();
558 LOG.warn("Unable to update freshly loaded session "+idInCluster, e);
564 if (LOG.isDebugEnabled())
565 LOG.debug("getSession ({}): Session has expired", idInCluster);
566 //ensure that the session id for the expired session is deleted so that a new session with the
567 //same id cannot be created (because the idInUse() test would succeed)
568 _jdbcSessionIdMgr.removeSession(idInCluster);
575 //the session loaded from the db and the one in memory are the same, so keep using the one in memory
576 session = memSession;
577 if (LOG.isDebugEnabled())
578 LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
583 //No session in db with matching id and context path.
584 LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
593 * Get the number of sessions.
595 * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSessions()
598 public int getSessions()
600 return _sessions.size();
605 * Start the session manager.
607 * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
610 public void doStart() throws Exception
612 if (_sessionIdManager==null)
613 throw new IllegalStateException("No session id manager defined");
615 _jdbcSessionIdMgr = (JDBCSessionIdManager)_sessionIdManager;
616 _sessionTableSchema = _jdbcSessionIdMgr.getSessionTableSchema();
618 _sessions = new ConcurrentHashMap<String, Session>();
625 * Stop the session manager.
627 * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop()
630 public void doStop() throws Exception
638 protected void shutdownSessions()
640 //Save the current state of all of our sessions,
641 //do NOT delete them (so other nodes can manage them)
642 long gracefulStopMs = getContextHandler().getServer().getStopTimeout();
644 if (gracefulStopMs > 0)
645 stopTime = System.nanoTime() + (TimeUnit.NANOSECONDS.convert(gracefulStopMs, TimeUnit.MILLISECONDS));
647 ArrayList<Session> sessions = (_sessions == null? new ArrayList<Session>() :new ArrayList<Session>(_sessions.values()) );
649 // loop while there are sessions, and while there is stop time remaining, or if no stop time, just 1 loop
650 while (sessions.size() > 0 && ((stopTime > 0 && (System.nanoTime() < stopTime)) || (stopTime == 0)))
652 for (Session session : sessions)
662 _sessions.remove(session.getClusterId());
665 //check if we should terminate our loop if we're not using the stop timer
669 // Get any sessions that were added by other requests during processing and go around the loop again
670 sessions=new ArrayList<Session>(_sessions.values());
677 * @see org.eclipse.jetty.server.SessionManager#renewSessionId(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
679 public void renewSessionId (String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
681 Session session = null;
684 session = (Session)_sessions.remove(oldClusterId);
687 synchronized (session)
689 session.setClusterId(newClusterId); //update ids
690 session.setNodeId(newNodeId);
691 _sessions.put(newClusterId, session); //put it into list in memory
692 updateSession(session); //update database
701 super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
707 * Invalidate a session.
711 protected void invalidateSession (String idInCluster)
713 Session session = (Session)_sessions.get(idInCluster);
717 session.invalidate();
722 * Delete an existing session, both from the in-memory map and
725 * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
728 protected boolean removeSession(String idInCluster)
730 Session session = (Session)_sessions.remove(idInCluster);
734 deleteSession(session);
738 LOG.warn("Problem deleting session id="+idInCluster, e);
740 return session!=null;
745 * Add a newly created session to our in-memory list for this node and persist it.
747 * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
750 protected void addSession(AbstractSession session)
755 _sessions.put(session.getClusterId(), (Session)session);
759 synchronized (session)
761 session.willPassivate();
762 storeSession(((JDBCSessionManager.Session)session));
763 session.didActivate();
768 LOG.warn("Unable to store new session id="+session.getId() , e);
774 * Make a new Session.
776 * @see org.eclipse.jetty.server.session.AbstractSessionManager#newSession(javax.servlet.http.HttpServletRequest)
779 protected AbstractSession newSession(HttpServletRequest request)
781 return new Session(request);
793 protected AbstractSession newSession (String sessionId, String rowId, long created, long accessed, long maxInterval)
795 return new Session(sessionId, rowId, created, accessed, maxInterval);
798 /* ------------------------------------------------------------ */
799 /** Remove session from manager
800 * @param session The session to remove
801 * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
802 * {@link SessionIdManager#invalidateAll(String)} should be called.
805 public boolean removeSession(AbstractSession session, boolean invalidate)
807 // Remove session from context and global maps
808 boolean removed = super.removeSession(session, invalidate);
814 session.willPassivate();
823 * Expire any Sessions we have in memory matching the list of
824 * expired Session ids.
828 protected Set<String> expire (Set<String> sessionIds)
830 //don't attempt to scavenge if we are shutting down
831 if (isStopping() || isStopped())
835 Thread thread=Thread.currentThread();
836 ClassLoader old_loader=thread.getContextClassLoader();
838 Set<String> successfullyExpiredIds = new HashSet<String>();
841 Iterator<?> itor = sessionIds.iterator();
842 while (itor.hasNext())
844 String sessionId = (String)itor.next();
845 if (LOG.isDebugEnabled())
846 LOG.debug("Expiring session id "+sessionId);
848 Session session = (Session)_sessions.get(sessionId);
850 //if session is not in our memory, then fetch from db so we can call the usual listeners on it
853 if (LOG.isDebugEnabled())LOG.debug("Force loading session id "+sessionId);
854 session = loadSession(sessionId, canonicalize(_context.getContextPath()), getVirtualHost(_context));
857 //loaded an expired session last managed on this node for this context, add it to the list so we can
858 //treat it like a normal expired session
859 _sessions.put(session.getClusterId(), session);
863 if (LOG.isDebugEnabled())
864 LOG.debug("Unrecognized session id="+sessionId);
872 successfullyExpiredIds.add(session.getClusterId());
875 return successfullyExpiredIds;
879 LOG.warn("Problem expiring sessions", t);
880 return successfullyExpiredIds;
884 thread.setContextClassLoader(old_loader);
890 * Load a session from the database
892 * @return the session data that was loaded
895 protected Session loadSession (final String id, final String canonicalContextPath, final String vhost)
898 final AtomicReference<Session> _reference = new AtomicReference<Session>();
899 final AtomicReference<Exception> _exception = new AtomicReference<Exception>();
900 Runnable load = new Runnable()
903 * @see java.lang.Runnable#run()
905 @SuppressWarnings("unchecked")
908 try (Connection connection = getConnection();
909 PreparedStatement statement = _sessionTableSchema.getLoadStatement(connection, id, canonicalContextPath, vhost);
910 ResultSet result = statement.executeQuery())
912 Session session = null;
915 long maxInterval = result.getLong(_sessionTableSchema.getMaxIntervalColumn());
916 if (maxInterval == JDBCSessionIdManager.MAX_INTERVAL_NOT_SET)
918 maxInterval = getMaxInactiveInterval(); //if value not saved for maxInactiveInterval, use current value from sessionmanager
920 session = (Session)newSession(id, result.getString(_sessionTableSchema.getRowIdColumn()),
921 result.getLong(_sessionTableSchema.getCreateTimeColumn()),
922 result.getLong(_sessionTableSchema.getAccessTimeColumn()),
924 session.setCookieSet(result.getLong(_sessionTableSchema.getCookieTimeColumn()));
925 session.setLastAccessedTime(result.getLong(_sessionTableSchema.getLastAccessTimeColumn()));
926 session.setLastNode(result.getString(_sessionTableSchema.getLastNodeColumn()));
927 session.setLastSaved(result.getLong(_sessionTableSchema.getLastSavedTimeColumn()));
928 session.setExpiryTime(result.getLong(_sessionTableSchema.getExpiryTimeColumn()));
929 session.setCanonicalContext(result.getString(_sessionTableSchema.getContextPathColumn()));
930 session.setVirtualHost(result.getString(_sessionTableSchema.getVirtualHostColumn()));
932 try (InputStream is = ((JDBCSessionIdManager)getSessionIdManager())._dbAdaptor.getBlobInputStream(result, _sessionTableSchema.getMapColumn());
933 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
935 Object o = ois.readObject();
936 session.addAttributes((Map<String,Object>)o);
939 if (LOG.isDebugEnabled())
940 LOG.debug("LOADED session "+session);
943 if (LOG.isDebugEnabled())
944 LOG.debug("Failed to load session "+id);
945 _reference.set(session);
957 _context.getContextHandler().handle(load);
959 if (_exception.get()!=null)
961 //if the session could not be restored, take its id out of the pool of currently-in-use
963 _jdbcSessionIdMgr.removeSession(id);
964 throw _exception.get();
967 return _reference.get();
971 * Insert a session into the database.
976 protected void storeSession (Session session)
982 //put into the database
983 try (Connection connection = getConnection();
984 PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._insertSession))
986 String rowId = calculateRowId(session);
988 long now = System.currentTimeMillis();
989 connection.setAutoCommit(true);
990 statement.setString(1, rowId); //rowId
991 statement.setString(2, session.getClusterId()); //session id
992 statement.setString(3, session.getCanonicalContext()); //context path
993 statement.setString(4, session.getVirtualHost()); //first vhost
994 statement.setString(5, getSessionIdManager().getWorkerName());//my node id
995 statement.setLong(6, session.getAccessed());//accessTime
996 statement.setLong(7, session.getLastAccessedTime()); //lastAccessTime
997 statement.setLong(8, session.getCreationTime()); //time created
998 statement.setLong(9, session.getCookieSet());//time cookie was set
999 statement.setLong(10, now); //last saved time
1000 statement.setLong(11, session.getExpiryTime());
1001 statement.setLong(12, session.getMaxInactiveInterval());
1003 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1004 ObjectOutputStream oos = new ObjectOutputStream(baos);
1005 oos.writeObject(session.getAttributeMap());
1007 byte[] bytes = baos.toByteArray();
1009 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
1010 statement.setBinaryStream(13, bais, bytes.length);//attribute map as blob
1013 statement.executeUpdate();
1014 session.setRowId(rowId); //set it on the in-memory data as well as in db
1015 session.setLastSaved(now);
1017 if (LOG.isDebugEnabled())
1018 LOG.debug("Stored session "+session);
1023 * Update data on an existing persisted session.
1025 * @param data the session
1028 protected void updateSession (Session data)
1034 try (Connection connection = getConnection();
1035 PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSession))
1037 long now = System.currentTimeMillis();
1038 connection.setAutoCommit(true);
1039 statement.setString(1, data.getClusterId());
1040 statement.setString(2, getSessionIdManager().getWorkerName());//my node id
1041 statement.setLong(3, data.getAccessed());//accessTime
1042 statement.setLong(4, data.getLastAccessedTime()); //lastAccessTime
1043 statement.setLong(5, now); //last saved time
1044 statement.setLong(6, data.getExpiryTime());
1045 statement.setLong(7, data.getMaxInactiveInterval());
1047 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1048 ObjectOutputStream oos = new ObjectOutputStream(baos);
1049 oos.writeObject(data.getAttributeMap());
1051 byte[] bytes = baos.toByteArray();
1052 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
1054 statement.setBinaryStream(8, bais, bytes.length);//attribute map as blob
1055 statement.setString(9, data.getRowId()); //rowId
1056 statement.executeUpdate();
1058 data.setLastSaved(now);
1060 if (LOG.isDebugEnabled())
1061 LOG.debug("Updated session "+data);
1066 * Update the node on which the session was last seen to be my node.
1068 * @param data the session
1071 protected void updateSessionNode (Session data)
1074 String nodeId = getSessionIdManager().getWorkerName();
1075 try (Connection connection = getConnection();
1076 PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionNode))
1078 connection.setAutoCommit(true);
1079 statement.setString(1, nodeId);
1080 statement.setString(2, data.getRowId());
1081 statement.executeUpdate();
1083 if (LOG.isDebugEnabled())
1084 LOG.debug("Updated last node for session id="+data.getId()+", lastNode = "+nodeId);
1088 * Persist the time the session was last accessed.
1090 * @param data the session
1093 private void updateSessionAccessTime (Session data)
1096 try (Connection connection = getConnection();
1097 PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionAccessTime))
1099 long now = System.currentTimeMillis();
1100 connection.setAutoCommit(true);
1101 statement.setString(1, getSessionIdManager().getWorkerName());
1102 statement.setLong(2, data.getAccessed());
1103 statement.setLong(3, data.getLastAccessedTime());
1104 statement.setLong(4, now);
1105 statement.setLong(5, data.getExpiryTime());
1106 statement.setLong(6, data.getMaxInactiveInterval());
1107 statement.setString(7, data.getRowId());
1109 statement.executeUpdate();
1110 data.setLastSaved(now);
1112 if (LOG.isDebugEnabled())
1113 LOG.debug("Updated access time session id="+data.getId()+" with lastsaved="+data.getLastSaved());
1120 * Delete a session from the database. Should only be called
1121 * when the session has been invalidated.
1126 protected void deleteSession (Session data)
1129 try (Connection connection = getConnection();
1130 PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._deleteSession))
1132 connection.setAutoCommit(true);
1133 statement.setString(1, data.getRowId());
1134 statement.executeUpdate();
1135 if (LOG.isDebugEnabled())
1136 LOG.debug("Deleted Session "+data);
1143 * Get a connection from the driver.
1145 * @throws SQLException
1147 private Connection getConnection ()
1150 return ((JDBCSessionIdManager)getSessionIdManager()).getConnection();
1154 * Calculate a unique id for this session across the cluster.
1156 * Unique id is composed of: contextpath_virtualhost0_sessionid
1160 private String calculateRowId (Session data)
1162 String rowId = canonicalize(_context.getContextPath());
1163 rowId = rowId + "_" + getVirtualHost(_context);
1164 rowId = rowId+"_"+data.getId();
1169 * Get the first virtual host for the context.
1171 * Used to help identify the exact session/contextPath.
1173 * @return 0.0.0.0 if no virtual host is defined
1175 private static String getVirtualHost (ContextHandler.Context context)
1177 String vhost = "0.0.0.0";
1182 String [] vhosts = context.getContextHandler().getVirtualHosts();
1183 if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
1190 * Make an acceptable file name from a context path.
1195 private static String canonicalize (String path)
1200 return path.replace('/', '_').replace('.','_').replace('\\','_');