You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by cr...@locus.apache.org on 2000/08/12 01:39:22 UTC

cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session Constants.java FileStore.java LocalStrings.properties ManagerBase.java StandardManager.java StandardSession.java package.html

craigmcc    00/08/11 16:39:19

  Added:       catalina/src/share/org/apache/catalina/session
                        Constants.java FileStore.java
                        LocalStrings.properties ManagerBase.java
                        StandardManager.java StandardSession.java
                        package.html
  Log:
  Migrate to Tomcat 4.0.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/Constants.java
  
  Index: Constants.java
  ===================================================================
  /*
   * ====================================================================
   *
   * 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;
  
  /**
   * Manifest constants for the <code>org.apache.catalina.session</code>
   * package.
   *
   * @author Craig R. McClanahan
   */
  
  public class Constants {
  
      public static final String Package = "org.apache.catalina.session";
  
      public static final String SESSION_COOKIE_NAME = "JSESSIONID";
      public static final String SESSION_PARAMETER_NAME = "jsessionid";
      public static final String WORKDIR_ATTR =
  	"javax.servlet.context.tempdir";
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/FileStore.java
  
  Index: FileStore.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/FileStore.java,v 1.1 2000/08/11 23:39:15 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 23:39:15 $
   *
   * ====================================================================
   *
   * 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.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.IOException;
  import java.io.ObjectInputStream;
  import java.io.ObjectOutputStream;
  import java.io.Serializable;
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.Vector;
  import org.apache.catalina.Lifecycle;
  import org.apache.catalina.LifecycleEvent;
  import org.apache.catalina.LifecycleException;
  import org.apache.catalina.LifecycleListener;
  import org.apache.catalina.Session;
  import org.apache.catalina.Store;
  import org.apache.catalina.util.LifecycleSupport;
  import org.apache.catalina.util.StringManager;
  
  
  /**
   * Concrete implementation of the <b>Store</b> interface that utilizes
   * a file per saved Session in a configured directory.  Sessions that are
   * saved are still subject to being expired based on inactivity.
   *
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2000/08/11 23:39:15 $
   */
  
  public final class FileStore
      implements Lifecycle, Runnable, Store {
  
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * The interval (in seconds) between checks for expired sessions.
       */
      private int checkInterval = 60;
  
  
      /**
       * The pathname of the directory in which Sessions are stored.
       */
      private String directoryPath = "./STORE";
  
  
      /**
       * The descriptive information about this implementation.
       */
      private static final String info = "FileStore/1.0";
  
  
      /**
       * The lifecycle event support for this component.
       */
      protected LifecycleSupport lifecycle = new LifecycleSupport(this);
  
  
      /**
       * The string manager for this package.
       */
      private StringManager sm =
  	StringManager.getManager(Constants.Package);
  
  
      /**
       * Has this component been started yet?
       */
      private boolean started = false;
  
  
      /**
       * The property change support for this component.
       */
      private PropertyChangeSupport support = new PropertyChangeSupport(this);
  
  
      /**
       * 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 = "FileStore";
  
  
      // ------------------------------------------------------------- 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));
  
      }
  
  
      /**
       * Return the directory path for this Store.
       */
      public String getDirectoryPath() {
  
  	return (directoryPath);
  
      }
  
  
      /**
       * Set the directory path for this Store.
       *
       * @param path The new directory path
       */
      public void setDirectoryPath(String path) {
  
  	String oldDirectoryPath = this.directoryPath;
  	this.directoryPath = path;
  	support.firePropertyChange("directoryPath", oldDirectoryPath,
  				   this.directoryPath);
  
      }
  
  
      /**
       * Return descriptive information about this Store implementation and
       * the corresponding version number, in the format
       * <code>&lt;description&gt;/&lt;version&gt;</code>.
       */
      public String getInfo() {
  
  	return (info);
  
      }
  
  
      /**
       * Return the number of Sessions present in this Store.
       *
       * @exception IOException if an input/output error occurs
       */
      public int getSize() throws IOException {
  
  	return (0);	// FIXME: getSize()
  
      }
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Add a property change listener to this component.
       *
       * @param listener The listener to add
       */
      public void addPropertyChangeListener(PropertyChangeListener listener) {
  
  	support.addPropertyChangeListener(listener);
  
      }
  
  
      /**
       * Return an array containing the session identifiers of all Sessions
       * currently saved in this Store.  If there are no such Sessions, a
       * zero-length array is returned.
       *
       * @exception IOException if an input/output error occurred
       */
      public String[] keys() throws IOException {
  
  	return (new String[0]);	// FIXME: keys()
  
      }
  
  
      /**
       * Load and return the Session associated with the specified session
       * identifier from this Store, without removing it.  If there is no
       * such stored Session, return <code>null</code>.
       *
       * @param id Session identifier of the session to load
       *
       * @exception ClassNotFoundException if a deserialization error occurs
       * @exception IOException if an input/output error occurs
       */
      public Session load(String id)
          throws ClassNotFoundException, IOException {
  
  	return (null);	// FIXME: load()
  
      }
  
  
      /**
       * Remove the Session with the specified session identifier from
       * this Store, if present.  If no such Session is present, this method
       * takes no action.
       *
       * @param id Session identifier of the Session to be removed
       *
       * @exception IOException if an input/output error occurs
       */
      public void remove(String id) throws IOException {
  
  	;	// FIXME: remove()
  
      }
  
  
      /**
       * Remove a property change listener from this component.
       *
       * @param listener The listener to remove
       */
      public void removePropertyChangeListener(PropertyChangeListener listener) {
  
  	support.removePropertyChangeListener(listener);
  
      }
  
  
      /**
       * Save the specified Session into this Store.  Any previously saved
       * information for the associated session identifier is replaced.
       *
       * @param session Session to be saved
       *
       * @exception IOException if an input/output error occurs
       */
      public void save(Session session) throws IOException {
  
  	;	// FIXME: save()
  
      }
  
  
      // ------------------------------------------------------ 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 add
       */
      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 {
  
  	// Validate and update our current component state
  	if (started)
  	    throw new LifecycleException
  		(sm.getString("fileStore.alreadyStarted"));
  	lifecycle.fireLifecycleEvent(START_EVENT, null);
  	started = true;
  
  	// 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 {
  
  	// Validate and update our current component state
  	if (!started)
  	    throw new LifecycleException
  		(sm.getString("fileStore.notStarted"));
  	lifecycle.fireLifecycleEvent(STOP_EVENT, null);
  	started = false;
  
  	// Stop the background reaper thread
  	threadStop();
  
      }
  
  
      // -------------------------------------------------------- Private Methods
  
  
      /**
       * Invalidate all sessions that have expired.
       */
      private void processExpires() {
  
  	long timeNow = System.currentTimeMillis();
  	/*
  	Session sessions[] = findSessions();
  
  	for (int i = 0; i < sessions.length; i++) {
  	    StandardSession session = (StandardSession) sessions[i];
  	    if (!session.isValid())
  		continue;
  	    int maxInactiveInterval = session.getMaxInactiveInterval();
  	    if (maxInactiveInterval < 0)
  		continue;
  	    int timeIdle = // Truncate, do not round up
  		(int) ((timeNow - session.getLastAccessedTime()) / 1000L);
  	    if (timeIdle >= maxInactiveInterval)
  		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;
  	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();
  	}
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/LocalStrings.properties
  
  Index: LocalStrings.properties
  ===================================================================
  applicationSession.session.ise=invalid session state
  applicationSession.value.iae=null value
  fileStore.alreadyStarted=File Store has already been started
  fileStore.notStarted=File Store has not yet been started
  managerBase.complete=Seeding of random number generator has been completed
  managerBase.getting=Getting message digest component for algorithm {0}
  managerBase.gotten=Completed getting message digest component
  managerBase.random=Exception initializing random number generator of class {0}
  managerBase.seeding=Seeding random number generator class {0}
  serverSession.value.iae=null value
  standardManager.alreadyStarted=Manager has already been started
  standardManager.createSession.ise=createSession: Too many active sessions
  standardManager.loading=Loading persisted sessions from {0}
  standardManager.notStarted=Manager has not yet been started
  standardManager.sessionTimeout=Invalid session timeout setting {0}
  standardManager.unloading=Saving persisted sessions to {0}
  standardSession.invalidate.ise=invalidate: Session already invalidated
  standardSession.isNew.ise=isNew: Session already invalidated
  standardSession.getAttribute.ise=getAttribute: Session already invalidated
  standardSession.getAttributeNames.ise=getAttributeNames: Session already invalidated
  standardSession.getCreationTime.ise=getCreationTime: Session already invalidated
  standardSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: Session already invalidated
  standardSession.getValueNames.ise=getAttributeNames: Session already invalidated
  standardSession.notSerializable=Cannot serialize session attribute {0} for session {1}
  standardSession.removeAttribute.ise=removeAttribute: Session already invalidated
  standardSession.setAttribute.ise=setAttribute: Non-serializable attribute
  standardSession.setAttribute.ise=setAttribute: Session already invalidated
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/ManagerBase.java
  
  Index: ManagerBase.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/ManagerBase.java,v 1.1 2000/08/11 23:39:15 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 23:39:15 $
   *
   * ====================================================================
   *
   * 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.PropertyChangeListener;
  import java.beans.PropertyChangeSupport;
  import java.io.IOException;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Random;
  import org.apache.catalina.Container;
  import org.apache.catalina.Logger;
  import org.apache.catalina.Manager;
  import org.apache.catalina.Session;
  import org.apache.catalina.util.StringManager;
  
  
  /**
   * Minimal implementation of the <b>Manager</b> interface that supports
   * no session persistence or distributable capabilities.  This class may
   * be subclassed to create more sophisticated Manager implementations.
   *
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2000/08/11 23:39:15 $
   */
  
  public abstract class ManagerBase implements Manager {
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * The default message digest algorithm to use if we cannot use
       * the requested one.
       */
      protected static final String DEFAULT_ALGORITHM = "MD5";
  
  
      /**
       * The number of random bytes to include when generating a
       * session identifier.
       */
      protected static final int SESSION_ID_BYTES = 16;
  
  
      /**
       * The message digest algorithm to be used when generating session
       * identifiers.  This must be an algorithm supported by the
       * <code>java.security.MessageDigest</code> class on your platform.
       */
      protected String algorithm = DEFAULT_ALGORITHM;
  
  
      /**
       * The Container with which this Manager is associated.
       */
      protected Container container;
  
  
      /**
       * The debugging detail level for this component.
       */
      protected int debug = 0;
  
  
      /**
       * Return the MessageDigest implementation to be used when
       * creating session identifiers.
       */
      protected MessageDigest digest = null;
  
  
      /**
       * The distributable flag for Sessions created by this Manager.  If this
       * flag is set to <code>true</code>, any user attributes added to a
       * session controlled by this Manager must be Serializable.
       */
      protected boolean distributable;
  
  
      /**
       * A String initialization parameter used to increase the entropy of
       * the initialization of our random number generator.
       */
      protected String entropy = null;
  
  
      /**
       * The descriptive information string for this implementation.
       */
      private static final String info = "ManagerBase/1.0";
  
  
      /**
       * The default maximum inactive interval for Sessions created by
       * this Manager.
       */
      protected int maxInactiveInterval = 60;
  
  
      /**
       * A random number generator to use when generating session identifiers.
       */
      protected Random random = null;
  
  
      /**
       * The Java class name of the random number generator class to be used
       * when generating session identifiers.
       */
      protected String randomClass = "java.security.SecureRandom";
  
  
      /**
       * The set of previously recycled Sessions for this Manager.
       */
      protected ArrayList recycled = new ArrayList();
  
  
      /**
       * The set of currently active Sessions for this Manager, keyed by
       * session identifier.
       */
      protected HashMap sessions = new HashMap();
  
  
      /**
       * The string manager for this package.
       */
      protected static StringManager sm =
          StringManager.getManager(Constants.Package);
  
  
      /**
       * The property change support for this component.
       */
      protected PropertyChangeSupport support = new PropertyChangeSupport(this);
  
  
      // ------------------------------------------------------------- Properties
  
  
      /**
       * Return the message digest algorithm for this Manager.
       */
      public String getAlgorithm() {
  
  	return (this.algorithm);
  
      }
  
  
      /**
       * Set the message digest algorithm for this Manager.
       *
       * @param algorithm The new message digest algorithm
       */
      public void setAlgorithm(String algorithm) {
  
  	String oldAlgorithm = this.algorithm;
  	this.algorithm = algorithm;
  	support.firePropertyChange("algorithm", oldAlgorithm, this.algorithm);
  
      }
  
  
      /**
       * Return the Container with which this Manager is associated.
       */
      public Container getContainer() {
  
  	return (this.container);
  
      }
  
  
      /**
       * Set the Container with which this Manager is associated.
       *
       * @param container The newly associated Container
       */
      public void setContainer(Container container) {
  
  	Container oldContainer = this.container;
  	this.container = container;
  	support.firePropertyChange("container", oldContainer, this.container);
  
      }
  
  
      /**
       * Return the debugging detail level for this component.
       */
      public int getDebug() {
  
  	return (this.debug);
  
      }
  
  
      /**
       * Set the debugging detail level for this component.
       *
       * @param debug The new debugging detail level
       */
      public void setDebug(int debug) {
  
  	this.debug = debug;
  
      }
  
  
      /**
       * Return the MessageDigest object to be used for calculating
       * session identifiers.  If none has been created yet, initialize
       * one the first time this method is called.
       */
      public synchronized MessageDigest getDigest() {
  
  	if (this.digest == null) {
  	    if (debug >= 1)
  		log(sm.getString("managerBase.getting", algorithm));
  	    try {
  		this.digest = MessageDigest.getInstance(algorithm);
  	    } catch (NoSuchAlgorithmException e) {
  		log(sm.getString("managerBase.digest", algorithm), e);
  		try {
  		    this.digest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
  		} catch (NoSuchAlgorithmException f) {
  		    log(sm.getString("managerBase.digest",
  				     DEFAULT_ALGORITHM), e);
  		    this.digest = null;
  		}
  	    }
  	    if (debug >= 1)
  		log(sm.getString("managerBase.gotten"));
  	}
  
  	return (this.digest);
  
      }
  
  
      /**
       * Return the distributable flag for the sessions supported by
       * this Manager.
       */
      public boolean getDistributable() {
  
  	return (this.distributable);
  
      }
  
  
      /**
       * Set the distributable flag for the sessions supported by this
       * Manager.  If this flag is set, all user data objects added to
       * sessions associated with this manager must implement Serializable.
       *
       * @param distributable The new distributable flag
       */
      public void setDistributable(boolean distributable) {
  
  	boolean oldDistributable = this.distributable;
  	this.distributable = distributable;
  	support.firePropertyChange("distributable",
  				   new Boolean(oldDistributable),
  				   new Boolean(this.distributable));
  
      }
  
  
      /**
       * Return the entropy increaser value, or compute a semi-useful value
       * if this String has not yet been set.
       */
      public String getEntropy() {
  
  	// Calculate a semi-useful value if this has not been set
  	if (this.entropy == null)
  	    setEntropy(this.toString());
  
  	return (this.entropy);
  
      }
  
  
      /**
       * Set the entropy increaser value.
       *
       * @param entropy The new entropy increaser value
       */
      public void setEntropy(String entropy) {
  
  	String oldEntropy = entropy;
  	this.entropy = entropy;
  	support.firePropertyChange("entropy", oldEntropy, this.entropy);
  
      }
  
  
      /**
       * 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 default maximum inactive interval (in seconds)
       * for Sessions created by this Manager.
       */
      public int getMaxInactiveInterval() {
  
  	return (this.maxInactiveInterval);
  
      }
  
  
      /**
       * Set the default maximum inactive interval (in seconds)
       * for Sessions created by this Manager.
       *
       * @param interval The new default value
       */
      public void setMaxInactiveInterval(int interval) {
  
  	int oldMaxInactiveInterval = this.maxInactiveInterval;
  	this.maxInactiveInterval = interval;
  	support.firePropertyChange("maxInactiveInterval",
  				   new Integer(oldMaxInactiveInterval),
  				   new Integer(this.maxInactiveInterval));
  
      }
  
  
      /**
       * Return the random number generator instance we should use for
       * generating session identifiers.  If there is no such generator
       * currently defined, construct and seed a new one.
       */
      public synchronized Random getRandom() {
  
  	if (this.random == null) {
  	    log(sm.getString("managerBase.seeding", randomClass));
  	    try {
  		Class clazz = Class.forName(randomClass);
  		this.random = (Random) clazz.newInstance();
  		long seed = System.currentTimeMillis();
  		char entropy[] = getEntropy().toCharArray();
  		for (int i = 0; i < entropy.length; i++) {
  		    long update = ((byte) entropy[i]) << ((i % 8) * 8);
  		    seed ^= update;		    
  		}
  	    } catch (Exception e) {
  		log(sm.getString("managerBase.random", randomClass), e);
  		this.random = new java.util.Random();
  	    }
  	    log(sm.getString("managerBase.complete", randomClass));
  	}
  
  	return (this.random);
  
      }
  
  
      /**
       * Return the random number generator class name.
       */
      public String getRandomClass() {
  
  	return (this.randomClass);
  
      }
  
  
      /**
       * Set the random number generator class name.
       *
       * @param randomClass The new random number generator class name
       */
      public void setRandomClass(String randomClass) {
  
  	String oldRandomClass = this.randomClass;
  	this.randomClass = randomClass;
  	support.firePropertyChange("randomClass", oldRandomClass,
  				   this.randomClass);
  
      }
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Add a property change listener to this component.
       *
       * @param listener The listener to add
       */
      public void addPropertyChangeListener(PropertyChangeListener listener) {
  
  	support.addPropertyChangeListener(listener);
  
      }
  
  
      /**
       * 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() {
  
  	// Recycle or create a Session instance
  	StandardSession session = null;
  	synchronized (recycled) {
  	    int size = recycled.size();
  	    if (size > 0) {
  		session = (StandardSession) recycled.get(size - 1);
  		recycled.remove(size - 1);
  	    }
  	}
  	if (session == null)
  	    session = new StandardSession(this);
  
  	// Initialize the properties of the new session and return it
  	session.setNew(true);
  	session.setValid(true);
  	session.setCreationTime(System.currentTimeMillis());
  	session.setMaxInactiveInterval(this.maxInactiveInterval);
  	String sessionId = generateSessionId();
  	/*
  	synchronized (sessions) {
  	    while (sessions.get(sessionId) != null)	// Guarantee uniqueness
  		sessionId = generateSessionId();
  	}
  	*/
  	session.setId(sessionId);
  
  	return (session);
  
      }
  
  
      /**
       * 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 {
  
  	if (id == null)
  	    return (null);
  	synchronized (sessions) {
  	    return ((Session) sessions.get(id));
  	}
  
      }
  
  
      /**
       * Return the set of active Sessions associated with this Manager.
       * 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);
  
      }
  
  
      /**
       * Remove a property change listener from this component.
       *
       * @param listener The listener to remove
       */
      public void removePropertyChangeListener(PropertyChangeListener listener) {
  
  	support.removePropertyChangeListener(listener);
  
      }
  
  
      // ------------------------------------------------------ Protected Methods
  
  
      /**
       * Generate and return a new session identifier.
       */
      protected synchronized String generateSessionId() {
  
  	// Generate a byte array containing a session identifier
  	Random random = getRandom();
  	byte bytes[] = new byte[SESSION_ID_BYTES];
  	getRandom().nextBytes(bytes);
  	bytes = getDigest().digest(bytes);
  
  	// Render the result as a String of hexadecimal digits
  	StringBuffer result = new StringBuffer();
  	for (int i = 0; i < bytes.length; i++) {
  	    byte b1 = (byte) ((bytes[i] & 0xf0) >> 4);
  	    byte b2 = (byte) (bytes[i] & 0x0f);
  	    if (b1 < 10)
  		result.append((char) ('0' + b1));
  	    else
  		result.append((char) ('A' + (b1 - 10)));
  	    if (b2 < 10)
  		result.append((char) ('0' + b2));
  	    else
  		result.append((char) ('0' + (b2 - 10)));
  	}
  	return (result.toString());
  
      }
  
  
      // -------------------------------------------------------- Package Methods
  
  
      /**
       * Add this Session to the set of active Sessions for this Manager.
       *
       * @param session Session to be added
       */
      void add(StandardSession session) {
  
  	synchronized (sessions) {
  	    sessions.put(session.getId(), session);
  	}
  
      }
  
  
      /**
       * Log a message on the Logger associated with our Container (if any).
       *
       * @param message Message to be logged
       */
      void log(String message) {
  
  	Logger logger = null;
  	if (container != null)
  	    logger = container.getLogger();
  	if (logger != null)
  	    logger.log("Manager[" + container.getName() + "]: "
  		       + message);
  	else {
  	    String containerName = null;
  	    if (container != null)
  		containerName = container.getName();
  	    System.out.println("Manager[" + containerName
  			       + "]: " + message);
  	}
  
      }
  
  
      /**
       * Log a message on the Logger associated with our Container (if any).
       *
       * @param message Message to be logged
       * @param throwable Associated exception
       */
      void log(String message, Throwable throwable) {
  
  	Logger logger = null;
  	if (container != null)
  	    logger = container.getLogger();
  	if (logger != null)
  	    logger.log("Manager[" + container.getName() + "] "
  		       + message, throwable);
  	else {
  	    String containerName = null;
  	    if (container != null)
  		containerName = container.getName();
  	    System.out.println("Manager[" + containerName
  			       + "]: " + message);
  	    throwable.printStackTrace(System.out);
  	}
  
      }
  
  
      /**
       * Add this Session to the recycle collection for this Manager.
       *
       * @param session Session to be recycled
       */
      void recycle(StandardSession session) {
  
  	synchronized (recycled) {
  	    recycled.add(session);
  	}
  
      }
  
  
      /**
       * Remove this Session from the active Sessions for this Manager.
       *
       * @param session Session to be removed
       */
      void remove(StandardSession session) {
  
  	synchronized (sessions) {
  	    sessions.remove(session.getId());
  	}
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/StandardManager.java
  
  Index: StandardManager.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/StandardManager.java,v 1.1 2000/08/11 23:39:15 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 23:39:15 $
   *
   * ====================================================================
   *
   * 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.Iterator;
  import javax.servlet.ServletContext;
  import org.apache.catalina.Container;
  import org.apache.catalina.Context;
  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.util.LifecycleSupport;
  
  
  /**
   * Standard implementation of the <b>Manager</b> interface that provides
   * simple session persistence across restarts of this component (such as
   * when the entire server is shut down and restarted, or when a particular
   * web application is reloaded.
   * <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: 2000/08/11 23:39:15 $
   */
  
  public final class StandardManager
      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 = "StandardManager/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;
  
  
      /**
       * Path name of the disk file in which active sessions are saved
       * when we stop, and from which these sessions are loaded when we start.
       * A <code>null</code> value indicates that no persistence is desired.
       * If this pathname is relative, it will be resolved against the
       * temporary working directory provided by our context, available via
       * the <code>javax.servlet.context.tempdir</code> context attribute.
       */
      //    private String pathname = "sessions.ser";
      private String pathname = null;
  
  
      /**
       * 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 = "StandardManager";
  
  
      // ------------------------------------------------------------- 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));
  
      }
  
  
      /**
       * Return the session persistence pathname, if any.
       */
      public String getPathname() {
  
  	return (this.pathname);
  
      }
  
  
      /**
       * Set the session persistence pathname to the specified value.  If no
       * persistence support is desired, set the pathname to <code>null</code>.
       *
       * @param pathname New session persistence pathname
       */
      public void setPathname(String pathname) {
  
  	String oldPathname = this.pathname;
  	this.pathname = pathname;
  	support.firePropertyChange("pathname", oldPathname, this.pathname);
  
      }
  
  
      // --------------------------------------------------------- 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());
  
      }
  
  
      // ------------------------------------------------------ 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 {
  
  	// Validate and update our current component state
  	if (started)
  	    throw new LifecycleException
  		(sm.getString("standardManager.alreadyStarted"));
  	lifecycle.fireLifecycleEvent(START_EVENT, null);
  	started = true;
  
  	// Load any previously persisted sessions
  	load();
  
  	// 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 {
  
  	// 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();
  
  	// Save any currently active sessions
  	unload();
  
  	// 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
  
  
      /**
       * Return a File object representing the pathname to our
       * persistence file, if any.
       */
      private File file() {
  
  	if (pathname == null)
  	    return (null);
  	File file = new File(pathname);
  	if (!file.isAbsolute()) {
  	    if (container instanceof Context) {
  		ServletContext servletContext =
  		    ((Context) container).getServletContext();
  		File tempdir = (File)
  		    servletContext.getAttribute(Constants.WORKDIR_ATTR);
  		if (tempdir != null)
  		    file = new File(tempdir, pathname);
  	    }
  	}
  	if (!file.isAbsolute())
  	    return (null);
  	return (file);
  
      }
  
  
      /**
       * Load any currently active sessions that were previously unloaded
       * to the specified persistence file, if any.
       *
       * @exception LifecycleException if a fatal error is encountered
       */
      private void load() throws LifecycleException {
  
  	// Initialize our internal data structures
  	recycled.clear();
  	sessions.clear();
  
  	// Open an input stream to the specified pathname, if any
  	File file = file();
  	if (file == null)
  	    return;
  	if (debug >= 1)
  	    log(sm.getString("standardManager.loading", pathname));
  	FileInputStream fis = null;
  	ObjectInputStream ois = null;
  	Loader loader = null;
  	ClassLoader classLoader = null;
  	try {
  	    fis = new FileInputStream(file.getAbsolutePath());
  	    BufferedInputStream bis = new BufferedInputStream(fis);
  	    if (container != null)
  		loader = container.getLoader();
  	    if (loader != null)
  		classLoader = loader.getClassLoader();
  	    if (classLoader != null)
  		ois = new CustomObjectInputStream(bis, classLoader);
  	    else
  		ois = new ObjectInputStream(bis);
  	} catch (FileNotFoundException e) {
  	    return;
  	} catch (IOException e) {
  	    if (ois != null) {
  		try {
  		    ois.close();
  		} catch (IOException f) {
  		    ;
  		}
  		ois = null;
  	    }
  	    throw new LifecycleException("load/open: IOException", e);
  	}
  
  	// Load the previously unloaded active sessions
  	synchronized (sessions) {
  	    try {
  		Integer count = (Integer) ois.readObject();
  		int n = count.intValue();
  		for (int i = 0; i < n; i++) {
  		    Session session = (Session) ois.readObject();
  		    session.setManager(this);
  		    sessions.put(session.getId(), session);
  		}
  	    } catch (ClassNotFoundException e) {
  		if (ois != null) {
  		    try {
  			ois.close();
  		    } catch (IOException f) {
  			;
  		    }
  		    ois = null;
  		}
  		throw new LifecycleException
  		    ("load/read: ClassNotFoundException", e);
  	    } catch (IOException e) {
  		if (ois != null) {
  		    try {
  			ois.close();
  		    } catch (IOException f) {
  			;
  		    }
  		    ois = null;
  		}
  		throw new LifecycleException("load/read: IOException", e);
  	    }
  	}
  
  	// Close the input stream
  	try {
  	    ois.close();
  	} catch (IOException f) {
  	    ;
  	}
  
      }
  
  
      /**
       * Invalidate all sessions that have expired.
       */
      private void processExpires() {
  
  	long timeNow = System.currentTimeMillis();
  	Session sessions[] = findSessions();
  
  	for (int i = 0; i < sessions.length; i++) {
  	    StandardSession session = (StandardSession) sessions[i];
  	    if (!session.isValid())
  		continue;
  	    int maxInactiveInterval = session.getMaxInactiveInterval();
  	    if (maxInactiveInterval < 0)
  		continue;
  	    int timeIdle = // Truncate, do not round up
  		(int) ((timeNow - session.getLastAccessedTime()) / 1000L);
  	    if (timeIdle >= maxInactiveInterval)
  		session.expire();
  	}
      }
  
  
      /**
       * Save any currently active sessions in the specified persistence file,
       * if any.
       *
       * @exception LifecycleException if a fatal error is encountered
       */
      private void unload() throws LifecycleException {
  
  	// Open an output stream to the specified pathname, if any
  	File file = file();
  	if (file == null)
  	    return;
  	if (debug >= 1)
  	    log(sm.getString("standardManager.unloading", pathname));
  	FileOutputStream fos = null;
  	ObjectOutputStream oos = null;
  	try {
  	    fos = new FileOutputStream(file.getAbsolutePath());
  	    oos = new ObjectOutputStream(new BufferedOutputStream(fos));
  	} catch (IOException e) {
  	    if (oos != null) {
  		try {
  		    oos.close();
  		} catch (IOException f) {
  		    ;
  		}
  		oos = null;
  	    }
  	    throw new LifecycleException("unload/open: IOException", e);
  	}
  
  	// Write the number of active sessions, followed by the details
  	synchronized (sessions) {
  	    try {
  		oos.writeObject(new Integer(sessions.size()));
  		Iterator elements = sessions.values().iterator();
  		while (elements.hasNext()) {
  		    Session session = (Session) elements.next();
  		    oos.writeObject(session);
  		}
  	    } catch (IOException e) {
  		if (oos != null) {
  		    try {
  			oos.close();
  		    } catch (IOException f) {
  			;
  		    }
  		    oos = null;
  		}
  		throw new LifecycleException("unload/write: IOException", e);
  	    }
  	}
  
  	// Flush and close the output stream
  	try {
  	    oos.flush();
  	    oos.close();
  	    oos = null;
  	} catch (IOException e) {
  	    if (oos != null) {
  		try {
  		    oos.close();
  		} catch (IOException f) {
  		    ;
  		}
  		oos = null;
  	    }
  	    throw new LifecycleException("unload/close: IOException", e);
  	}
  
      }
  
  
      /**
       * 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.
       */
      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();
  	}
  
      }
  
  
      // -------------------------------------------------------- Private Classes
  
  
      /**
       * Custom subclass of <code>ObjectInputStream</code> that loads from the
       * class loader for this web application.  This allows classes defined only
       * with the web application to be found correctly.
       */
      private static final class CustomObjectInputStream
  	extends ObjectInputStream {
  
  
  	/**
  	 * The class loader we will use to resolve classes.
  	 */
  	private ClassLoader classLoader = null;
  
  
  	/**
  	 * Construct a new instance of CustomObjectInputStream
  	 *
  	 * @param stream The input stream we will read from
  	 * @param classLoader The class loader used to instantiate objects
  	 *
  	 * @exception IOException if an input/output error occurs
  	 */
  	public CustomObjectInputStream(InputStream stream,
  				       ClassLoader classLoader)
  	    throws IOException {
  
  	    super(stream);
  	    this.classLoader = classLoader;
  
  	}
  
  
  	/**
  	 * Load the local class equivalent of the specified stream class
  	 * description, by using the class loader assigned to this Context.
  	 *
  	 * @param classDesc Class description from the input stream
  	 *
  	 * @exception ClassNotFoundException if this class cannot be found
  	 * @exception IOException if an input/output error occurs
  	 */
  	protected Class resolveClass(ObjectStreamClass classDesc)
  	    throws ClassNotFoundException, IOException {
  
  	    return (classLoader.loadClass(classDesc.getName()));
  
  	}
  
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/StandardSession.java
  
  Index: StandardSession.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/StandardSession.java,v 1.1 2000/08/11 23:39:15 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 23:39:15 $
   *
   * ====================================================================
   *
   * 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.io.IOException;
  import java.io.NotSerializableException;
  import java.io.ObjectInputStream;
  import java.io.ObjectOutputStream;
  import java.io.Serializable;
  import java.security.Principal;
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.HashMap;
  import java.util.Iterator;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpSession;
  import javax.servlet.http.HttpSessionBindingEvent;
  import javax.servlet.http.HttpSessionBindingListener;
  import javax.servlet.http.HttpSessionContext;
  import org.apache.catalina.Manager;
  import org.apache.catalina.Session;
  import org.apache.catalina.util.Enumerator;
  import org.apache.catalina.util.StringManager;
  
  
  /**
   * Standard implementation of the <b>Session</b> interface.  This object is
   * serializable, so that it can be stored in persistent storage or transferred
   * to a different JVM for distributable session support.
   * <p>
   * <b>IMPLEMENTATION NOTE</b>:  An instance of this class represents both the
   * internal (Session) and application level (HttpSession) view of the session.
   * However, because the class itself is not declared public, Java logic outside
   * of the <code>org.apache.catalina.session</code> package cannot cast an
   * HttpSession view of this instance back to a Session view.
   * <p>
   * <b>IMPLEMENTATION NOTE</b>:  If you add fields to this class, you must
   * make sure that you carry them over in the read/writeObject methods so
   * that this class is properly serialized.
   *
   * @author Craig R. McClanahan
   * @author Sean Legassick
   * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
   * @version $Revision: 1.1 $ $Date: 2000/08/11 23:39:15 $
   */
  
  final class StandardSession
      implements HttpSession, Session, Serializable {
  
  
      // ----------------------------------------------------------- Constructors
  
  
      /**
       * Construct a new Session associated with the specified Manager.
       *
       * @param manager The manager with which this Session is associated
       */
      public StandardSession(Manager manager) {
  
  	super();
  	this.manager = manager;
  
      }
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * The dummy attribute value serialized when a NotSerializableException is
       * encountered in <code>writeObject()</code>.
       */
      private static final String NOT_SERIALIZED =
  	"___NOT_SERIALIZABLE_EXCEPTION___";
  
  
      /**
       * The collection of user data attributes associated with this Session.
       */
      private HashMap attributes = new HashMap();
  
  
      /**
       * The time this session was created, in milliseconds since midnight,
       * January 1, 1970 GMT.
       */
      private long creationTime = 0L;
  
  
      /**
       * We are currently processing a session expiration, so bypass
       * certain IllegalStateException tests.  NOTE:  This value is not
       * included in the serialized version of this object.
       */
      private transient boolean expiring = false;
  
  
      /**
       * The session identifier of this Session.
       */
      private String id = null;
  
  
      /**
       * Descriptive information describing this Session implementation.
       */
      private static final String info = "StandardSession/1.0";
  
  
      /**
       * The last accessed time for this Session.
       */
      private long lastAccessedTime = creationTime;
  
  
      /**
       * The Manager with which this Session is associated.
       */
      private Manager manager = null;
  
  
      /**
       * The maximum time interval, in seconds, between client requests before
       * the servlet container may invalidate this session.  A negative time
       * indicates that the session should never time out.
       */
      private int maxInactiveInterval = -1;
  
  
      /**
       * Flag indicating whether this session is new or not.
       */
      private boolean isNew = false;
  
  
      /**
       * Flag indicating whether this session is valid or not.
       */
      private boolean isValid = false;
  
  
      /**
       * The authenticated Principal associated with this session, if any.
       * <b>IMPLEMENTATION NOTE:</b>  This object is <i>not</i> saved and
       * restored across session serializations!
       */
      private transient Principal principal = null;
  
  
      /**
       * The string manager for this package.
       */
      private static StringManager sm =
          StringManager.getManager(Constants.Package);
  
  
      /**
       * The HTTP session context associated with this session.
       */
      private static HttpSessionContext sessionContext = null;
  
  
      /**
       * The current accessed time for this session.
       */
      private long thisAccessedTime = creationTime;
  
  
      // ----------------------------------------------------- Session Properties
  
  
      /**
       * Set the creation time for this session.  This method is called by the
       * Manager when an existing Session instance is reused.
       *
       * @param time The new creation time
       */
      public void setCreationTime(long time) {
  
  	this.creationTime = time;
  	this.lastAccessedTime = time;
  	this.thisAccessedTime = time;
  
      }
  
  
      /**
       * Return the session identifier for this session.
       */
      public String getId() {
  
  	return (this.id);
  
      }
  
  
      /**
       * Set the session identifier for this session.
       *
       * @param id The new session identifier
       */
      public void setId(String id) {
  
  	if ((this.id != null) && (manager != null) &&
  	  (manager instanceof ManagerBase))
  	    ((ManagerBase) manager).remove(this);
  
  	this.id = id;
  
  	if ((manager != null) && (manager instanceof ManagerBase))
  	    ((ManagerBase) manager).add(this);
  
      }
  
  
      /**
       * Return descriptive information about this Session 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 last time the client sent a request associated with this
       * session, as the number of milliseconds since midnight, January 1, 1970
       * GMT.  Actions that your application takes, such as getting or setting
       * a value associated with the session, do not affect the access time.
       */
      public long getLastAccessedTime() {
  
  	return (this.lastAccessedTime);
  
      }
  
  
      /**
       * Return the Manager within which this Session is valid.
       */
      public Manager getManager() {
  
  	return (this.manager);
  
      }
  
  
      /**
       * Set the Manager within which this Session is valid.
       *
       * @param manager The new Manager
       */
      public void setManager(Manager manager) {
  
  	this.manager = manager;
  
      }
  
  
      /**
       * Return the maximum time interval, in seconds, between client requests
       * before the servlet container will invalidate the session.  A negative
       * time indicates that the session should never time out.
       *
       * @exception IllegalStateException if this method is called on
       *  an invalidated session
       */
      public int getMaxInactiveInterval() {
  
  	if (!isValid)
  	    throw new IllegalStateException
  		(sm.getString("standardSession.getMaxInactiveInterval.ise"));
  
  	return (this.maxInactiveInterval);
  
      }
  
  
      /**
       * Set the maximum time interval, in seconds, between client requests
       * before the servlet container will invalidate the session.  A negative
       * time indicates that the session should never time out.
       *
       * @param interval The new maximum interval
       */
      public void setMaxInactiveInterval(int interval) {
  
  	this.maxInactiveInterval = interval;
  
      }
  
  
  
      /**
       * Return the authenticated Principal that is associated with this Session.
       * This provides an <code>Authenticator</code> with a means to cache a
       * previously authenticated Principal, and avoid potentially expensive
       * <code>Realm.authenticate()</code> calls on every request.  If there
       * is no current associated Principal, return <code>null</code>.
       */
      public Principal getPrincipal() {
  
  	return (this.principal);
  
      }
  
  
      /**
       * Set the authenticated Principal that is associated with this Session.
       * This provides an <code>Authenticator</code> with a means to cache a
       * previously authenticated Principal, and avoid potentially expensive
       * <code>Realm.authenticate()</code> calls on every request.
       *
       * @param principal The new Principal, or <code>null</code> if none
       */
      public void setPrincipal(Principal principal) {
  
  	this.principal = principal;
  
      }
  
  
      /**
       * Return the <code>HttpSession</code> for which this object
       * is the facade.
       */
      public HttpSession getSession() {
  
  	return ((HttpSession) this);
  
      }
  
  
      // ------------------------------------------------- Session Public Methods
  
  
      /**
       * Update the accessed time information for this session.  This method
       * should be called by the context when a request comes in for a particular
       * session, even if the application does not reference it.
       */
      public void access() {
  
  	this.isNew = false;
  	this.lastAccessedTime = this.thisAccessedTime;
  	this.thisAccessedTime = System.currentTimeMillis();
  
      }
  
  
      /**
       * Perform the internal processing required to invalidate this session,
       * without triggering an exception if the session has already expired.
       */
      public void expire() {
  
  	if (expiring)
  	    return;		// Already in progress
  	expiring = true;
  
  	// Remove this session from our manager's active sessions
  	if ((manager != null) && (manager instanceof ManagerBase))
  	    ((ManagerBase) manager).remove(this);
  
  	// Unbind any objects associated with this session
  	String keys[] = keys();
  	for (int i = 0; i < keys.length; i++)
  	    removeAttribute(keys[i]);
  
  	// Mark this session as invalid
  	setValid(false);
  
  	expiring = false;
  
      }
  
  
      /**
       * Return the <code>isValid</code> flag for this session.
       */
      public boolean isValid() {
  
  	return (this.isValid);
  
      }
  
  
      /**
       * Release all object references, and initialize instance variables, in
       * preparation for reuse of this object.
       */
      public void recycle() {
  
  	// Reset the instance variables associated with this Session
  	attributes.clear();
  	creationTime = 0L;
  	expiring = false;
  	id = null;
  	lastAccessedTime = 0L;
  	manager = null;
  	maxInactiveInterval = -1;
  	principal = null;
  	isNew = false;
  	isValid = false;
  
  	// Tell our Manager that this Session has been recycled
  	if ((manager != null) && (manager instanceof ManagerBase))
  	    ((ManagerBase) manager).recycle(this);
  
      }
  
  
      /**
       * Return a string representation of this object.
       */
      public String toString() {
  
  	StringBuffer sb = new StringBuffer();
  	sb.append("StandardSession[");
  	sb.append(id);
  	sb.append("]");
  	return (sb.toString());
  
      }
  
  
      // ------------------------------------------------ Session Package Methods
  
  
      /**
       * Set the <code>isNew</code> flag for this session.
       *
       * @param isNew The new value for the <code>isNew</code> flag
       */
      void setNew(boolean isNew) {
  
  	this.isNew = isNew;
  
      }
  
  
      /**
       * Set the <code>isValid</code> flag for this session.
       *
       * @param isValid The new value for the <code>isValid</code> flag
       */
      void setValid(boolean isValid) {
  
  	this.isValid = isValid;
      }
  
  
      // ------------------------------------------------- HttpSession Properties
  
  
      /**
       * Return the time when this session was created, in milliseconds since
       * midnight, January 1, 1970 GMT.
       *
       * @exception IllegalStateException if this method is called on an
       *  invalidated session
       */
      public long getCreationTime() {
  
  	if (!isValid)
  	    throw new IllegalStateException
  		(sm.getString("standardSession.getCreationTime.ise"));
  
  	return (this.creationTime);
  
      }
  
  
      /**
       * Return the session context with which this session is associated.
       *
       * @deprecated As of Version 2.1, this method is deprecated and has no
       *  replacement.  It will be removed in a future version of the
       *  Java Servlet API.
       */
      public HttpSessionContext getSessionContext() {
  
  	if (sessionContext == null)
  	    sessionContext = new StandardSessionContext();
  	return (sessionContext);
  
      }
  
  
      // ----------------------------------------------HttpSession Public Methods
  
  
      /**
       * Return the object bound with the specified name in this session, or
       * <code>null</code> if no object is bound with that name.
       *
       * @param name Name of the attribute to be returned
       *
       * @exception IllegalStateException if this method is called on an
       *  invalidated session
       */
      public Object getAttribute(String name) {
  
  	if (!isValid)
  	    throw new IllegalStateException
  		(sm.getString("standardSession.getAttribute.ise"));
  
  	synchronized (attributes) {
  	    return (attributes.get(name));
  	}
  
      }
  
  
      /**
       * Return an <code>Enumeration</code> of <code>String</code> objects
       * containing the names of the objects bound to this session.
       *
       * @exception IllegalStateException if this method is called on an
       *  invalidated session
       */
      public Enumeration getAttributeNames() {
  
  	if (!isValid)
  	    throw new IllegalStateException
  		(sm.getString("standardSession.getAttributeNames.ise"));
  
  	synchronized (attributes) {
  	    return (new Enumerator(attributes.keySet()));
  	}
  
      }
  
  
      /**
       * Return the object bound with the specified name in this session, or
       * <code>null</code> if no object is bound with that name.
       *
       * @param name Name of the value to be returned
       *
       * @exception IllegalStateException if this method is called on an
       *  invalidated session
       *
       * @deprecated As of Version 2.2, this method is replaced by
       *  <code>getAttribute()</code>
       */
      public Object getValue(String name) {
  
  	return (getAttribute(name));
  
      }
  
  
      /**
       * Return the set of names of objects bound to this session.  If there
       * are no such objects, a zero-length array is returned.
       *
       * @exception IllegalStateException if this method is called on an
       *  invalidated session
       *
       * @deprecated As of Version 2.2, this method is replaced by
       *  <code>getAttributeNames()</code>
       */
      public String[] getValueNames() {
  
  	if (!isValid)
  	    throw new IllegalStateException
  		(sm.getString("standardSession.getValueNames.ise"));
  
  	return (keys());
  
      }
  
  
      /**
       * Invalidates this session and unbinds any objects bound to it.
       *
       * @exception IllegalStateException if this method is called on
       *  an invalidated session
       */
      public void invalidate() {
  
  	if (!isValid)
  	    throw new IllegalStateException
  		(sm.getString("standardSession.invalidate.ise"));
  
  	// Cause this session to expire
  	expire();
  
      }
  
  
      /**
       * Return <code>true</code> if the client does not yet know about the
       * session, or if the client chooses not to join the session.  For
       * example, if the server used only cookie-based sessions, and the client
       * has disabled the use of cookies, then a session would be new on each
       * request.
       *
       * @exception IllegalStateException if this method is called on an
       *  invalidated session
       */
      public boolean isNew() {
  
  	if (!isValid)
  	    throw new IllegalStateException
  		(sm.getString("standardSession.isNew.ise"));
  
  	return (this.isNew);
  
      }
  
  
      /**
       * Bind an object to this session, using the specified name.  If an object
       * of the same name is already bound to this session, the object is
       * replaced.
       * <p>
       * After this method executes, and if the object implements
       * <code>HttpSessionBindingListener</code>, the container calls
       * <code>valueBound()</code> on the object.
       *
       * @param name Name to which the object is bound, cannot be null
       * @param value Object to be bound, cannot be null
       *
       * @exception IllegalStateException if this method is called on an
       *  invalidated session
       *
       * @deprecated As of Version 2.2, this method is replaced by
       *  <code>setAttribute()</code>
       */
      public void putValue(String name, Object value) {
  
  	setAttribute(name, value);
  
      }
  
  
      /**
       * Remove the object bound with the specified name from this session.  If
       * the session does not have an object bound with this name, this method
       * does nothing.
       * <p>
       * After this method executes, and if the object implements
       * <code>HttpSessionBindingListener</code>, the container calls
       * <code>valueUnbound()</code> on the object.
       *
       * @param name Name of the object to remove from this session.
       *
       * @exception IllegalStateException if this method is called on an
       *  invalidated session
       */
      public void removeAttribute(String name) {
  
  	// Validate our current state
  	if (!expiring && !isValid)
  	    throw new IllegalStateException
  		(sm.getString("standardSession.removeAttribute.ise"));
  
  	// Remove this attribute from our collection
  	Object object = null;
  	synchronized (attributes) {
  	    object = attributes.get(name);
  	    if (object == null)
  		return;
  	    attributes.remove(name);
  	}
  
  	// Call the valueUnbound() method if necessary
  	if (object instanceof HttpSessionBindingListener)
  	    ((HttpSessionBindingListener) object).valueUnbound
  		(new HttpSessionBindingEvent((HttpSession) this, name));
  
      }
  
  
      /**
       * Remove the object bound with the specified name from this session.  If
       * the session does not have an object bound with this name, this method
       * does nothing.
       * <p>
       * After this method executes, and if the object implements
       * <code>HttpSessionBindingListener</code>, the container calls
       * <code>valueUnbound()</code> on the object.
       *
       * @param name Name of the object to remove from this session.
       *
       * @exception IllegalStateException if this method is called on an
       *  invalidated session
       *
       * @deprecated As of Version 2.2, this method is replaced by
       *  <code>removeAttribute()</code>
       */
      public void removeValue(String name) {
  
  	removeAttribute(name);
  
      }
  
  
      /**
       * Bind an object to this session, using the specified name.  If an object
       * of the same name is already bound to this session, the object is
       * replaced.
       * <p>
       * After this method executes, and if the object implements
       * <code>HttpSessionBindingListener</code>, the container calls
       * <code>valueBound()</code> on the object.
       *
       * @param name Name to which the object is bound, cannot be null
       * @param value Object to be bound, cannot be null
       *
       * @exception IllegalArgumentException if an attempt is made to add a
       *  non-serializable object in an environment marked distributable.
       * @exception IllegalStateException if this method is called on an
       *  invalidated session
       */
      public void setAttribute(String name, Object value) {
  
  	// Validate our current state
  	if (!isValid)
  	    throw new IllegalStateException
  		(sm.getString("standardSession.setAttribute.ise"));
  	if ((manager != null) && manager.getDistributable() &&
  	  !(value instanceof Serializable))
  	    throw new IllegalArgumentException
  		(sm.getString("standardSession.setAttribute.iae"));
  
  	// Call the valueBound() method if necessary
  	if (value instanceof HttpSessionBindingListener)
  	    ((HttpSessionBindingListener) value).valueBound
  		(new HttpSessionBindingEvent((HttpSession) this, name));
  
  	// Replace or add this attribute
  	synchronized (attributes) {
  	    removeAttribute(name);
  	    attributes.put(name, value);
  	}
  
      }
  
  
      // -------------------------------------------- HttpSession Private Methods
  
  
      /**
       * Read a serialized version of this session object from the specified
       * object input stream.
       * <p>
       * <b>IMPLEMENTATION NOTE</b>:  The reference to the owning Manager
       * is not restored by this method, and must be set explicitly.
       *
       * @param stream The input stream to read from
       *
       * @exception ClassNotFoundException if an unknown class is specified
       * @exception IOException if an input/output error occurs
       */
      private void readObject(ObjectInputStream stream)
  	throws ClassNotFoundException, IOException {
  
  	// Deserialize the scalar instance variables (except Manager)
  	creationTime = ((Long) stream.readObject()).longValue();
  	id = (String) stream.readObject();
  	lastAccessedTime = ((Long) stream.readObject()).longValue();
  	maxInactiveInterval = ((Integer) stream.readObject()).intValue();
  	isNew = ((Boolean) stream.readObject()).booleanValue();
  	isValid = ((Boolean) stream.readObject()).booleanValue();
  	thisAccessedTime = ((Long) stream.readObject()).longValue();
  	principal = null;	// Transient only
  
  	// Deserialize the attribute count and attribute values
  	if (attributes == null)
  	    attributes = new HashMap();
  	int n = ((Integer) stream.readObject()).intValue();
  	for (int i = 0; i < n; i++) {
  	    String name = (String) stream.readObject();
  	    Object value = (Object) stream.readObject();
  	    if ((value instanceof String) && (value.equals(NOT_SERIALIZED)))
  		continue;
  	    attributes.put(name, value);	// No multithread access
  	}
  
      }
  
  
      /**
       * Write a serialized version of this session object to the specified
       * object output stream.
       * <p>
       * <b>IMPLEMENTATION NOTE</b>:  The owning Manager will not be stored
       * in the serialized representation of this Session.  After calling
       * <code>readObject()</code>, you must set the associated Manager
       * explicitly.
       * <p>
       * <b>IMPLEMENTATION NOTE</b>:  Any attribute that is not Serializable
       * will be unbound from the session, with appropriate actions if it
       * implements HttpSessionBindingListener.  If you do not want any such
       * attributes, be sure the <code>distributable</code> property of the
       * associated Manager is set to <code>true</code>.
       *
       * @param stream The output stream to write to
       *
       * @exception IOException if an input/output error occurs
       */
      private void writeObject(ObjectOutputStream stream) throws IOException {
  
  	// Write the scalar instance variables (except Manager)
  	stream.writeObject(new Long(creationTime));
  	stream.writeObject(id);
  	stream.writeObject(new Long(lastAccessedTime));
  	stream.writeObject(new Integer(maxInactiveInterval));
  	stream.writeObject(new Boolean(isNew));
  	stream.writeObject(new Boolean(isValid));
  	stream.writeObject(new Long(thisAccessedTime));
  
  	// Accumulate the names of serializable and non-serializable attributes
  	String keys[] = keys();
  	ArrayList saveNames = new ArrayList();
  	ArrayList saveValues = new ArrayList();
  	ArrayList unbinds = new ArrayList();
  	for (int i = 0; i < keys.length; i++) {
  	    Object value = null;
  	    synchronized (attributes) {
  		value = attributes.get(keys[i]);
  	    }
  	    if (value == null)
  		continue;
  	    else if (value instanceof Serializable) {
  		saveNames.add(keys[i]);
  		saveValues.add(value);
  	    } else
  		unbinds.add(keys[i]);
  	}
  
  	// Serialize the attribute count and the Serializable attributes
  	int n = saveNames.size();
  	stream.writeObject(new Integer(n));
  	for (int i = 0; i < n; i++) {
  	    stream.writeObject((String) saveNames.get(i));
  	    try {
  		stream.writeObject(saveValues.get(i));
  	    } catch (NotSerializableException e) {
  		log(sm.getString("standardSession.notSerializable",
  				 saveNames.get(i), id));
  		stream.writeObject(NOT_SERIALIZED);
  		unbinds.add(saveNames.get(i));
  	    }
  	}
  
  	// Unbind the non-Serializable attributes
  	Iterator names = unbinds.iterator();
  	while (names.hasNext()) {
  	    removeAttribute((String) names.next());
  	}
  
      }
  
  
      // -------------------------------------------------------- Private Methods
  
  
      /**
       * Return the names of all currently defined session attributes
       * as an array of Strings.  If there are no defined attributes, a
       * zero-length array is returned.
       */
      private String[] keys() {
  
  	String results[] = new String[0];
  	synchronized (attributes) {
  	    return ((String[]) attributes.keySet().toArray(results));
  	}
  
      }
  
  
      /**
       * Log a message on the Logger associated with our Manager (if any).
       *
       * @param message Message to be logged
       */
      private void log(String message) {
  
  	if ((manager != null) && (manager instanceof StandardManager)) {
  	    ((StandardManager) manager).log(message);
  	} else {
  	    System.out.println("StandardSession: " + message);
  	}
  
      }
  
  
      /**
       * Log a message on the Logger associated with our Manager (if any).
       *
       * @param message Message to be logged
       * @param throwable Associated exception
       */
      private void log(String message, Throwable throwable) {
  
  	if ((manager != null) && (manager instanceof StandardManager)) {
  	    ((StandardManager) manager).log(message, throwable);
  	} else {
  	    System.out.println("StandardSession: " + message);
  	    throwable.printStackTrace(System.out);
  	}
  
      }
  
  
  }
  
  
  // -------------------------------------------------------------- Private Class
  
  
  /**
   * This class is a dummy implementation of the <code>HttpSessionContext</code>
   * interface, to conform to the requirement that such an object be returned
   * when <code>HttpSession.getSessionContext()</code> is called.
   *
   * @author Craig R. McClanahan
   *
   * @deprecated As of Java Servlet API 2.1 with no replacement.  The
   *  interface will be removed in a future version of this API.
   */
  
  final class StandardSessionContext implements HttpSessionContext {
  
  
      private HashMap dummy = new HashMap();
  
      /**
       * Return the session identifiers of all sessions defined
       * within this context.
       *
       * @deprecated As of Java Servlet API 2.1 with no replacement.
       *  This method must return an empty <code>Enumeration</code>
       *  and will be removed in a future version of the API.
       */
      public Enumeration getIds() {
  
  	return (new Enumerator(dummy));
  
      }
  
  
      /**
       * Return the <code>HttpSession</code> associated with the
       * specified session identifier.
       *
       * @param id Session identifier for which to look up a session
       *
       * @deprecated As of Java Servlet API 2.1 with no replacement.
       *  This method must return null and will be removed in a
       *  future version of the API.
       */
      public HttpSession getSession(String id) {
  
  	return (null);
  
      }
  
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/package.html
  
  Index: package.html
  ===================================================================
  <body>
  
  <p>This package contains the standard <code>Manager</code> and
  <code>Session</code> implementations that represent the collection of
  active sessions and the individual sessions themselves, respectively,
  that are associated with a <code>Context</code>.  Additional implementations
  of the <code>Manager</code> interface can be based upon the supplied
  convenience base class (<code>ManagerBase</code>), if desired.  Different
  implementations of <code>Session</code> are possible, but a need for
  functionality beyond what is provided by the standard implementation
  (<code>StandardSession</code>) is not expected.</p>
  
  <p>The convenience <code>ManagerBase</code> base class is configured by
  setting the following properties:</p>
  <ul>
  <li><b>algorithm</b> - Message digest algorithm to be used when
      generating session identifiers.  This must be the name of an
      algorithm supported by the <code>java.security.MessageDigest</code>
      class on your platform.  [DEFAULT_ALGORITHM]</li>
  <li><b>debug</b> - Debugging detail level for this component. [0]</li>
  <li><b>distributable</b> - Has the web application we are associated with
      been marked as "distributable"?  If it has, attempts to add or replace
      a session attribute object that does not implement the
      <code>java.io.Serializable</code> interface will be rejected.
      [false]</li>
  <li><b>entropy</b> - A string initialization parameter that is used to
      increase the entropy of the seeding of the random number generator
      used in creation of session identifiers.  [NONE]</li>
  <li><b>maxInactiveInterval</b> - The default maximum inactive interval,
      in minutes, for sessions created by this Manager.  The standard
      implementation automatically updates this value based on the configuration
      settings in the web application deployment descriptor.  [60]</li>
  <li><b>randomClass</b> - The Java class name of the random number generator
      to be used when creating session identifiers for this Manager.
      [java.security.SecureRandom]</li>
  </ul>
  
  <p>The standard implementation of the <code>Manager</code> interface
  (<code>StandardManager</code>) supports the following additional configuration
  properties:</p>
  <ul>
  <li><b>checkInterval</b> - The interval, in seconds, between checks for
      sessions that have expired and should be invalidated.  [60]</li>
  <li><b>maxActiveSessions</b> - The maximum number of active sessions that
      will be allowed, or -1 for no limit.  [-1]</li>
  <li><b>pathname</b> - Pathname to the file that is used to store session
      data persistently across container restarts.  If this pathname is relative,
      it is resolved against the temporary working directory provided by our
      associated Context, if any.  ["sessions.ser"]</li>
  </ul>
  
  </body>