You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ki...@apache.org on 2001/04/15 11:33:27 UTC

cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session PersistentManagerBase.java PersistentManager.java

kief        01/04/15 02:33:27

  Modified:    catalina/src/share/org/apache/catalina/session
                        PersistentManager.java
  Added:       catalina/src/share/org/apache/catalina/session
                        PersistentManagerBase.java
  Log:
  Refactored PersistentManager, moving basic persistence functionality,
  as well as Lifecycle and background maintenance threat, into
  PersistentManagerBase. This base class can also be used for
  StandardManager's session reloading feature, once it has been tested
  thoroughly.
  
  These classes will undoubtedly go through some significant revisions,
  especially once we start working on DistributedManager.
  
  Revision  Changes    Path
  1.7       +110 -620  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/PersistentManager.java
  
  Index: PersistentManager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/PersistentManager.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- PersistentManager.java	2001/04/12 20:24:36	1.6
  +++ PersistentManager.java	2001/04/15 09:33:27	1.7
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/PersistentManager.java,v 1.6 2001/04/12 20:24:36 kief Exp $
  - * $Revision: 1.6 $
  - * $Date: 2001/04/12 20:24:36 $
  + * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/PersistentManager.java,v 1.7 2001/04/15 09:33:27 kief Exp $
  + * $Revision: 1.7 $
  + * $Date: 2001/04/15 09:33:27 $
    *
    * ====================================================================
    *
  @@ -106,12 +106,12 @@
    * <li>Limit the number of active sessions kept in memory by
    *     swapping less active sessions out to disk.</li>
    *
  - * @version $Revision: 1.6 $
  + * @version $Revision: 1.7 $
    * @author Kief Morris (kief@kief.com)
    */
   
   public final class PersistentManager
  -    extends ManagerBase
  +    extends PersistentManagerBase
       implements Lifecycle, PropertyChangeListener, Runnable {
   
   
  @@ -119,54 +119,12 @@
   
   
       /**
  -     * The interval (in seconds) between checks for expired sessions.
  -     */
  -    private int checkInterval = 60;
  -
  -
  -    /**
        * The descriptive information about this implementation.
        */
       private static final String info = "PersistentManager/1.0";
   
   
       /**
  -     * The lifecycle event support for this component.
  -     */
  -    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
  -
  -
  -    /**
  -     * The maximum number of active Sessions allowed, or -1 for no limit.
  -     */
  -    private int maxActiveSessions = -1;
  -
  -
  -    /**
  -     * Has this component been started yet?
  -     */
  -    private boolean started = false;
  -
  -
  -    /**
  -     * The background thread.
  -     */
  -    private Thread thread = null;
  -
  -
  -    /**
  -     * The background thread completion semaphore.
  -     */
  -    private boolean threadDone = false;
  -
  -
  -    /**
  -     * Name to register for the background thread.
  -     */
  -    private String threadName = "PersistentManager";
  -
  -
  -    /**
        * Whether to save and reload sessions when the Manager <code>unload</code>
        * and <code>load</code> methods are called.
        */
  @@ -174,7 +132,7 @@
       
       
       /**
  -     * How long a session must be idle before it is backed up.
  +     * How long a session must be idle before it should be backed up.
        * -1 means sessions won't be backed up.
        */
       private int maxIdleBackup = -1;
  @@ -194,91 +152,11 @@
        */
       private int maxIdleSwap = -1;
   
  -    /**
  -     * Store object which will manage the Session store.
  -     */
  -    private Store store = null;
  -
   
       // ------------------------------------------------------------- Properties
   
   
       /**
  -     * Return the check interval (in seconds) for this Manager.
  -     */
  -    public int getCheckInterval() {
  -
  -        return (this.checkInterval);
  -
  -    }
  -
  -
  -    /**
  -     * Set the check interval (in seconds) for this Manager.
  -     *
  -     * @param checkInterval The new check interval
  -     */
  -    public void setCheckInterval(int checkInterval) {
  -
  -        int oldCheckInterval = this.checkInterval;
  -        this.checkInterval = checkInterval;
  -        support.firePropertyChange("checkInterval",
  -                                   new Integer(oldCheckInterval),
  -                                   new Integer(this.checkInterval));
  -
  -    }
  -
  -
  -    /**
  -     * Set the Container with which this Manager has been associated.  If
  -     * it is a Context (the usual case), listen for changes to the session
  -     * timeout property.
  -     *
  -     * @param container The associated Container
  -     */
  -    public void setContainer(Container container) {
  -
  -        // De-register from the old Container (if any)
  -        if ((this.container != null) && (this.container instanceof Context))
  -            ((Context) this.container).removePropertyChangeListener(this);
  -
  -        // Default processing provided by our superclass
  -        super.setContainer(container);
  -
  -        // Register with the new Container (if any)
  -        if ((this.container != null) && (this.container instanceof Context)) {
  -            setMaxInactiveInterval
  -                ( ((Context) this.container).getSessionTimeout()*60 );
  -            ((Context) this.container).addPropertyChangeListener(this);
  -        }
  -
  -    }
  -
  -
  -    /**
  -     * Set the Store object which will manage persistent Session
  -     * storage for this Manager.
  -     *
  -     * @param store the associated Store
  -     */
  -    public void setStore(Store store) {
  -    
  -        this.store = store;
  -        store.setManager(this);
  -            
  -    }
  -    
  -    /**
  -     * Return the Store object which manages persistent Session
  -     * storage for this Manager.
  -     */
  -    public Store getStore() {
  -            
  -        return (this.store);
  -            
  -    }
  -
  -    /**
        * Return descriptive information about this Manager implementation and
        * the corresponding version number, in the format
        * <code>&lt;description&gt;/&lt;version&gt;</code>.
  @@ -291,65 +169,74 @@
   
   
       /**
  -     * Indicates whether sessions are saved when the Manager is shut down
  -     * properly. This requires the unload() method to be called.
  +     * Indicates how many seconds old a session can get, after its last 
  +     * use in a request, before it should be backed up to the store. -1
  +     * means sessions are not backed up.
        */
  -    public boolean getSaveOnRestart() {
  +    public int getMaxIdleBackup() {
   
  -        return saveOnRestart;
  +        return maxIdleBackup;
   
       }
       
       
       /**
  -     * Set the option to save sessions to the Store when the Manager is
  -     * shut down, then loaded when the Manager starts again. If set to 
  -     * false, any sessions found in the Store may still be picked up when 
  -     * the Manager is started again.
  +     * Sets the option to back sessions up to the Store after they
  +     * are used in a request. Sessions remain available in memory
  +     * after being backed up, so they are not passivated as they are 
  +     * when swapped out. The value set indicates how old a session 
  +     * may get (since its last use) before it must be backed up: -1 
  +     * means sessions are not backed up.
  +     * <p>
  +     * Note that this is not a hard limit: sessions are checked 
  +     * against this age limit periodically according to <b>checkInterval</b>.
  +     * This value should be considered to indicate when a session is
  +     * ripe for backing up.
  +     * <p>
  +     * So it is possible that a session may be idle for maxIdleBackup +
  +     * checkInterval seconds, plus the time it takes to handle other
  +     * session expiration, swapping, etc. tasks.
        *
  -     * @param save true if sessions should be saved on restart, false if
  -     *     they should be ignored.
  +     * @param backup The number of seconds after their last accessed
  +     * time when they should be written to the Store. 
        */
  -    public void setSaveOnRestart(boolean saveOnRestart) {
  +    public void setMaxIdleBackup (int backup) {
   
  -        if (saveOnRestart == this.saveOnRestart)
  +        if (backup == this.maxIdleBackup)
               return;
  -
  -        boolean oldSaveOnRestart = this.saveOnRestart;
  -        this.saveOnRestart = saveOnRestart;
  -        support.firePropertyChange("saveOnRestart",
  -                                   new Boolean(oldSaveOnRestart),
  -                                   new Boolean(this.saveOnRestart));
  +        int oldBackup = this.maxIdleBackup;
  +        this.maxIdleBackup = backup;
  +        support.firePropertyChange("maxIdleBackup",
  +                                   new Integer(oldBackup),
  +                                   new Integer(this.maxIdleBackup));
                                      
       }
       
       
       /**
  -     * Return the maximum number of active Sessions allowed, or -1 for
  -     * no limit.
  +     * The time in seconds after which a session should be swapped out of
  +     * memory to disk.
        */
  -    public int getMaxActiveSessions() {
  +    public int getMaxIdleSwap() {
   
  -        return (this.maxActiveSessions);
  +        return maxIdleSwap;
   
       }
  -
  -
  +    
  +    
       /**
  -     * Set the maximum number of actives Sessions allowed, or -1 for
  -     * no limit.
  -     *
  -     * @param max The new maximum number of sessions
  +     * Sets the time in seconds after which a session should be swapped out of
  +     * memory to disk.
        */
  -    public void setMaxActiveSessions(int max) {
  +    public void setMaxIdleSwap(int max) {
   
  -        if (max == this.maxActiveSessions)
  +        if (max == this.maxIdleSwap)
               return;
  -        int oldMaxActiveSessions = this.maxActiveSessions;
  -        this.maxActiveSessions = max;
  -        support.firePropertyChange("maxActiveSessions",
  -                                   new Integer(oldMaxActiveSessions),
  -                                   new Integer(this.maxActiveSessions));
  +        int oldMaxIdleSwap = this.maxIdleSwap;
  +        this.maxIdleSwap = max;
  +        support.firePropertyChange("maxIdleSwap",
  +                                   new Integer(oldMaxIdleSwap),
  +                                   new Integer(this.maxIdleSwap));
   
       }
   
  @@ -385,57 +272,35 @@
       
       
       /**
  -     * The time in seconds after which a session should be swapped out of
  -     * memory to disk.
  +     * Indicates whether sessions are saved when the Manager is shut down
  +     * properly. This requires the unload() method to be called.
        */
  -    public int getMaxIdleSwap() {
  +    public boolean getSaveOnRestart() {
   
  -        return maxIdleSwap;
  +        return saveOnRestart;
   
       }
       
       
       /**
  -     * Sets the time in seconds after which a session should be swapped out of
  -     * memory to disk.
  +     * Set the option to save sessions to the Store when the Manager is
  +     * shut down, then loaded when the Manager starts again. If set to 
  +     * false, any sessions found in the Store may still be picked up when 
  +     * the Manager is started again.
  +     *
  +     * @param save true if sessions should be saved on restart, false if
  +     *     they should be ignored.
        */
  -    public void setMaxIdleSwap(int max) {
  +    public void setSaveOnRestart(boolean saveOnRestart) {
   
  -        if (max == this.maxIdleSwap)
  +        if (saveOnRestart == this.saveOnRestart)
               return;
  -        int oldMaxIdleSwap = this.maxIdleSwap;
  -        this.maxIdleSwap = max;
  -        support.firePropertyChange("maxIdleSwap",
  -                                   new Integer(oldMaxIdleSwap),
  -                                   new Integer(this.maxIdleSwap));
   
  -    }
  -
  -    /**
  -     * Indicates whether sessions will be written to the store after
  -     * every request in which they are accessed.
  -     */
  -    public int getMaxIdleBackup() {
  -
  -        return maxIdleBackup;
  -
  -    }
  -    
  -    
  -    /**
  -     * Sets the option to save sessions to the Store. Setting this to 
  -     * true means most sessions should be recovered after the server
  -     * goes down, even if it's not a graceful shutdown.
  -     */
  -    public void setMaxIdleBackup (int backup) {
  -
  -        if (backup == this.maxIdleBackup)
  -            return;
  -        int oldBackup = this.maxIdleBackup;
  -        this.maxIdleBackup = backup;
  -        support.firePropertyChange("maxIdleBackup",
  -                                   new Integer(oldBackup),
  -                                   new Integer(this.maxIdleBackup));
  +        boolean oldSaveOnRestart = this.saveOnRestart;
  +        this.saveOnRestart = saveOnRestart;
  +        support.firePropertyChange("saveOnRestart",
  +                                   new Boolean(oldSaveOnRestart),
  +                                   new Boolean(this.saveOnRestart));
                                      
       }
       
  @@ -443,100 +308,16 @@
       // --------------------------------------------------------- Public Methods
   
   
  -    /**
  -     * Return the active Session, associated with this Manager, with the
  -     * specified session id (if any); otherwise return <code>null</code>.
  -     *
  -     * @param id The session id for the session to be returned
  -     *
  -     * @exception IllegalStateException if a new session cannot be
  -     *  instantiated for any reason
  -     * @exception IOException if an input/output error occurs while
  -     *  processing this request
  -     */
  -    public Session findSession(String id) throws IOException {
  -
  -        Session session = super.findSession(id);
  -        if (session != null)
  -            return (session);
  -
  -        // See if the Session is in the Store
  -        if (maxActiveSessions >= 0 || maxIdleSwap >= 0 || 
  -                maxIdleBackup >= 0)
  -            session = swapIn(id);
  -
  -        return (session);
  -
  -    }
  -        
       /**
  -     * Load any currently active sessions that were previously unloaded
  -     * or backed up to the appropriate persistence mechanism, if any.  
  -     * If persistence is not supported, this method returns without doing 
  -     * anything.
  -     *
  -     * @exception ClassNotFoundException if a serialized class cannot be
  -     *  found during the reload
  -     * @exception IOException if an input/output error occurs
  +     * Called by the background thread after active sessions have
  +     * been checked for expiration, to allow sessions to be
  +     * swapped out, backed up, etc.
        */
  -    public void load() throws ClassNotFoundException, IOException {
  -
  -        // Initialize our internal data structures
  -        recycled.clear();
  -        sessions.clear();
  -
  -        if (store == null)
  -            return;
  -
  -        // If persistence is off, clear the Store and finish
  -        if (!saveOnRestart && maxActiveSessions < 0 && 
  -                maxIdleSwap < 0 && maxIdleBackup < 0) {
  -            try {
  -                store.clear();
  -            } catch (IOException e) {
  -                log("Exception clearing the Store: " + e);
  -                e.printStackTrace();
  -            }
  -            
  -            return;
  -        }
  -
  -        String[] ids = store.keys();
  -        int n = ids.length;
  -        if (n == 0)
  -            return;
  -            
  -        if (debug >= 1)
  -            log(sm.getString("persistentManager.loading", String.valueOf(n)));
  -
  -        for (int i = 0; i < n; i++)
  -            swapIn(ids[i]);
  -        
  -    }
  -
  -    /**
  -     * Save all currently active sessions in the appropriate persistence
  -     * mechanism, if any.  If persistence is not supported, this method
  -     * returns without doing anything.
  -     *
  -     * @exception IOException if an input/output error occurs
  -     */
  -    public void unload() throws IOException {
  -
  -        if (store == null || !saveOnRestart)
  -            return;
  -
  -        Session sessions[] = findSessions();
  -        int n = sessions.length;
  -        if (n == 0)
  -            return;
  -            
  -        if (debug >= 1)
  -            log(sm.getString("persistentManager.unloading", 
  -                             String.valueOf(n)));
  -
  -        for (int i = 0; i < n; i++)
  -            swapOut(sessions[i]);
  +    public void processPersistenceChecks() {
  +    
  +            processMaxIdleSwaps();
  +            processMaxActiveSwaps();
  +            processMaxIdleBackups();
   
       }
   
  @@ -545,70 +326,6 @@
   
   
       /**
  -     * Add a lifecycle event listener to this component.
  -     *
  -     * @param listener The listener to add
  -     */
  -    public void addLifecycleListener(LifecycleListener listener) {
  -
  -        lifecycle.addLifecycleListener(listener);
  -
  -    }
  -
  -
  -    /**
  -     * Remove a lifecycle event listener from this component.
  -     *
  -     * @param listener The listener to remove
  -     */
  -    public void removeLifecycleListener(LifecycleListener listener) {
  -
  -        lifecycle.removeLifecycleListener(listener);
  -
  -    }
  -
  -
  -    /**
  -     * Prepare for the beginning of active use of the public methods of this
  -     * component.  This method should be called after <code>configure()</code>,
  -     * and before any of the public methods of the component are utilized.
  -     *
  -     * @exception IllegalStateException if this component has already been
  -     *  started
  -     * @exception LifecycleException if this component detects a fatal error
  -     *  that prevents this component from being used
  -     */
  -    public void start() throws LifecycleException {
  -
  -        if (debug >= 1)
  -            log("Starting PersistentManager");
  -
  -        // Validate and update our current component state
  -        if (started)
  -            throw new LifecycleException
  -                (sm.getString("standardManager.alreadyStarted"));
  -        lifecycle.fireLifecycleEvent(START_EVENT, null);
  -        started = true;
  -
  -        // Force initialization of the random number generator
  -        if (debug >= 1)
  -            log("Force random number initialization starting");
  -        String dummy = generateSessionId();
  -        if (debug >= 1)
  -            log("Force random number initialization completed");
  -
  -        if (store == null)
  -            log("No Store configured");
  -        else if (store instanceof Lifecycle)
  -            ((Lifecycle)store).start();
  -            
  -        // Start the background reaper thread
  -        threadStart();
  -
  -    }
  -
  -
  -    /**
        * Gracefully terminate the active use of the public methods of this
        * component.  This method should be the last one called on a given
        * instance of this component.
  @@ -620,95 +337,49 @@
       public void stop() throws LifecycleException {
   
           if (debug >= 1)
  -            log("Stopping PersistentManager");
  +            log("Stopping");
   
           // Validate and update our current component state
  -        if (!started)
  +        if (!isStarted())
               throw new LifecycleException
                   (sm.getString("standardManager.notStarted"));
           lifecycle.fireLifecycleEvent(STOP_EVENT, null);
  -        started = false;
  +        setStarted(false);
   
           // Stop the background reaper thread
           threadStop();
   
  -        // Expire all active sessions
  -        Session sessions[] = findSessions();
  -        for (int i = 0; i < sessions.length; i++) {
  -            StandardSession session = (StandardSession) sessions[i];
  -            if (!session.isValid())
  -                continue;
  -            session.expire();
  +        if (getStore() != null && saveOnRestart) {
  +            unload();
  +        } else {
  +            // Expire all active sessions
  +            Session sessions[] = findSessions();
  +            for (int i = 0; i < sessions.length; i++) {
  +                StandardSession session = (StandardSession) sessions[i];
  +                if (!session.isValid())
  +                    continue;
  +                session.expire();
  +            }
           }
  +        
  +        if (getStore() != null && getStore() instanceof Lifecycle)
  +            ((Lifecycle)getStore()).stop();
   
           // Require a new random number generator if we are restarted
           this.random = null;
  -        
  -        if (store instanceof Lifecycle)
  -            ((Lifecycle)store).stop();
   
       }
   
   
  -    // ----------------------------------------- PropertyChangeListener Methods
  -
  -
  -    /**
  -     * Process property change events from our associated Context.
  -     *
  -     * @param event The property change event that has occurred
  -     */
  -    public void propertyChange(PropertyChangeEvent event) {
  -
  -        // Validate the source of this event
  -        if (!(event.getSource() instanceof Context))
  -            return;
  -        Context context = (Context) event.getSource();
  -
  -        // Process a relevant property change
  -        if (event.getPropertyName().equals("sessionTimeout")) {
  -            try {
  -                setMaxInactiveInterval
  -                    ( ((Integer) event.getNewValue()).intValue()*60 );
  -            } catch (NumberFormatException e) {
  -                log(sm.getString("standardManager.sessionTimeout",
  -                                 event.getNewValue().toString()));
  -            }
  -        }
  -
  -    }
  -
  -
       // -------------------------------------------------------- Private Methods
   
   
       /**
  -     * Invalidate all sessions that have expired.
  -     */
  -    private void processExpires() {
  -
  -        if (!started)
  -            return;
  -            
  -        long timeNow = System.currentTimeMillis();
  -        Session sessions[] = findSessions();
  -
  -        for (int i = 0; i < sessions.length; i++) {
  -            StandardSession session = (StandardSession) sessions[i];
  -            if (!session.isValid())
  -                continue;
  -            if (isSessionStale(session, timeNow))
  -                session.expire();
  -        }
  -    }
  -
  -
  -    /**
        * Swap idle sessions out to Store if they are idle too long.
        */
       private void processMaxIdleSwaps() {
   
  -        if (!started || maxIdleSwap < 0)
  +        if (!isStarted() || maxIdleSwap < 0)
               return;
   
           Session sessions[] = findSessions();
  @@ -729,7 +400,11 @@
                           log(sm.getString
                               ("persistentManager.swapMaxIdle", 
                                session.getId(), new Integer(timeIdle)));
  -                    swapOut(session);
  +                    try {
  +                        swapOut(session);
  +                    } catch (IOException e) {
  +                        ;   // This is logged in writeSession()
  +                    }
                   }
               }
           }
  @@ -742,13 +417,13 @@
        */
       private void processMaxActiveSwaps() {
   
  -        if (!started || maxActiveSessions < 0)
  +        if (!isStarted() || getMaxActiveSessions() < 0)
               return;
   
           Session sessions[] = findSessions();
           
           // FIXME: Smarter algorithm (LRU)
  -        if (maxActiveSessions >= sessions.length)
  +        if (getMaxActiveSessions() >= sessions.length)
               return;
   
           if(debug > 0)
  @@ -756,7 +431,7 @@
                   ("persistentManager.tooManyActive", 
                    new Integer(sessions.length)));
   
  -        int toswap = sessions.length - maxActiveSessions;
  +        int toswap = sessions.length - getMaxActiveSessions();
           long timeNow = System.currentTimeMillis();
           
           for (int i = 0; i < sessions.length && toswap > 0; i++) {
  @@ -767,7 +442,11 @@
                       log(sm.getString
                           ("persistentManager.swapTooManyActive", 
                            sessions[i].getId(), new Integer(timeIdle)));
  -                swapOut(sessions[i]);
  +                try {
  +                    swapOut(sessions[i]);
  +                } catch (IOException e) {
  +                    ;   // This is logged in writeSession()
  +                }
                   toswap--;
               }
           }
  @@ -780,7 +459,7 @@
        */
       private void processMaxIdleBackups() {
   
  -        if (!started || maxIdleBackup < 0)
  +        if (!isStarted() || maxIdleBackup < 0)
               return;
           
           Session sessions[] = findSessions();
  @@ -801,203 +480,14 @@
                               session.getId(), new Integer(timeIdle)));
           
                       try {
  -                        backup(session);
  +                        writeSession(session);
                       } catch (IOException e) {
  -                        log(sm.getString
  -                            ("persistentManager.backupException", session.getId(), e));
  +                        ;   // This is logged in writeSession()
                       }
                   }
               }
           }
   
  -    }
  -
  -
  -    /**
  -     * Remove the session from the Manager's list of active 
  -     * sessions and write it out to the Store. 
  -     *
  -     * @param session The Session to write out.
  -     */
  -    private void swapOut(Session session) {
  -        
  -        if (!session.isValid() 
  -            || isSessionStale(session, System.currentTimeMillis()))
  -            return;
  -
  -        ((StandardSession)session).passivate();
  -
  -        try {
  -            store.save(session);
  -        } catch (IOException e) {
  -            log(sm.getString
  -                ("persistentManager.serializeError", session.getId(), e));
  -            throw new IllegalStateException 
  -                (sm.getString("persistentManager.serializeError", 
  -                              session.getId(), e));
  -        }
  -
  -        remove((StandardSession)session);
  -        ((StandardSession)session).recycle();
  -        recycle((StandardSession)session);
  -
  -    }
  -
  -
  -    /**
  -     * Look for a session in the Store and, if found, restore
  -     * it in the Manager's list of active sessions if appropriate.
  -     * The session will be removed from the Store after swapping
  -     * in, but will not be added to the active session list if it
  -     * is invalid or past its expiration.
  -     */
  -    private Session swapIn(String id) throws IOException {
  -    
  -        StandardSession session = null;
  -        try {
  -            session = (StandardSession)store.load(id);
  -        } catch (ClassNotFoundException e) {
  -            log(sm.getString("persistentManager.deserializeError", id, e));
  -            throw new IllegalStateException
  -                (sm.getString("persistentManager.deserializeError", id, e));
  -        }
  -
  -        if (session == null)
  -            return (null);
  -
  -        if (!session.isValid() 
  -            || isSessionStale(session, System.currentTimeMillis())) {
  -            log("session swapped in is invalid or expired");
  -            session.expire();
  -            store.remove(id);
  -            return (null);
  -        }
  -
  -        if(debug > 2)
  -            log(sm.getString("persistentManager.swapIn", id));
  -
  -        session.setManager(this);
  -        add(session);
  -        session.activate();
  -
  -        return (session);
  -    
  -    }
  -    
  -    
  -    /**
  -     * Write the session out to Store, but leave the copy in
  -     * the Manager's memory unmodified.
  -     */
  -    private void backup(Session session) throws IOException {
  -
  -        if (!session.isValid()
  -                || isSessionStale(session, System.currentTimeMillis()))
  -            return;
  -
  -        try {
  -            store.save(session);
  -        } catch (IOException e) {
  -            log(sm.getString
  -                ("persistentManager.serializeError", session.getId(), e));
  -            throw e;
  -        }
  - 
  -    }
  -
  -
  -    /**
  -     * Indicate whether the session has been idle for longer
  -     * than its expiration date as of the supplied time.
  -     *
  -     * FIXME: Probably belongs in the Session class.
  -     */
  -    private boolean isSessionStale(Session session, long timeNow) {
  -
  -        int maxInactiveInterval = session.getMaxInactiveInterval();
  -        if (maxInactiveInterval >= 0) {
  -            int timeIdle = // Truncate, do not round up
  -                (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
  -            if (timeIdle >= maxInactiveInterval)
  -                return true;
  -        }
  -        
  -        return false;
  -
  -    }
  -    
  -    
  -    /**
  -     * Sleep for the duration specified by the <code>checkInterval</code>
  -     * property.
  -     */
  -    private void threadSleep() {
  -
  -        try {
  -            Thread.sleep(checkInterval * 1000L);
  -        } catch (InterruptedException e) {
  -            ;
  -        }
  -
  -    }
  -
  -
  -    /**
  -     * Start the background thread that will periodically check for
  -     * session timeouts.
  -     */
  -    private void threadStart() {
  -
  -        if (thread != null)
  -            return;
  -
  -        threadDone = false;
  -        threadName = "PersistentManager[" + container.getName() + "]";
  -        thread = new Thread(this, threadName);
  -        thread.setDaemon(true);
  -        thread.start();
  -
  -    }
  -
  -
  -    /**
  -     * Stop the background thread that is periodically checking for
  -     * session timeouts.
  -     */
  -    private void threadStop() {
  -
  -        if (thread == null)
  -            return;
  -
  -        threadDone = true;
  -        thread.interrupt();
  -        try {
  -            thread.join();
  -        } catch (InterruptedException e) {
  -            ;
  -        }
  -
  -        thread = null;
  -
  -    }
  -
  -
  -    // ------------------------------------------------------ Background Thread
  -
  -
  -    /**
  -     * The background thread that checks for session timeouts and shutdown.
  -     */
  -    public void run() {
  -
  -        // Loop until the termination semaphore is set
  -        while (!threadDone) {
  -            threadSleep();
  -            processExpires();
  -            processMaxIdleSwaps();
  -            processMaxActiveSwaps();
  -            processMaxIdleBackups();
  -        }
       }
   
   
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/PersistentManagerBase.java
  
  Index: PersistentManagerBase.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/PersistentManagerBase.java,v 1.1 2001/04/15 09:33:27 kief Exp $
   * $Revision: 1.1 $
   * $Date: 2001/04/15 09:33:27 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  
  package org.apache.catalina.session;
  
  import java.beans.PropertyChangeEvent;
  import java.beans.PropertyChangeListener;
  import java.beans.PropertyChangeSupport;
  import java.io.BufferedInputStream;
  import java.io.BufferedOutputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.FileOutputStream;
  import java.io.InputStream;
  import java.io.IOException;
  import java.io.ObjectInputStream;
  import java.io.ObjectOutputStream;
  import java.io.ObjectStreamClass;
  import java.util.ArrayList;
  import java.util.Iterator;
  import javax.servlet.ServletContext;
  import org.apache.catalina.Container;
  import org.apache.catalina.Context;
  import org.apache.catalina.Globals;
  import org.apache.catalina.Lifecycle;
  import org.apache.catalina.LifecycleEvent;
  import org.apache.catalina.LifecycleException;
  import org.apache.catalina.LifecycleListener;
  import org.apache.catalina.Loader;
  import org.apache.catalina.Manager;
  import org.apache.catalina.Session;
  import org.apache.catalina.Store;
  import org.apache.catalina.util.LifecycleSupport;
  
  
  /**
   * Extends the <b>ManagerBase</b> class to implement most of the
   * functionality required by a Manager which supports any kind of
   * persistence, even if onlyfor  restarts.
   * <p>
   * <b>IMPLEMENTATION NOTE</b>:  Correct behavior of session storing and
   * reloading depends upon external calls to the <code>start()</code> and
   * <code>stop()</code> methods of this class at the correct times.
   *
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2001/04/15 09:33:27 $
   */
  
  public abstract class PersistentManagerBase
      extends ManagerBase
      implements Lifecycle, PropertyChangeListener, Runnable {
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * The interval (in seconds) between checks for expired sessions.
       */
      private int checkInterval = 60;
  
  
      /**
       * The descriptive information about this implementation.
       */
      private static final String info = "MiddleManager/1.0";
  
  
      /**
       * The lifecycle event support for this component.
       */
      protected LifecycleSupport lifecycle = new LifecycleSupport(this);
  
  
      /**
       * The maximum number of active Sessions allowed, or -1 for no limit.
       */
      private int maxActiveSessions = -1;
  
  
      /**
       * Has this component been started yet?
       */
      private boolean started = false;
  
  
      /**
       * The background thread.
       */
      private Thread thread = null;
  
  
      /**
       * The background thread completion semaphore.
       */
      private boolean threadDone = false;
  
  
      /**
       * Name to register for the background thread.
       */
      private String threadName = "MiddleManager";
  
  
      /**
       * Store object which will manage the Session store.
       */
      private Store store = null;
  
  
      // ------------------------------------------------------------- Properties
  
  
      /**
       * Return the check interval (in seconds) for this Manager.
       */
      public int getCheckInterval() {
  
          return (this.checkInterval);
  
      }
  
  
      /**
       * Set the check interval (in seconds) for this Manager.
       *
       * @param checkInterval The new check interval
       */
      public void setCheckInterval(int checkInterval) {
  
          int oldCheckInterval = this.checkInterval;
          this.checkInterval = checkInterval;
          support.firePropertyChange("checkInterval",
                                     new Integer(oldCheckInterval),
                                     new Integer(this.checkInterval));
  
      }
  
  
      /**
       * Set the Container with which this Manager has been associated.  If
       * it is a Context (the usual case), listen for changes to the session
       * timeout property.
       *
       * @param container The associated Container
       */
      public void setContainer(Container container) {
  
          // De-register from the old Container (if any)
          if ((this.container != null) && (this.container instanceof Context))
              ((Context) this.container).removePropertyChangeListener(this);
  
          // Default processing provided by our superclass
          super.setContainer(container);
  
          // Register with the new Container (if any)
          if ((this.container != null) && (this.container instanceof Context)) {
              setMaxInactiveInterval
                  ( ((Context) this.container).getSessionTimeout()*60 );
              ((Context) this.container).addPropertyChangeListener(this);
          }
  
      }
  
  
      /**
       * Return descriptive information about this Manager implementation and
       * the corresponding version number, in the format
       * <code>&lt;description&gt;/&lt;version&gt;</code>.
       */
      public String getInfo() {
  
          return (this.info);
  
      }
  
  
      /**
       * Return the maximum number of active Sessions allowed, or -1 for
       * no limit.
       */
      public int getMaxActiveSessions() {
  
          return (this.maxActiveSessions);
  
      }
  
  
      /**
       * Set the maximum number of actives Sessions allowed, or -1 for
       * no limit.
       *
       * @param max The new maximum number of sessions
       */
      public void setMaxActiveSessions(int max) {
  
          int oldMaxActiveSessions = this.maxActiveSessions;
          this.maxActiveSessions = max;
          support.firePropertyChange("maxActiveSessions",
                                     new Integer(oldMaxActiveSessions),
                                     new Integer(this.maxActiveSessions));
  
      }
  
  
      /**
       * Get the started status.
       */
      protected boolean isStarted() {
      
          return started;
      
      }
      
      
      /**
       * Set the started flag
       */
      protected void setStarted(boolean started) {
      
          this.started = started;
      
      }
      
  
      /**
       * Set the Store object which will manage persistent Session
       * storage for this Manager.
       *
       * @param store the associated Store
       */
      public void setStore(Store store) {
      
          this.store = store;
          store.setManager(this);
              
      }
      
  
      /**
       * Return the Store object which manages persistent Session
       * storage for this Manager.
       */
      public Store getStore() {
              
          return (this.store);
              
      }
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Clear all sessions from the Store.
       */
      public void clearStore() {
      
          if (store == null)
              return;
          
          try {
              store.clear();
          } catch (IOException e) {
              log("Exception clearing the Store: " + e);
              e.printStackTrace();
          }
      
      }
      
  
      /**
       * Return a new session object as long as the number of active
       * sessions does not exceed <b>maxActiveSessions</b>. If there
       * aren't too many active sessions, or if there is no limit,
       * a session is created or retrieved from the recycled pool.
       *
       * @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());
  
      }
  
  
      /**
       * Return the active Session, associated with this Manager, with the
       * specified session id (if any); otherwise return <code>null</code>.
       * This method checks the persistence store if persistence is enabled,
       * otherwise just uses the functionality from ManagerBase.
       *
       * @param id The session id for the session to be returned
       *
       * @exception IllegalStateException if a new session cannot be
       *  instantiated for any reason
       * @exception IOException if an input/output error occurs while
       *  processing this request
       */
      public Session findSession(String id) throws IOException {
  
          Session session = super.findSession(id);
          if (session != null)
              return (session);
  
          // See if the Session is in the Store
          session = swapIn(id);
          return (session);
  
      }
          
  
      /**
       * Load all sessions found in the persistence mechanism, assuming
       * they are marked as valid and have not passed their expiration
       * limit. If persistence is not supported, this method returns 
       * without doing anything.
       * <p>
       * Note that by default, this method is not called by the MiddleManager
       * class. In order to use it, a subclass must specifically call it,
       * for example in the start() and/or processPersistenceChecks() methods.
       *
       * @exception ClassNotFoundException if a serialized class cannot be
       *  found during the reload
       * @exception IOException if an input/output error occurs
       */
      public void load() throws ClassNotFoundException, IOException {
  
          // Initialize our internal data structures
  //        recycled.clear();
  //        sessions.clear();
  
          if (store == null)
              return;
  
          String[] ids = store.keys();
          int n = ids.length;
          if (n == 0)
              return;
              
          if (debug >= 1)
              log(sm.getString("persistentManager.loading", String.valueOf(n)));
  
          for (int i = 0; i < n; i++)
              swapIn(ids[i]);
          
      }
  
  
      /**
       * Called by the background thread after active sessions have
       * been checked for expiration, to allow sessions to be
       * swapped out, backed up, etc. This method must be implemented,
       * but may be empty if it isn't needed.
       */
      public abstract void processPersistenceChecks();
  
  
      /**
       * Save all currently active sessions in the appropriate persistence
       * mechanism, if any.  If persistence is not supported, this method
       * returns without doing anything.
       * <p>
       * Note that by default, this method is not called by the MiddleManager
       * class. In order to use it, a subclass must specifically call it,
       * for example in the stop() and/or processPersistenceChecks() methods.
       *
       * @exception IOException if an input/output error occurs
       */
      public void unload() {
  
          if (store == null)
              return;
  
          Session sessions[] = findSessions();
          int n = sessions.length;
          if (n == 0)
              return;
              
          if (debug >= 1)
              log(sm.getString("persistentManager.unloading", 
                               String.valueOf(n)));
  
          for (int i = 0; i < n; i++)
              try {
                  swapOut(sessions[i]);
              } catch (IOException e) {
                  ;   // This is logged in writeSession()
              }
  
      }
  
  
      // ------------------------------------------------------ Protected Methods
  
  
      /**
       * Look for a session in the Store and, if found, restore
       * it in the Manager's list of active sessions if appropriate.
       * The session will be removed from the Store after swapping
       * in, but will not be added to the active session list if it
       * is invalid or past its expiration.
       */
      protected Session swapIn(String id) throws IOException {
  
          if (store == null)
              return null;
          
          Session session = null;
          try {
              session = store.load(id);
          } catch (ClassNotFoundException e) {
              log(sm.getString("persistentManager.deserializeError", id, e));
              throw new IllegalStateException
                  (sm.getString("persistentManager.deserializeError", id, e));
          }
  
          if (session == null)
              return (null);
  
          if (!session.isValid() 
                  || isSessionStale(session, System.currentTimeMillis())) {
              log("session swapped in is invalid or expired");
              session.expire();
              store.remove(id);
              return (null);
          }
  
          if(debug > 2)
              log(sm.getString("persistentManager.swapIn", id));
  
          session.setManager(this);
          add(session);
          ((StandardSession)session).activate();
  
          return (session);
      
      }
      
      
      /**
       * Remove the session from the Manager's list of active 
       * sessions and write it out to the Store. If the session
       * is past its expiration or invalid, this method does
       * nothing.
       *
       * @param session The Session to write out.
       */
      protected void swapOut(Session session) throws IOException {
          
          if (store == null ||
                  !session.isValid() ||
                  isSessionStale(session, System.currentTimeMillis()))
              return;
  
          ((StandardSession)session).passivate();
          writeSession(session);
          session.expire();
  
      }
  
  
      /**
       * Write the provided session to the Store without modifying 
       * the copy in memory or triggering passivation events. Does 
       * nothing if the session is invalid or past its expiration.
       */
      protected void writeSession(Session session) throws IOException {
  
          if (store == null ||
                  !session.isValid() ||
                  isSessionStale(session, System.currentTimeMillis()))
              return;
  
          try {
              store.save(session);
          } catch (IOException e) {
              log(sm.getString
                  ("persistentManager.serializeError", session.getId(), e));
              throw e;
          }
   
      }
  
  
      // ------------------------------------------------------ Lifecycle Methods
  
  
      /**
       * Add a lifecycle event listener to this component.
       *
       * @param listener The listener to add
       */
      public void addLifecycleListener(LifecycleListener listener) {
  
          lifecycle.addLifecycleListener(listener);
  
      }
  
  
      /**
       * Remove a lifecycle event listener from this component.
       *
       * @param listener The listener to remove
       */
      public void removeLifecycleListener(LifecycleListener listener) {
  
          lifecycle.removeLifecycleListener(listener);
  
      }
  
  
      /**
       * Prepare for the beginning of active use of the public methods of this
       * component.  This method should be called after <code>configure()</code>,
       * and before any of the public methods of the component are utilized.
       *
       * @exception IllegalStateException if this component has already been
       *  started
       * @exception LifecycleException if this component detects a fatal error
       *  that prevents this component from being used
       */
      public void start() throws LifecycleException {
  
          if (debug >= 1)
              log("Starting");
  
          // Validate and update our current component state
          if (started)
              throw new LifecycleException
                  (sm.getString("standardManager.alreadyStarted"));
          lifecycle.fireLifecycleEvent(START_EVENT, null);
          started = true;
  
          // Force initialization of the random number generator
          if (debug >= 1)
              log("Force random number initialization starting");
          String dummy = generateSessionId();
          if (debug >= 1)
              log("Force random number initialization completed");
  
          if (store == null)
              log("No Store configured, persistence disabled");
          else if (store instanceof Lifecycle)
              ((Lifecycle)store).start();
              
          // Start the background reaper thread
          threadStart();
  
      }
  
  
      /**
       * Gracefully terminate the active use of the public methods of this
       * component.  This method should be the last one called on a given
       * instance of this component.
       *
       * @exception IllegalStateException if this component has not been started
       * @exception LifecycleException if this component detects a fatal error
       *  that needs to be reported
       */
      public void stop() throws LifecycleException {
  
          if (debug >= 1)
              log("Stopping");
  
          // Validate and update our current component state
          if (!started)
              throw new LifecycleException
                  (sm.getString("standardManager.notStarted"));
          lifecycle.fireLifecycleEvent(STOP_EVENT, null);
          started = false;
  
          // Stop the background reaper thread
          threadStop();
  
          // Expire all active sessions
          Session sessions[] = findSessions();
          for (int i = 0; i < sessions.length; i++) {
              StandardSession session = (StandardSession) sessions[i];
              if (!session.isValid())
                  continue;
              session.expire();
          }
  
          // Require a new random number generator if we are restarted
          this.random = null;
  
      }
  
  
      // ----------------------------------------- PropertyChangeListener Methods
  
  
      /**
       * Process property change events from our associated Context.
       *
       * @param event The property change event that has occurred
       */
      public void propertyChange(PropertyChangeEvent event) {
  
          // Validate the source of this event
          if (!(event.getSource() instanceof Context))
              return;
          Context context = (Context) event.getSource();
  
          // Process a relevant property change
          if (event.getPropertyName().equals("sessionTimeout")) {
              try {
                  setMaxInactiveInterval
                      ( ((Integer) event.getNewValue()).intValue()*60 );
              } catch (NumberFormatException e) {
                  log(sm.getString("standardManager.sessionTimeout",
                                   event.getNewValue().toString()));
              }
          }
  
      }
  
  
      // -------------------------------------------------------- Private Methods
  
  
      /**
       * Indicate whether the session has been idle for longer
       * than its expiration date as of the supplied time.
       *
       * FIXME: Probably belongs in the Session class.
       */
      private boolean isSessionStale(Session session, long timeNow) {
  
          int maxInactiveInterval = session.getMaxInactiveInterval();
          if (maxInactiveInterval >= 0) {
              int timeIdle = // Truncate, do not round up
                  (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
              if (timeIdle >= maxInactiveInterval)
                  return true;
          }
          
          return false;
  
      }
      
      
      /**
       * Invalidate all sessions that have expired.
       */
      private void processExpires() {
  
          if (!started)
              return;
              
          long timeNow = System.currentTimeMillis();
          Session sessions[] = findSessions();
  
          for (int i = 0; i < sessions.length; i++) {
              StandardSession session = (StandardSession) sessions[i];
              if (!session.isValid())
                  continue;
              if (isSessionStale(session, timeNow))
                  session.expire();
          }
  
      }
  
  
      /**
       * Sleep for the duration specified by the <code>checkInterval</code>
       * property.
       */
      private void threadSleep() {
  
          try {
              Thread.sleep(checkInterval * 1000L);
          } catch (InterruptedException e) {
              ;
          }
  
      }
  
  
      /**
       * Start the background thread that will periodically check for
       * session timeouts.
       */
      private void threadStart() {
  
          if (thread != null)
              return;
  
          threadDone = false;
          threadName = "StandardManager[" + container.getName() + "]";
          thread = new Thread(this, threadName);
          thread.setDaemon(true);
          thread.start();
  
      }
  
  
      /**
       * Stop the background thread that is periodically checking for
       * session timeouts.
       */
      protected void threadStop() {
  
          if (thread == null)
              return;
  
          threadDone = true;
          thread.interrupt();
          try {
              thread.join();
          } catch (InterruptedException e) {
              ;
          }
  
          thread = null;
  
      }
  
  
      // ------------------------------------------------------ Background Thread
  
  
      /**
       * The background thread that checks for session timeouts and shutdown.
       */
      public void run() {
  
          // Loop until the termination semaphore is set
          while (!threadDone) {
              threadSleep();
              processExpires();
              processPersistenceChecks();
          }
  
      }
  
  
  }