2 // ========================================================================
3 // Copyright (c) 1995-2014 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 javax.servlet.http.HttpSessionEvent;
41 import javax.servlet.http.HttpSessionListener;
43 import org.eclipse.jetty.server.SessionIdManager;
44 import org.eclipse.jetty.server.handler.ContextHandler;
45 import org.eclipse.jetty.server.session.JDBCSessionIdManager.SessionTableSchema;
46 import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
47 import org.eclipse.jetty.util.log.Log;
48 import org.eclipse.jetty.util.log.Logger;
53 * SessionManager that persists sessions to a database to enable clustering.
55 * Session data is persisted to the JettySessions table:
57 * rowId (unique in cluster: webapp name/path + virtualhost + sessionId)
58 * contextPath (of the context owning the session)
59 * sessionId (unique in a context)
60 * lastNode (name of node last handled session)
61 * accessTime (time in milliseconds session was accessed)
62 * lastAccessTime (previous time in milliseconds session was accessed)
63 * createTime (time in milliseconds session created)
64 * cookieTime (time in milliseconds session cookie created)
65 * lastSavedTime (last time in milliseconds session access times were saved)
66 * expiryTime (time in milliseconds that the session is due to expire)
69 * As an optimization, to prevent thrashing the database, we do not persist
70 * the accessTime and lastAccessTime every time the session is accessed. Rather,
71 * we write it out every so often. The frequency is controlled by the saveIntervalSec
74 public class JDBCSessionManager extends AbstractSessionManager
76 private static final Logger LOG = Log.getLogger(JDBCSessionManager.class);
78 private ConcurrentHashMap<String, Session> _sessions;
79 protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
80 protected long _saveIntervalSec = 60; //only persist changes to session access times every 60 secs
81 protected SessionTableSchema _sessionTableSchema;
91 public class Session extends MemSession
93 private static final long serialVersionUID = 5208464051134226143L;
96 * If dirty, session needs to be (re)persisted
98 protected boolean _dirty=false;
102 * Time in msec since the epoch that a session cookie was set for this session
104 protected long _cookieSet;
108 * Time in msec since the epoch that the session will expire
110 protected long _expiryTime;
114 * Time in msec since the epoch that the session was last persisted
116 protected long _lastSaved;
120 * Unique identifier of the last node to host the session
122 protected String _lastNode;
126 * Virtual host for context (used to help distinguish 2 sessions with same id on different contexts)
128 protected String _virtualHost;
132 * Unique row in db for session
134 protected String _rowId;
138 * Mangled context name (used to help distinguish 2 sessions with same id on different contexts)
140 protected String _canonicalContext;
144 * Session from a request.
148 protected Session (HttpServletRequest request)
150 super(JDBCSessionManager.this,request);
151 int maxInterval=getMaxInactiveInterval();
152 _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
153 _virtualHost = JDBCSessionManager.getVirtualHost(_context);
154 _canonicalContext = canonicalize(_context.getContextPath());
155 _lastNode = getSessionIdManager().getWorkerName();
160 * Session restored from database
166 protected Session (String sessionId, String rowId, long created, long accessed, long maxInterval)
168 super(JDBCSessionManager.this, created, accessed, sessionId);
170 super.setMaxInactiveInterval((int)maxInterval); //restore the session's previous inactivity interval setting
171 _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
175 protected synchronized String getRowId()
180 protected synchronized void setRowId(String rowId)
185 public synchronized void setVirtualHost (String vhost)
190 public synchronized String getVirtualHost ()
195 public synchronized long getLastSaved ()
200 public synchronized void setLastSaved (long time)
205 public synchronized void setExpiryTime (long time)
210 public synchronized long getExpiryTime ()
216 public synchronized void setCanonicalContext(String str)
218 _canonicalContext=str;
221 public synchronized String getCanonicalContext ()
223 return _canonicalContext;
226 public void setCookieSet (long ms)
231 public synchronized long getCookieSet ()
236 public synchronized void setLastNode (String node)
241 public synchronized String getLastNode ()
247 public void setAttribute (String name, Object value)
249 Object old = changeAttribute(name, value);
250 if (value == null && old == null)
251 return; //if same as remove attribute but attribute was already removed, no change
257 public void removeAttribute (String name)
259 Object old = changeAttribute(name, null);
260 if (old != null) //only dirty if there was a previous value
265 protected void cookieSet()
267 _cookieSet = getAccessed();
272 * Called by SessionHandler on inbound request and the session already exists in this node's memory.
274 * @see org.eclipse.jetty.server.session.AbstractSession#access(long)
277 protected boolean access(long time)
281 if (super.access(time))
283 int maxInterval=getMaxInactiveInterval();
284 _expiryTime = (maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
296 * Change the max idle time for this session. This recalculates the expiry time.
297 * @see org.eclipse.jetty.server.session.AbstractSession#setMaxInactiveInterval(int)
300 public void setMaxInactiveInterval(int secs)
304 super.setMaxInactiveInterval(secs);
305 int maxInterval=getMaxInactiveInterval();
306 _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
307 //force the session to be written out right now
310 updateSessionAccessTime(this);
314 LOG.warn("Problem saving changed max idle time for session "+ this, e);
322 * @see org.eclipse.jetty.server.session.AbstractSession#complete()
325 protected void complete()
336 //The session attributes have changed, write to the db, ensuring
337 //http passivation/activation listeners called
340 else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
342 updateSessionAccessTime(this);
348 LOG.warn("Problem persisting changed session data id="+getId(), e);
357 protected void save() throws Exception
372 protected void save (boolean reactivate) throws Exception
378 //The session attributes have changed, write to the db, ensuring
379 //http passivation/activation listeners called
390 protected void timeout() throws IllegalStateException
392 if (LOG.isDebugEnabled())
393 LOG.debug("Timing out session id="+getClusterId());
399 public String toString ()
401 return "Session rowId="+_rowId+",id="+getId()+",lastNode="+_lastNode+
402 ",created="+getCreationTime()+",accessed="+getAccessed()+
403 ",lastAccessed="+getLastAccessedTime()+",cookieSet="+_cookieSet+
404 ",maxInterval="+getMaxInactiveInterval()+",lastSaved="+_lastSaved+",expiry="+_expiryTime;
412 * Set the time in seconds which is the interval between
413 * saving the session access time to the database.
415 * This is an optimization that prevents the database from
416 * being overloaded when a session is accessed very frequently.
418 * On session exit, if the session attributes have NOT changed,
419 * the time at which we last saved the accessed
420 * time is compared to the current accessed time. If the interval
421 * is at least saveIntervalSecs, then the access time will be
422 * persisted to the database.
424 * If any session attribute does change, then the attributes and
425 * the accessed time are persisted.
429 public void setSaveInterval (long sec)
431 _saveIntervalSec=sec;
434 public long getSaveInterval ()
436 return _saveIntervalSec;
442 * A method that can be implemented in subclasses to support
443 * distributed caching of sessions. This method will be
444 * called whenever the session is written to the database
445 * because the session data has changed.
447 * This could be used eg with a JMS backplane to notify nodes
448 * that the session has changed and to delete the session from
449 * the node's cache, and re-read it from the database.
452 public void cacheInvalidate (Session session)
459 * A session has been requested by its id on this node.
461 * Load the session by id AND context path from the database.
462 * Multiple contexts may share the same session id (due to dispatching)
463 * but they CANNOT share the same contents.
465 * Check if last node id is my node id, if so, then the session we have
466 * in memory cannot be stale. If another node used the session last, then
467 * we need to refresh from the db.
469 * NOTE: this method will go to the database, so if you only want to check
470 * for the existence of a Session in memory, use _sessions.get(id) instead.
472 * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
475 public Session getSession(String idInCluster)
477 Session session = null;
481 Session memSession = (Session)_sessions.get(idInCluster);
483 //check if we need to reload the session -
484 //as an optimization, don't reload on every access
485 //to reduce the load on the database. This introduces a window of
486 //possibility that the node may decide that the session is local to it,
487 //when the session has actually been live on another node, and then
488 //re-migrated to this node. This should be an extremely rare occurrence,
489 //as load-balancers are generally well-behaved and consistently send
490 //sessions to the same node, changing only iff that node fails.
491 //Session data = null;
492 long now = System.currentTimeMillis();
493 if (LOG.isDebugEnabled())
495 if (memSession==null)
496 LOG.debug("getSession("+idInCluster+"): not in session map,"+
498 " lastSaved="+(memSession==null?0:memSession._lastSaved)+
499 " interval="+(_saveIntervalSec * 1000L));
501 LOG.debug("getSession("+idInCluster+"): in session map, "+
502 " hashcode="+memSession.hashCode()+
504 " lastSaved="+(memSession==null?0:memSession._lastSaved)+
505 " interval="+(_saveIntervalSec * 1000L)+
506 " lastNode="+memSession._lastNode+
507 " thisNode="+getSessionIdManager().getWorkerName()+
508 " difference="+(now - memSession._lastSaved));
513 if (memSession==null)
515 LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
516 session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
518 else if ((now - memSession._lastSaved) >= (_saveIntervalSec * 1000L))
520 LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
521 session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
525 LOG.debug("getSession("+idInCluster+"): session in session map");
526 session = memSession;
531 LOG.warn("Unable to load session "+idInCluster, e);
536 //If we have a session
539 //If the session was last used on a different node, or session doesn't exist on this node
540 if (!session.getLastNode().equals(getSessionIdManager().getWorkerName()) || memSession==null)
542 //if session doesn't expire, or has not already expired, update it and put it in this nodes' memory
543 if (session._expiryTime <= 0 || session._expiryTime > now)
545 if (LOG.isDebugEnabled())
546 LOG.debug("getSession("+idInCluster+"): lastNode="+session.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
548 session.setLastNode(getSessionIdManager().getWorkerName());
549 _sessions.put(idInCluster, session);
554 updateSessionNode(session);
555 session.didActivate();
559 LOG.warn("Unable to update freshly loaded session "+idInCluster, e);
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 LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
582 //No session in db with matching id and context path.
583 LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
592 * Get the number of sessions.
594 * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSessions()
597 public int getSessions()
599 return _sessions.size();
604 * Start the session manager.
606 * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
609 public void doStart() throws Exception
611 if (_sessionIdManager==null)
612 throw new IllegalStateException("No session id manager defined");
614 _jdbcSessionIdMgr = (JDBCSessionIdManager)_sessionIdManager;
615 _sessionTableSchema = _jdbcSessionIdMgr.getSessionTableSchema();
617 _sessions = new ConcurrentHashMap<String, Session>();
624 * Stop the session manager.
626 * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop()
629 public void doStop() throws Exception
637 protected void shutdownSessions()
639 //Save the current state of all of our sessions,
640 //do NOT delete them (so other nodes can manage them)
641 long gracefulStopMs = getContextHandler().getServer().getStopTimeout();
643 if (gracefulStopMs > 0)
644 stopTime = System.nanoTime() + (TimeUnit.NANOSECONDS.convert(gracefulStopMs, TimeUnit.MILLISECONDS));
646 ArrayList<Session> sessions = (_sessions == null? new ArrayList<Session>() :new ArrayList<Session>(_sessions.values()) );
648 // loop while there are sessions, and while there is stop time remaining, or if no stop time, just 1 loop
649 while (sessions.size() > 0 && ((stopTime > 0 && (System.nanoTime() < stopTime)) || (stopTime == 0)))
651 for (Session session : sessions)
661 _sessions.remove(session.getClusterId());
664 //check if we should terminate our loop if we're not using the stop timer
668 // Get any sessions that were added by other requests during processing and go around the loop again
669 sessions=new ArrayList<Session>(_sessions.values());
676 * @see org.eclipse.jetty.server.SessionManager#renewSessionId(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
678 public void renewSessionId (String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
680 Session session = null;
683 session = (Session)_sessions.remove(oldClusterId);
686 synchronized (session)
688 session.setClusterId(newClusterId); //update ids
689 session.setNodeId(newNodeId);
690 _sessions.put(newClusterId, session); //put it into list in memory
691 updateSession(session); //update database
700 super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
706 * Invalidate a session.
710 protected void invalidateSession (String idInCluster)
712 Session session = (Session)_sessions.get(idInCluster);
716 session.invalidate();
721 * Delete an existing session, both from the in-memory map and
724 * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
727 protected boolean removeSession(String idInCluster)
729 Session session = (Session)_sessions.remove(idInCluster);
733 deleteSession(session);
737 LOG.warn("Problem deleting session id="+idInCluster, e);
739 return session!=null;
744 * Add a newly created session to our in-memory list for this node and persist it.
746 * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
749 protected void addSession(AbstractSession session)
754 _sessions.put(session.getClusterId(), (Session)session);
758 synchronized (session)
760 session.willPassivate();
761 storeSession(((JDBCSessionManager.Session)session));
762 session.didActivate();
767 LOG.warn("Unable to store new session id="+session.getId() , e);
773 * Make a new Session.
775 * @see org.eclipse.jetty.server.session.AbstractSessionManager#newSession(javax.servlet.http.HttpServletRequest)
778 protected AbstractSession newSession(HttpServletRequest request)
780 return new Session(request);
792 protected AbstractSession newSession (String sessionId, String rowId, long created, long accessed, long maxInterval)
794 return new Session(sessionId, rowId, created, accessed, maxInterval);
797 /* ------------------------------------------------------------ */
798 /** Remove session from manager
799 * @param session The session to remove
800 * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
801 * {@link SessionIdManager#invalidateAll(String)} should be called.
804 public boolean removeSession(AbstractSession session, boolean invalidate)
806 // Remove session from context and global maps
807 boolean removed = super.removeSession(session, invalidate);
813 session.willPassivate();
822 * Expire any Sessions we have in memory matching the list of
823 * expired Session ids.
827 protected Set<String> expire (Set<String> sessionIds)
829 //don't attempt to scavenge if we are shutting down
830 if (isStopping() || isStopped())
834 Thread thread=Thread.currentThread();
835 ClassLoader old_loader=thread.getContextClassLoader();
837 Set<String> successfullyExpiredIds = new HashSet<String>();
840 Iterator<?> itor = sessionIds.iterator();
841 while (itor.hasNext())
843 String sessionId = (String)itor.next();
844 if (LOG.isDebugEnabled())
845 LOG.debug("Expiring session id "+sessionId);
847 Session session = (Session)_sessions.get(sessionId);
849 //if session is not in our memory, then fetch from db so we can call the usual listeners on it
852 if (LOG.isDebugEnabled())LOG.debug("Force loading session id "+sessionId);
853 session = loadSession(sessionId, canonicalize(_context.getContextPath()), getVirtualHost(_context));
856 //loaded an expired session last managed on this node for this context, add it to the list so we can
857 //treat it like a normal expired session
858 _sessions.put(session.getClusterId(), session);
862 if (LOG.isDebugEnabled())
863 LOG.debug("Unrecognized session id="+sessionId);
871 successfullyExpiredIds.add(session.getClusterId());
874 return successfullyExpiredIds;
878 LOG.warn("Problem expiring sessions", t);
879 return successfullyExpiredIds;
883 thread.setContextClassLoader(old_loader);
889 * Load a session from the database
891 * @return the session data that was loaded
894 protected Session loadSession (final String id, final String canonicalContextPath, final String vhost)
897 final AtomicReference<Session> _reference = new AtomicReference<Session>();
898 final AtomicReference<Exception> _exception = new AtomicReference<Exception>();
899 Runnable load = new Runnable()
902 * @see java.lang.Runnable#run()
904 @SuppressWarnings("unchecked")
907 try (Connection connection = getConnection();
908 PreparedStatement statement = _sessionTableSchema.getLoadStatement(connection, id, canonicalContextPath, vhost);
909 ResultSet result = statement.executeQuery())
911 Session session = null;
914 long maxInterval = result.getLong(_sessionTableSchema.getMaxIntervalColumn());
915 if (maxInterval == JDBCSessionIdManager.MAX_INTERVAL_NOT_SET)
917 maxInterval = getMaxInactiveInterval(); //if value not saved for maxInactiveInterval, use current value from sessionmanager
919 session = (Session)newSession(id, result.getString(_sessionTableSchema.getRowIdColumn()),
920 result.getLong(_sessionTableSchema.getCreateTimeColumn()),
921 result.getLong(_sessionTableSchema.getAccessTimeColumn()),
923 session.setCookieSet(result.getLong(_sessionTableSchema.getCookieTimeColumn()));
924 session.setLastAccessedTime(result.getLong(_sessionTableSchema.getLastAccessTimeColumn()));
925 session.setLastNode(result.getString(_sessionTableSchema.getLastNodeColumn()));
926 session.setLastSaved(result.getLong(_sessionTableSchema.getLastSavedTimeColumn()));
927 session.setExpiryTime(result.getLong(_sessionTableSchema.getExpiryTimeColumn()));
928 session.setCanonicalContext(result.getString(_sessionTableSchema.getContextPathColumn()));
929 session.setVirtualHost(result.getString(_sessionTableSchema.getVirtualHostColumn()));
931 try (InputStream is = ((JDBCSessionIdManager)getSessionIdManager())._dbAdaptor.getBlobInputStream(result, _sessionTableSchema.getMapColumn());
932 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
934 Object o = ois.readObject();
935 session.addAttributes((Map<String,Object>)o);
938 if (LOG.isDebugEnabled())
939 LOG.debug("LOADED session "+session);
942 if (LOG.isDebugEnabled())
943 LOG.debug("Failed to load session "+id);
944 _reference.set(session);
956 _context.getContextHandler().handle(load);
958 if (_exception.get()!=null)
960 //if the session could not be restored, take its id out of the pool of currently-in-use
962 _jdbcSessionIdMgr.removeSession(id);
963 throw _exception.get();
966 return _reference.get();
970 * Insert a session into the database.
975 protected void storeSession (Session session)
981 //put into the database
982 try (Connection connection = getConnection();
983 PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._insertSession))
985 String rowId = calculateRowId(session);
987 long now = System.currentTimeMillis();
988 connection.setAutoCommit(true);
989 statement.setString(1, rowId); //rowId
990 statement.setString(2, session.getClusterId()); //session id
991 statement.setString(3, session.getCanonicalContext()); //context path
992 statement.setString(4, session.getVirtualHost()); //first vhost
993 statement.setString(5, getSessionIdManager().getWorkerName());//my node id
994 statement.setLong(6, session.getAccessed());//accessTime
995 statement.setLong(7, session.getLastAccessedTime()); //lastAccessTime
996 statement.setLong(8, session.getCreationTime()); //time created
997 statement.setLong(9, session.getCookieSet());//time cookie was set
998 statement.setLong(10, now); //last saved time
999 statement.setLong(11, session.getExpiryTime());
1000 statement.setLong(12, session.getMaxInactiveInterval());
1002 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1003 ObjectOutputStream oos = new ObjectOutputStream(baos);
1004 oos.writeObject(session.getAttributeMap());
1006 byte[] bytes = baos.toByteArray();
1008 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
1009 statement.setBinaryStream(13, bais, bytes.length);//attribute map as blob
1012 statement.executeUpdate();
1013 session.setRowId(rowId); //set it on the in-memory data as well as in db
1014 session.setLastSaved(now);
1016 if (LOG.isDebugEnabled())
1017 LOG.debug("Stored session "+session);
1022 * Update data on an existing persisted session.
1024 * @param data the session
1027 protected void updateSession (Session data)
1033 try (Connection connection = getConnection();
1034 PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSession))
1036 long now = System.currentTimeMillis();
1037 connection.setAutoCommit(true);
1038 statement.setString(1, data.getClusterId());
1039 statement.setString(2, getSessionIdManager().getWorkerName());//my node id
1040 statement.setLong(3, data.getAccessed());//accessTime
1041 statement.setLong(4, data.getLastAccessedTime()); //lastAccessTime
1042 statement.setLong(5, now); //last saved time
1043 statement.setLong(6, data.getExpiryTime());
1044 statement.setLong(7, data.getMaxInactiveInterval());
1046 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1047 ObjectOutputStream oos = new ObjectOutputStream(baos);
1048 oos.writeObject(data.getAttributeMap());
1050 byte[] bytes = baos.toByteArray();
1051 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
1053 statement.setBinaryStream(8, bais, bytes.length);//attribute map as blob
1054 statement.setString(9, data.getRowId()); //rowId
1055 statement.executeUpdate();
1057 data.setLastSaved(now);
1059 if (LOG.isDebugEnabled())
1060 LOG.debug("Updated session "+data);
1065 * Update the node on which the session was last seen to be my node.
1067 * @param data the session
1070 protected void updateSessionNode (Session data)
1073 String nodeId = getSessionIdManager().getWorkerName();
1074 try (Connection connection = getConnection();
1075 PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionNode))
1077 connection.setAutoCommit(true);
1078 statement.setString(1, nodeId);
1079 statement.setString(2, data.getRowId());
1080 statement.executeUpdate();
1082 if (LOG.isDebugEnabled())
1083 LOG.debug("Updated last node for session id="+data.getId()+", lastNode = "+nodeId);
1087 * Persist the time the session was last accessed.
1089 * @param data the session
1092 private void updateSessionAccessTime (Session data)
1095 try (Connection connection = getConnection();
1096 PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionAccessTime))
1098 long now = System.currentTimeMillis();
1099 connection.setAutoCommit(true);
1100 statement.setString(1, getSessionIdManager().getWorkerName());
1101 statement.setLong(2, data.getAccessed());
1102 statement.setLong(3, data.getLastAccessedTime());
1103 statement.setLong(4, now);
1104 statement.setLong(5, data.getExpiryTime());
1105 statement.setLong(6, data.getMaxInactiveInterval());
1106 statement.setString(7, data.getRowId());
1108 statement.executeUpdate();
1109 data.setLastSaved(now);
1111 if (LOG.isDebugEnabled())
1112 LOG.debug("Updated access time session id="+data.getId()+" with lastsaved="+data.getLastSaved());
1119 * Delete a session from the database. Should only be called
1120 * when the session has been invalidated.
1125 protected void deleteSession (Session data)
1128 try (Connection connection = getConnection();
1129 PreparedStatement statement = connection.prepareStatement(_jdbcSessionIdMgr._deleteSession))
1131 connection.setAutoCommit(true);
1132 statement.setString(1, data.getRowId());
1133 statement.executeUpdate();
1134 if (LOG.isDebugEnabled())
1135 LOG.debug("Deleted Session "+data);
1142 * Get a connection from the driver.
1144 * @throws SQLException
1146 private Connection getConnection ()
1149 return ((JDBCSessionIdManager)getSessionIdManager()).getConnection();
1153 * Calculate a unique id for this session across the cluster.
1155 * Unique id is composed of: contextpath_virtualhost0_sessionid
1159 private String calculateRowId (Session data)
1161 String rowId = canonicalize(_context.getContextPath());
1162 rowId = rowId + "_" + getVirtualHost(_context);
1163 rowId = rowId+"_"+data.getId();
1168 * Get the first virtual host for the context.
1170 * Used to help identify the exact session/contextPath.
1172 * @return 0.0.0.0 if no virtual host is defined
1174 private static String getVirtualHost (ContextHandler.Context context)
1176 String vhost = "0.0.0.0";
1181 String [] vhosts = context.getContextHandler().getVirtualHosts();
1182 if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
1189 * Make an acceptable file name from a context path.
1194 private static String canonicalize (String path)
1199 return path.replace('/', '_').replace('.','_').replace('\\','_');