ClassNotFoundException when deserializing custom object with FileStore for PersistentManagerBase.processExpires
Hi, I have bug https://bz.apache.org/bugzilla/show_bug.cgi?id=48007 on recent tomcats. To reproduce one must: - use PersistentManager in global context.xml - have an object in session only available through a webapp class loader - wait for manager backgroundProcess to call PersistentManagerBase.processExpires > Storebase.processExpires (by default, 6 * 10 seconds) => it fails to unserialize, and removes the session from FileStore. My technical understanding of the issue: getObjectInputStream() is using Thread.currentThread().getContextClassLoader() which is set in FileStore.load() using current Context classLoader This is ok when called by manager.findSession(...) , but Storebase.processExpires() is using current context => it is ok when PersistentManager is in the application specific context.xml => it is wrong when PersistentManager is in global context.xml One can workaround the issue by moving from global context.xml to webapp context.xml , as explained here : https://dev.wicket.apache.narkive.com/KXmPFrnQ/problems-with-tomcat-session-persistence-classnotfoundexception-of-secondlevelcachesessionstore If you use PersistentValve, another workaround is to use + an external cron. Stacktrace with tomcat 9: SEVERE [Catalina-utility-3] org.apache.catalina.session.StoreBase.processExpires Error processing session expiration for key [xxx] java.lang.ClassNotFoundException: org.springframework.security.core.context.SecurityContextImpl at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1339) at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1148) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Unknown Source) at org.apache.catalina.util.CustomObjectInputStream.resolveClass(CustomObjectInputStream.java:149) at java.base/java.io.ObjectInputStream.readNonProxyDesc(Unknown Source) at java.base/java.io.ObjectInputStream.readClassDesc(Unknown Source) at java.base/java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) at java.base/java.io.ObjectInputStream.readObject0(Unknown Source) at java.base/java.io.ObjectInputStream.readObject(Unknown Source) at java.base/java.io.ObjectInputStream.readObject(Unknown Source) at org.apache.catalina.session.StandardSession.doReadObject(StandardSession.java:1268) at org.apache.catalina.session.StandardSession.readObjectData(StandardSession.java:846) at org.apache.catalina.session.FileStore.load(FileStore.java:203) at org.apache.catalina.session.StoreBase.processExpires(StoreBase.java:138) at org.apache.catalina.session.PersistentManagerBase.processExpires(PersistentManagerBase.java:409) at org.apache.catalina.session.ManagerBase.backgroundProcess(ManagerBase.java:595) at org.apache.catalina.core.StandardContext.backgroundProcess(StandardContext.java:4823) .. It would be nice in my use-cases to be able to configure globally PersistentManager. It would be enough for us to have an option to disable Storebase.processExpires(), but it may be somewhat ugly... cu Pascal Rigaux. - To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org For additional commands, e-mail: users-h...@tomcat.apache.org
Re: ClassNotFoundException when deserializing custom object with FileStore for PersistentManagerBase.processExpires
On 04/12/2024 18:09, Mark Thomas wrote: The Manager/Store component can't easily determine if it is being configured from the global, host or context level context.xml file. The root cause here is configuration error - configuring multiple web applications to use the same file for session persistence. Thanks for the explantation! Suggestions for StandardManager.file() : - allowing a directory as absolute pathname: if (file.isAbsolute()) { if (file.isDirectory()) { var contextName = getContext().getBaseName(); return new File(file, "SESSIONS-" + contextName + ".ser"); - a quite ugly code to detect bad usage: static Map pathname2contextName = new HashMap<>(); ... if (file.isAbsolute()) { var contextName = getContext().getBaseName(); var prev = pathname2contextName.get(pathname); if (prev != null && !prev.equals(contextName)) { log.error("You can not share same absolute \"pathname\" for multiple s (found same \"pathname\" for " + contextName + " and " + prev + ")"); } pathname2contextName.put(pathname, contextName); cu - To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org For additional commands, e-mail: users-h...@tomcat.apache.org
Re: ClassNotFoundException when deserializing custom object with FileStore for PersistentManagerBase.processExpires
Oops, I missed something : When you use or the behaviour is different if the pathname/directory is absolute or relative. If it is relative, it is stored relative to each applications temporary work directory, and handled with each application classLoader, and it works perfectly. Maybe this should be mentionned more clearly in the documentation? Maybe display a warning log for absolute pathname/directory in global context.xml? cu, Pascal R. On 03/12/2024 16:36, Pascal Rigaux wrote: Hi, I have bug https://bz.apache.org/bugzilla/show_bug.cgi?id=48007 on recent tomcats. To reproduce one must: - use PersistentManager in global context.xml - have an object in session only available through a webapp class loader - wait for manager backgroundProcess to call PersistentManagerBase.processExpires > Storebase.processExpires (by default, 6 * 10 seconds) => it fails to unserialize, and removes the session from FileStore. My technical understanding of the issue: getObjectInputStream() is using Thread.currentThread().getContextClassLoader() which is set in FileStore.load() using current Context classLoader This is ok when called by manager.findSession(...) , but Storebase.processExpires() is using current context => it is ok when PersistentManager is in the application specific context.xml => it is wrong when PersistentManager is in global context.xml One can workaround the issue by moving from global context.xml to webapp context.xml , as explained here : https://dev.wicket.apache.narkive.com/KXmPFrnQ/problems-with-tomcat-session-persistence-classnotfoundexception-of-secondlevelcachesessionstore If you use PersistentValve, another workaround is to use + an external cron. Stacktrace with tomcat 9: SEVERE [Catalina-utility-3] org.apache.catalina.session.StoreBase.processExpires Error processing session expiration for key [xxx] java.lang.ClassNotFoundException: org.springframework.security.core.context.SecurityContextImpl at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1339) at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1148) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Unknown Source) at org.apache.catalina.util.CustomObjectInputStream.resolveClass(CustomObjectInputStream.java:149) at java.base/java.io.ObjectInputStream.readNonProxyDesc(Unknown Source) at java.base/java.io.ObjectInputStream.readClassDesc(Unknown Source) at java.base/java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) at java.base/java.io.ObjectInputStream.readObject0(Unknown Source) at java.base/java.io.ObjectInputStream.readObject(Unknown Source) at java.base/java.io.ObjectInputStream.readObject(Unknown Source) at org.apache.catalina.session.StandardSession.doReadObject(StandardSession.java:1268) at org.apache.catalina.session.StandardSession.readObjectData(StandardSession.java:846) at org.apache.catalina.session.FileStore.load(FileStore.java:203) at org.apache.catalina.session.StoreBase.processExpires(StoreBase.java:138) at org.apache.catalina.session.PersistentManagerBase.processExpires(PersistentManagerBase.java:409) at org.apache.catalina.session.ManagerBase.backgroundProcess(ManagerBase.java:595) at org.apache.catalina.core.StandardContext.backgroundProcess(StandardContext.java:4823) .. It would be nice in my use-cases to be able to configure globally PersistentManager. It would be enough for us to have an option to disable Storebase.processExpires(), but it may be somewhat ugly... cu Pascal Rigaux. -- Pascal Rigaux Expert en développement et déploiement d'applications DSIUN-PAS (Pôle Applications et Services numériques) Université Paris 1 Panthéon-Sorbonne - Centre Pierre Mendès France (PMF) Coordonnées : https://annuaire.univ-paris1.fr/Pascal.Rigaux@ - To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org For additional commands, e-mail: users-h...@tomcat.apache.org
session Manager "maxActiveSessions" alternative behavior : allow new session but expire old session
Hi, On some applications we have: - quite low number of users most of the time - high number of users twice a year These applications store quite a lot of information in session. To cope with the surge of users, we would need to: - either increase mx (Java max memory) - or use short session timeout (lifetime) - or use horizontal scaling (k8s... but we currently have no such solutions) The best solution for us would be a "maxActiveSessions" setting which would set a high bound to the number of sessions, but compared to current implementation, it would allow new sessions and expire old sessions. We mostly achieved this with a cron (1) calling a script (2): it uses tomcat-manager to force a shorter lifetime when there is a lot of sessions. Since it would be nicer to have this behavior directly in tomcat, I did it using a small class extending StandardManager (2). I wonder if this could be useful to other users?... cu. (1) */10 * * * * ~/tomcat/expire-if-too-many-sessions student-info 4000 8 (2) script "expire-if-too-many-sessions": -- webapp=$1 max_sessions=$2 idle=$3 nb_sessions=`curl -s --user "$user" "$url/manager/text/list" | grep "$webapp" | awk -F: '{print $3}'` if [ $nb_sessions -gt $max_sessions ]; then echo "too many sessions ($nb_sessions > $max_sessions). telling tomcat to expire $webapp sessions inactive more than $idle" out=`curl -s --user "$user" "$url/manager/text/expire?path=/$webapp&idle=$idle"` if echo $out | grep -q '^OK'; then : else echo "error expiring: $out" fi fi - (3) - protected int maxActiveSessionsGoal = -1; /** * If you have too many sessions, you may memory overflow. * This setting will expire old sessions to keep sessions memory usage low. * -1 is no limit */ public void setMaxActiveSessionsGoal(int max) { maxActiveSessionsGoal = max; } public void processExpires() { super.processExpires(); if (maxActiveSessionsGoal >= 0) { var nb = getActiveSessions(); if (nb > maxActiveSessionsGoal) { expireNbOldSessions(nb - maxActiveSessionsGoal); } } } private void expireNbOldSessions(int nbToRemove) { var time = sessions.values().stream() .mapToLong(Session::getLastAccessedTimeInternal).sorted() // we want the nbToRemove-th element in the array .skip(nbToRemove).findFirst() .orElse(0); if (time == 0) { log.error("internal error expireNbOldSessions"); return; } log.info("To achieve maxActiveSessionsGoal (" + maxActiveSessionsGoal + ") for " + getContext().getBaseName() + ", we will expire sessions older than " + new Date(time) + " (" + nbToRemove + " sessions)"); for (Session session : findSessions()) { if (session.getLastAccessedTimeInternal() < time) { session.expire(); } } } - - To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org For additional commands, e-mail: users-h...@tomcat.apache.org
Re: session Manager "maxActiveSessions" alternative behavior : allow new session but expire old session
Hi, On 08/01/2025 22:13, Christopher Schultz wrote: [...] It would allow anyone to force a logout of all current users at will just by making any request that causes an unauthenticated session to be created. Instant DOS. Note that current "maxActiveSessions" implementation also causes a DOS: if you can create many sessions, it will block new users. I must look at the application unauthenticated sessions: - if they are already big, there is already a DOS via OutOfMemory - if there are small, they would need to be handled specifically, expiring them first In any case, as you suggested, the application should not depend on such big sessions. That's the real solution to avoid any issues! cu Pascal Rigaux. - To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org For additional commands, e-mail: users-h...@tomcat.apache.org