This PATCH implements the earlier PROPOSAL.  namely;

> PROPOSAL:  When Session Max reached, throw out oldest session
> 
> Currently tomcat ships with the maximum number of sessions set to
> unlimited.  They can expire by default after 30 minutes, however if
> too many sessions are created within the 30 minutes, you can run out
> of memory.
> 
> To prevent running out of memory, you might choose to limit the
> allowed number of active sessions.  If you use the default
> StanardManager (session manager) you can set the "maxActiveSessions"
> to effect a limit.  However if you exceed the number of allowed
> sessions, a RuntimeException (IllegalStateException) is thrown.
> 
> I propose two changes to reduce seeing these (IllegalStateException or
> OutOfMemory) exceptions for sessions;
> 
> 1. When the maximum number of sessions is reached, change
> StandardManager from throwing an IllegalStateException exception, to
> expiring the Least Recently Used (LRUMap) session.
> 
> 2. Instead of defaulting to an unlimited number of sessions (and
> getting visits from OutOfMemory), limit the number of sessions to
> 10000 by default.

This PATCH does 1 and 2.  It also fixes a problem with recycling
sessions (manager was set to null before session could be recycled.)

Can someone please review this?

Cheers,
-bob




Index: ManagerBase.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/ManagerBase.java,v
retrieving revision 1.11
diff -u -r1.11 ManagerBase.java
--- ManagerBase.java    14 Jan 2002 23:38:03 -0000      1.11
+++ ManagerBase.java    18 Jun 2002 20:47:04 -0000
@@ -73,6 +73,8 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Random;
+import java.util.Map;
+import java.util.Collections;
 import org.apache.catalina.Container;
 import org.apache.catalina.Engine;
 import org.apache.catalina.Logger;
@@ -194,7 +196,7 @@
      * The set of currently active Sessions for this Manager, keyed by
      * session identifier.
      */
-    protected HashMap sessions = new HashMap();
+    protected Map sessions = Collections.synchronizedMap(new HashMap()) ;
 
 
     /**
@@ -496,11 +498,7 @@
      * @param session Session to be added
      */
     public void add(Session session) {
-
-        synchronized (sessions) {
-            sessions.put(session.getId(), session);
-        }
-
+        sessions.put(session.getId(), session);
     }
 
 
@@ -582,11 +580,8 @@
 
         if (id == null)
             return (null);
-        synchronized (sessions) {
-            Session session = (Session) sessions.get(id);
-            return (session);
-        }
 
+        return ((Session) sessions.get(id));
     }
 
 
@@ -595,14 +590,7 @@
      * If this Manager has no active Sessions, a zero-length array is returned.
      */
     public Session[] findSessions() {
-
-        Session results[] = null;
-        synchronized (sessions) {
-            results = new Session[sessions.size()];
-            results = (Session[]) sessions.values().toArray(results);
-        }
-        return (results);
-
+             return ((Session[]) sessions.values().toArray(new Session[0]));
     }
 
 
@@ -612,11 +600,7 @@
      * @param session Session to be removed
      */
     public void remove(Session session) {
-
-        synchronized (sessions) {
-            sessions.remove(session.getId());
-        }
-
+        sessions.remove(session.getId());
     }
 
 
Index: StandardManager.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/StandardManager.java,v
retrieving revision 1.19
diff -u -r1.19 StandardManager.java
--- StandardManager.java        9 Jun 2002 02:19:43 -0000       1.19
+++ StandardManager.java        18 Jun 2002 20:47:06 -0000
@@ -80,6 +80,8 @@
 import java.io.ObjectStreamClass;
 import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.HashMap;
+import java.util.Collections;
 import javax.servlet.ServletContext;
 import org.apache.catalina.Container;
 import org.apache.catalina.Context;
@@ -93,6 +95,7 @@
 import org.apache.catalina.Session;
 import org.apache.catalina.util.CustomObjectInputStream;
 import org.apache.catalina.util.LifecycleSupport;
+import org.apache.commons.collections.LRUMap;
 
 
 /**
@@ -138,7 +141,7 @@
     /**
      * The maximum number of active Sessions allowed, or -1 for no limit.
      */
-    private int maxActiveSessions = -1;
+    private int maxActiveSessions = 10000;
 
 
     /**
@@ -274,6 +277,13 @@
                                    new Integer(oldMaxActiveSessions),
                                    new Integer(this.maxActiveSessions));
 
+        expireAll();
+
+        if ( max <= 0 )
+            sessions = Collections.synchronizedMap(new HashMap()) ;
+        else
+            sessions = Collections.synchronizedMap(new LocalLRUMap(max) );
+
     }
 
 
@@ -314,29 +324,6 @@
 
     // --------------------------------------------------------- Public Methods
 
-
-    /**
-     * Construct and return a new session object, based on the default
-     * settings specified by this Manager's properties.  The session
-     * id will be assigned by this method, and available via the getId()
-     * method of the returned session.  If a new session cannot be created
-     * for any reason, return <code>null</code>.
-     *
-     * @exception IllegalStateException if a new session cannot be
-     *  instantiated for any reason
-     */
-    public Session createSession() {
-
-        if ((maxActiveSessions >= 0) &&
-          (sessions.size() >= maxActiveSessions))
-            throw new IllegalStateException
-                (sm.getString("standardManager.createSession.ise"));
-
-        return (super.createSession());
-
-    }
-
-
     /**
      * Load any currently active sessions that were previously unloaded
      * to the appropriate persistence mechanism, if any.  If persistence is not
@@ -353,7 +340,10 @@
 
         // Initialize our internal data structures
         recycled.clear();
-        sessions.clear();
+        if ( maxActiveSessions <= 0 )
+            sessions = Collections.synchronizedMap(new HashMap() );
+        else
+            sessions = Collections.synchronizedMap(new LocalLRUMap(maxActiveSessions) 
+);
 
         // Open an input stream to the specified pathname, if any
         File file = file();
@@ -414,7 +404,7 @@
                     ((StandardSession) session).activate();
                 }
             } catch (ClassNotFoundException e) {
-              log(sm.getString("standardManager.loading.cnfe", e), e);
+                log(sm.getString("standardManager.loading.cnfe", e), e);
                 if (ois != null) {
                     try {
                         ois.close();
@@ -425,7 +415,7 @@
                 }
                 throw e;
             } catch (IOException e) {
-              log(sm.getString("standardManager.loading.ioe", e), e);
+                log(sm.getString("standardManager.loading.ioe", e), e);
                 if (ois != null) {
                     try {
                         ois.close();
@@ -492,7 +482,6 @@
         }
 
         // Write the number of active sessions, followed by the details
-        ArrayList list = new ArrayList();
         synchronized (sessions) {
             if (debug >= 1)
                 log("Unloading " + sessions.size() + " sessions");
@@ -502,7 +491,6 @@
                 while (elements.hasNext()) {
                     StandardSession session =
                         (StandardSession) elements.next();
-                    list.add(session);
                     ((StandardSession) session).passivate();
                     session.writeObjectData(oos);
                 }
@@ -537,18 +525,7 @@
             throw e;
         }
 
-        // Expire all the sessions we just wrote
-        if (debug >= 1)
-            log("Expiring " + list.size() + " persisted sessions");
-        Iterator expires = list.iterator();
-        while (expires.hasNext()) {
-            StandardSession session = (StandardSession) expires.next();
-            try {
-                session.expire(false);
-            } catch (Throwable t) {
-                ;
-            }
-        }
+        expireAll();
 
         if (debug >= 1)
             log("Unloading complete");
@@ -664,18 +641,7 @@
             log(sm.getString("standardManager.managerUnload"), e);
         }
 
-        // Expire all active sessions
-        Session sessions[] = findSessions();
-        for (int i = 0; i < sessions.length; i++) {
-            StandardSession session = (StandardSession) sessions[i];
-            if (!session.isValid())
-                continue;
-            try {
-                session.expire();
-            } catch (Throwable t) {
-                ;
-            }
-        }
+        expireAll();
 
         // Require a new random number generator if we are restarted
         this.random = null;
@@ -714,6 +680,31 @@
 
     // -------------------------------------------------------- Private Methods
 
+    /**
+     * extention of commons LRUMap, handles expiration and works around
+     * an LRU bug
+     */
+    private class LocalLRUMap extends LRUMap {
+       LocalLRUMap(int max) {
+           super(max);
+       }
+
+       protected void processRemovedLRU(Object key, Object value){
+           ((Session)value).expire();
+           log("StandardManager: WARNING Max sessions reached, expiring oldest 
+key:"+key);
+       }
+
+       // collections 2.0 has a bug in it - fixed in the latest CVS
+       public Object get(Object key){
+           
+           // put() it to move it to the top of the LRU
+           if ( containsKey( key ) ){
+               put( key, super.get(key));
+           }
+           return super.get(key);
+       }
+    };
+
 
     /**
      * Return a File object representing the pathname to our
@@ -823,6 +814,27 @@
 
         thread = null;
 
+    }
+
+
+    /**
+     * Expire all active sessions
+     */
+    private void expireAll(){
+
+        Session list[] = findSessions();
+        for (int i = 0; i < list.length; i++) {
+            StandardSession session = (StandardSession) list[i];
+            if (!session.isValid())
+                continue;
+            try {
+                session.expire();
+            } catch (Throwable t) {
+                ;
+            }
+        }
+
+        sessions.clear();
     }
 
 

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to