Author: markt Date: Fri Dec 18 18:42:09 2009 New Revision: 892341 URL: http://svn.apache.org/viewvc?rev=892341&view=rev Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=47930 Make swapIn thread safe so parallel requests for the same session don't result in multiple session objects for one sesison.
Modified: tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java Modified: tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java?rev=892341&r1=892340&r2=892341&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java (original) +++ tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java Fri Dec 18 18:42:09 2009 @@ -24,6 +24,9 @@ import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; +import java.util.HashMap; +import java.util.Map; + import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Lifecycle; @@ -207,6 +210,13 @@ protected long processingTime = 0; + /** + * Sessions currently being swapped in and the associated locks + */ + private Map<String,Object> sessionSwapInLocks = + new HashMap<String,Object>(); + + // ------------------------------------------------------------- Properties @@ -779,53 +789,89 @@ if (store == null) return null; - Session session = null; - try { - if (SecurityUtil.isPackageProtectionEnabled()){ - try{ - session = AccessController.doPrivileged( - new PrivilegedStoreLoad(id)); - }catch(PrivilegedActionException ex){ - Exception exception = ex.getException(); - log.error("Exception in the Store during swapIn: " - + exception); - if (exception instanceof IOException){ - throw (IOException)exception; - } else if (exception instanceof ClassNotFoundException) { - throw (ClassNotFoundException)exception; - } - } - } else { - session = store.load(id); - } - } catch (ClassNotFoundException e) { - log.error(sm.getString("persistentManager.deserializeError", id, e)); - throw new IllegalStateException - (sm.getString("persistentManager.deserializeError", id, e)); - } - - if (session == null) - return (null); - - if (!session.isValid()) { - log.error("session swapped in is invalid or expired"); - session.expire(); - removeSession(id); - return (null); - } + Object swapInLock = null; + + /* + * The purpose of this sync and these locks is to make sure that a + * session is only loaded once. It doesn't matter if the lock is removed + * and then another thread enters this method and trues to load the same + * session. That thread will re-creates a swapIn lock for that session, + * quickly find that the session is already in sessions, use it and + * carry on. + */ + synchronized (this) { + if (sessionSwapInLocks.containsKey(id)) { + swapInLock = sessionSwapInLocks.get(id); + } else { + swapInLock = new Object(); + sessionSwapInLocks.put(id, swapInLock); + } + } - if(log.isDebugEnabled()) - log.debug(sm.getString("persistentManager.swapIn", id)); + Session session = null; - session.setManager(this); - // make sure the listeners know about it. - ((StandardSession)session).tellNew(); - add(session); - ((StandardSession)session).activate(); - // endAccess() to ensure timeouts happen correctly. - // access() to keep access count correct or it will end up negative - session.access(); - session.endAccess(); + synchronized (swapInLock) { + // First check to see if another thread has loaded the session into + // the manager + session = sessions.get(id); + + if (session == null) { + try { + if (SecurityUtil.isPackageProtectionEnabled()){ + try { + session = AccessController.doPrivileged( + new PrivilegedStoreLoad(id)); + } catch (PrivilegedActionException ex) { + Exception e = ex.getException(); + log.error(sm.getString( + "persistentManager.swapInException", id), + e); + if (e instanceof IOException){ + throw (IOException)e; + } else if (e instanceof ClassNotFoundException) { + throw (ClassNotFoundException)e; + } + } + } else { + session = store.load(id); + } + } catch (ClassNotFoundException e) { + String msg = sm.getString( + "persistentManager.deserializeError", id); + log.error(msg, e); + throw new IllegalStateException(msg, e); + } + + if (session != null && !session.isValid()) { + log.error(sm.getString( + "persistentManager.swapInInvalid", id)); + session.expire(); + removeSession(id); + session = null; + } + + if (session != null) { + if(log.isDebugEnabled()) + log.debug(sm.getString("persistentManager.swapIn", id)); + + session.setManager(this); + // make sure the listeners know about it. + ((StandardSession)session).tellNew(); + add(session); + ((StandardSession)session).activate(); + // endAccess() to ensure timeouts happen correctly. + // access() to keep access count correct or it will end up + // negative + session.access(); + session.endAccess(); + } + } + } + + // Make sure the lock is removed + synchronized (this) { + sessionSwapInLocks.remove(id); + } return (session); --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org