You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@apache.org on 2001/03/21 07:54:02 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/modules/session SessionExpirer.java SessionIdGenerator.java SessionId.java SimpleSessionStore.java

costin      01/03/20 22:54:02

  Modified:    src/share/org/apache/tomcat/modules/session SessionId.java
                        SimpleSessionStore.java
  Added:       src/share/org/apache/tomcat/modules/session
                        SessionExpirer.java SessionIdGenerator.java
  Log:
  I'm looking into session expiration bugs and I decided it's easier
  to split the expire code so we can read it and understand what happens.
  
  I also created a separate module for ID generation, as the code is
  independent of the session storage ( which we want to keep it simple
  in case someone wants to backport file store or other advanced
  session stores ).
  
  In the expirer, the main difference is that we use a single expire thread
  per context manager - instead of one thread per context. This allows
  tomcat to scale a bit better when many contexts are deployed
  ( some dynamic loading/unloading is needed, but that can be done later ).
  ( there is no gain in having 100 expire threads working in paralel -
  even if you have a 100 processor machine, you don't want expirer to
  take all the time. You can still have multiple expirers if you explicitely
  define it per context )
  
  I tested the code, and it seems the sessions are expiring fine. I'm
  still working on the admin pages that will show the session statistics
  and allow a bit of control.
  
  Larry - please don't kill me, the changes are not very big ( even if it
  looks like ), I don't think they add too much instability but makes
  the code clearer.
  
  Revision  Changes    Path
  1.9       +1 -7      jakarta-tomcat/src/share/org/apache/tomcat/modules/session/SessionId.java
  
  Index: SessionId.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/modules/session/SessionId.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- SessionId.java	2001/01/01 00:17:23	1.8
  +++ SessionId.java	2001/03/21 06:54:01	1.9
  @@ -237,13 +237,7 @@
   
   	    // if the session was NEW ( never accessed - change it's state )
   	    if( sess.getState() == ServerSession.STATE_NEW ) {
  -		reqI= request.getContainer().
  -		    getInterceptors(Container.H_sessionState);
  -		for( int i=0; i< reqI.length; i++ ) {
  -		    reqI[i].sessionState( request,
  -					  sess,  ServerSession.STATE_ACCESSED);
  -		}
  -		sess.setState( ServerSession.STATE_ACCESSED);
  +		sess.setState( ServerSession.STATE_ACCESSED, request);
   	    }
   	}
   	return sess;
  
  
  
  1.13      +114 -285  jakarta-tomcat/src/share/org/apache/tomcat/modules/session/SimpleSessionStore.java
  
  Index: SimpleSessionStore.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/modules/session/SimpleSessionStore.java,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- SimpleSessionStore.java	2001/03/09 23:47:40	1.12
  +++ SimpleSessionStore.java	2001/03/21 06:54:01	1.13
  @@ -72,18 +72,10 @@
   
   
   /**
  - * This is the adapter between tomcat and a StandardManager.
  - * A session manager should not depend on tomcat internals - so you can
  - * use it in other engines and back. All you need to do is
  - * create an adapter ( tomcat Interceptor).
  + * A simple session store plugin. It will create, store and maintain
  + * session objects using a simple in-memory pool.
    *
  - * You can even have multiple session managers per context - the first that
  - * recognize the "requestedSessionId" will create it's own HttpSession object.
  - * By using standard tomcat interceptor mechanisms you can plug in one or
  - * many session managers per context or context manager ( or even per
  - * URL - but that's not standard API feature ).
  - * 
  - * It must be inserted after SessionInterceptor, which does common
  + * It must be inserted after SessionId, which does common
    * session stuff ( cookie, rewrite, etc)
    *
    * @author costin@eng.sun.com
  @@ -93,68 +85,35 @@
    */
   public final class SimpleSessionStore  extends BaseInterceptor {
       int manager_note;
  -
  -    int checkInterval = 60;
       int maxActiveSessions = -1;
  -    String randomClassName=null;
  -    // creating a Random is very expensive, make sure we reuse
  -    // instances ( keyed by class name - different contexts can use different
  -    // random sources 
  -    static Hashtable randoms=new Hashtable();
       
       public SimpleSessionStore() {
       }
   
       // -------------------- Configuration properties --------------------
   
  -    /**
  -     * Set the check interval (in seconds) for this Manager.
  -     *
  -     * @param checkInterval The new check interval
  -     */
  -    public void setCheckInterval( int secs ) {
  -	checkInterval=secs;
  -    }
  -
       public void setMaxActiveSessions( int count ) {
   	maxActiveSessions=count;
       }
   
  -    public final void setRandomClass(String randomClass) {
  -	this.randomClassName=randomClass;
  -	if( null == randoms.get( randomClassName) ) {
  -	    randoms.put( randomClassName,
  -			 createRandomClass( randomClassName ));
  -	}
  -    }
  -
  -    
       // -------------------- Tomcat request events --------------------
       public void engineInit( ContextManager cm ) throws TomcatException {
   	// set-up a per/container note for StandardManager
   	manager_note = cm.getNoteId( ContextManager.CONTAINER_NOTE,
   				     "tomcat.standardManager");
       }
  +
       
       public void reload( Request req, Context ctx ) throws TomcatException {
   	ClassLoader newLoader = ctx.getClassLoader();
   	SimpleSessionManager sM = getManager( ctx );    
   
   	// remove all non-serializable objects from session
  -	Enumeration sessionEnum=sM.getSessions().keys();
  +	Enumeration sessionEnum=sM.getSessionIds();
   	while( sessionEnum.hasMoreElements() ) {
   	    ServerSession session = (ServerSession)sessionEnum.nextElement();
   
  -	    // Move it to SUSPEND state
  -	    BaseInterceptor reqI[]= req.getContainer().
  -		getInterceptors(Container.H_sessionState);
  -	    for( int i=0; i< reqI.length; i++ ) {
  -		// during suspend hook the servlet callbacks will be called
  -		// - typically from the ServletInterceptor
  -		reqI[i].sessionState( req,
  -				      session,  ServerSession.STATE_SUSPEND);
  -	    }
  -	    session.setState( ServerSession.STATE_SUSPEND );
  +	    session.setState( ServerSession.STATE_SUSPEND, req );
   	    
   	    ClassLoader oldLoader=(ClassLoader)ctx.getContainer().
   		getNote("oldLoader");
  @@ -176,16 +135,7 @@
   		} 
   	    }
   
  -	    // Move it to RESTORE state
  -	    reqI= req.getContainer().
  -		getInterceptors(Container.H_sessionState);
  -	    for( int i=0; i< reqI.length; i++ ) {
  -		// during suspend hook the servlet callbacks will be called
  -		// - typically from the ServletInterceptor
  -		reqI[i].sessionState( req,
  -				      session,  ServerSession.STATE_RESTORED);
  -	    }
  -	    session.setState( ServerSession.STATE_RESTORED );
  +	    session.setState( ServerSession.STATE_RESTORED, req );
   
   	    /* now put back all attributs */
   	    e=newSession.keys();
  @@ -195,8 +145,6 @@
   		session.setAttribute( key, value );
   	    }
   	}
  -
  -	
       }
   
       /** The session store hook
  @@ -206,6 +154,7 @@
       {
   	Context ctx=request.getContext();
   	if( ctx==null ) return null;
  +	
   	SimpleSessionManager sM = getManager( ctx );    
   	
   	ServerSession sess=sM.findSession( sessionId );
  @@ -213,11 +162,17 @@
   
   	if( ! create ) return null; // not found, don't create
   
  -	// create new session
  -	// Fix from Shai Fultheim: load balancing needs jvmRoute
  -	ServerSession newS=sM.getNewSession(request.getJvmRoute());
  -	if( newS==null ) return null;
  -	newS.setContext( ctx );
  +	if ((maxActiveSessions >= 0) &&
  +	    (sM.getSessionCount() >= maxActiveSessions)) {
  +	    log( "Too many sessions " + maxActiveSessions );
  +	    return null;
  +	}
  +
  +	ServerSession newS=sM.getNewSession(request, ctx);
  +	if( newS==null ) {
  +	    log( "Create session failed " );
  +	    return null;
  +	}
   	
   	return newS;
       }
  @@ -230,32 +185,13 @@
       public void contextInit(Context ctx) throws TomcatException {
   	// Defaults !!
   	SimpleSessionManager sm= getManager( ctx );
  -	
  +
   	if( sm == null ) {
   	    sm=new SimpleSessionManager();
   	    ctx.getContainer().setNote( manager_note, sm );
  -	    if( randomClassName==null )
  -		setRandomClass("java.security.SecureRandom" );
  -	    sm.setRandomSource( (Random)randoms.get( randomClassName ));
  -	    sm.setMaxInactiveInterval( (long)ctx.getSessionTimeOut() *
  -				       60 * 1000 );
  -	}
  -	sm.setMaxActiveSessions( maxActiveSessions );
  -
  -	Expirer expirer=new Expirer();
  -	expirer.setCheckInterval( checkInterval );
  -	expirer.setExpireCallback( new Expirer.ExpireCallback() {
  -		public void expired(TimeStamp o ) {
  -		    ServerSession sses=(ServerSession)o.getParent();
  -		    SimpleSessionManager ssm=(SimpleSessionManager)
  -			sses.getManager();
  -		    ssm.removeSession( sses );
  -		}
  -	    });
  -	expirer.start();
  -	sm.setExpirer(expirer);
  +	}
       }
  -    
  +
       /** Notification of context shutdown.
        *  We should clean up any resources that are used by our
        *  session management code. 
  @@ -264,235 +200,128 @@
   	throws TomcatException
       {
   	if( debug > 0 ) ctx.log("Removing sessions from " + ctx );
  -	SimpleSessionManager sm=getManager(ctx);
  -	sm.getExpirer().stop();
  -	sm.removeAllSessions();
  -    }
   
  -    // -------------------- Internal methods --------------------
  -    private SimpleSessionManager getManager( Context ctx ) {
  -	return (SimpleSessionManager)ctx.getContainer().getNote(manager_note);
  -    }
  -
  -    private Random createRandomClass( String s ) {
  -	Random randomSource=null;
  -	String className = s;
  -	if (className != null) {
  -	    try {
  -		Class randomClass = Class.forName(className);
  -		randomSource = (java.util.Random)randomClass.newInstance();
  -	    } catch (Exception e) {
  -		e.printStackTrace();
  -	    }
  +	SimpleSessionManager sm=getManager(ctx);
  +	Enumeration ids = sm.getSessionIds();
  +	while (ids.hasMoreElements()) {
  +	    String id = (String) ids.nextElement();
  +	    ServerSession session = sm.findSession(id);
  +	    if (!session.getTimeStamp().isValid())
  +		continue;
  +	    session.setState( ServerSession.STATE_EXPIRED );
   	}
  -	if (randomSource == null)
  -	    randomSource = new java.security.SecureRandom();
  -	return randomSource;
  -    }
  -    
  -}
  -
  -/**
  - * The actual "simple" manager
  - * 
  - */
  -class SimpleSessionManager  
  -{
  -    protected Log loghelper = Log.getLog("tc_log", this);
  -    
  -    /** The set of previously recycled Sessions for this Manager.
  -     */
  -    protected SimplePool recycled = new SimplePool();
  -
  -    /**
  -     * The set of currently active Sessions for this Manager, keyed by
  -     * session identifier.
  -     */
  -    protected Hashtable sessions = new Hashtable();
  -
  -    protected Expirer expirer;
  -    /**
  -     * The interval (in seconds) between checks for expired sessions.
  -     */
  -    private int checkInterval = 60;
  -
  -    /**
  -     * The maximum number of active Sessions allowed, or -1 for no limit.
  -     */
  -    protected int maxActiveSessions = -1;
  -
  -    long maxInactiveInterval;
  -    
  -    protected Reaper reaper;
  -    Random randomSource=null;
  -    
  -    public SimpleSessionManager() {
  -    }
  -
  -    public void setExpirer( Expirer ex ) {
  -	expirer = ex;
  -    }
  -
  -    public Expirer getExpirer() {
  -	return expirer;
       }
   
  -    public void setRandomSource( Random r ) {
  -	randomSource=r;
  -    }
  -
  -    // ------------------------------------------------------------- Properties
  -
  -    public int getMaxActiveSessions() {
  -	return maxActiveSessions;
  -    }
  +    public int sessionState( Request req, ServerSession session, int state ) {
  +	TimeStamp ts=session.getTimeStamp();
   
  -    public void setMaxActiveSessions(int max) {
  -	maxActiveSessions = max;
  +	if( state==ServerSession.STATE_EXPIRED ) {
  +	    // session moved to expire state - remove all attributes from
  +	    // storage
  +	    SimpleSessionManager ssm=(SimpleSessionManager)session.getManager();
  +	    ssm.removeSession( session );
  +	}
  +	return state;
       }
  -
  -    // --------------------------------------------------------- Public Methods
   
  -    public void setMaxInactiveInterval( long l ) {
  -	maxInactiveInterval=l;
  +    // -------------------- State Info -------------------- 
  +    public Enumeration getSessionIds(Context ctx) {
  +	SimpleSessionManager sm= getManager( ctx );
  +	return sm.getSessionIds();
       }
       
  -    /**
  -     * Return the default maximum inactive interval (in miliseconds)
  -     * for Sessions created by this Manager. We use miliseconds
  -     * because that's how the time is expressed, avoid /1000
  -     * in the common code
  -     */
  -    public long getMaxInactiveInterval() {
  -	return maxInactiveInterval;
  -    }
  -
  -
  -    public Hashtable getSessions() {
  -	return sessions;
  +    public int getSessionCount(Context ctx) {
  +	SimpleSessionManager sm= getManager( ctx );
  +	return sm.getSessionCount();
       }
       
  -    public void setSessions(Hashtable s) {
  -	sessions=s;
  +    public int getRecycledCount(Context ctx) {
  +	SimpleSessionManager sm= getManager( ctx );
  +	return sm.getRecycledCount();
       }
  +    // -------------------- Internal methods --------------------
   
  -    public ServerSession findSession(String id) {
  -	if (id == null) return null;
  -	return (ServerSession)sessions.get(id);
  +    
  +    private SimpleSessionManager getManager( Context ctx ) {
  +	return (SimpleSessionManager)ctx.getContainer().getNote(manager_note);
       }
   
       /**
  -     * Remove this Session from the active Sessions for this Manager.
  -     *
  -     * @param session Session to be removed
  +     * The actual "simple" manager
  +     * 
        */
  -    public void removeSession(ServerSession session) {
  -	sessions.remove(session.getId().toString());
  -	recycled.put(session);
  -	// announce the state change
  -	BaseInterceptor reqI[]=session.getContext().getContainer().
  -	    getInterceptors(Container.H_sessionState);
  -	for( int i=0; i< reqI.length; i++ ) {
  -	    // during suspend hook the servlet callbacks will be called
  -	    // - typically from the ServletInterceptor
  -	    reqI[i].sessionState( null,
  -				  session,  ServerSession.STATE_EXPIRED);
  -	}
  -	session.setState( ServerSession.STATE_EXPIRED );
  -	
  -	session.removeAllAttributes();
  -	expirer.removeManagedObject( session.getTimeStamp());
  -	session.setValid(false);
  +    public static class SimpleSessionManager  
  +    {
  +	/** The set of previously recycled Sessions for this Manager.
  +	 */
  +	protected SimplePool recycled = new SimplePool();
   	
  -    }
  +	/**
  +	 * The set of currently active Sessions for this Manager, keyed by
  +	 * session identifier.
  +	 */
  +	protected Hashtable sessions = new Hashtable();
   
  -    public ServerSession getNewSession() {
  -	return getNewSession( null ) ;
  -    }
  -
  -    static Jdk11Compat jdk11Compat=Jdk11Compat.getJdkCompat();
  -    
  -    public ServerSession getNewSession(String jsIdent) {
  -	if ((maxActiveSessions >= 0) &&
  -	    (sessions.size() >= maxActiveSessions)) {
  -	    loghelper.log( "Too many sessions " + maxActiveSessions );
  -	    return null;
  +	public SimpleSessionManager() {
   	}
   
  -	// Recycle or create a Session instance
  -	ServerSession session = (ServerSession)recycled.get();
  -	if (session == null) {
  -	    session = new ServerSession();
  -	    session.setManager( this );
  -	}
  -	
  -	// XXX can return MessageBytes !!!
  +	// --------------------------------------------- Public Methods
   
  -        /**
  -         * When using a SecurityManager and a JSP page or servlet triggers
  -         * creation of a new session id it must be performed with the 
  -         * Permissions of this class using doPriviledged because the parent
  -         * JSP or servlet may not have sufficient Permissions.
  -         */
  -	String newId;
  -        if( System.getSecurityManager() != null ) {
  -            class doInit extends Action { // implements PrivilegedAction {
  -		private Random randomSource;
  -                private String jsIdent;
  -                public doInit(Random rs, String ident) {
  -		    randomSource = rs;
  -                    jsIdent = ident;
  -                }           
  -                public Object run() {
  -                    return SessionIdGenerator.getIdentifier(randomSource,
  -							    jsIdent);
  -                }           
  -            }    
  -            doInit di = new doInit(randomSource,jsIdent);
  -	    try {
  -		newId= (String)jdk11Compat.doPrivileged(di);
  -	    } catch( Exception ex ) {
  -		newId=null;
  -	    }
  -	    //AccessController.doPrivileged(di);
  -	} else {
  -	    newId= SessionIdGenerator.getIdentifier(randomSource, jsIdent);
  +	public Enumeration getSessionIds() {
  +	    return sessions.keys();
   	}
   
  -	// What if the newId belongs to an existing session ?
  -	// This shouldn't happen ( maybe we can try again ? )
  -	ServerSession oldS=findSession( newId );
  -	if( oldS!=null) {
  -	    // that's what the original code did
  -	    removeSession( oldS );
  +	public int getSessionCount() {
  +	    return sessions.size();
   	}
   
  -	// Initialize the properties of the new session and return it
  -	session.getId().setString( newId );
  +	public int getRecycledCount() {
  +	    return recycled.getMax();
  +	}
  +	
  +	public ServerSession findSession(String id) {
  +	    if (id == null) return null;
  +	    return (ServerSession)sessions.get(id);
  +	}
   
  -	TimeStamp ts=session.getTimeStamp();
  -	ts.setNew(true);
  -	ts.setValid(true);
  +	/**
  +	 * Remove this Session from the active Sessions for this Manager.
  +	 *
  +	 * @param session Session to be removed
  +	 */
  +	public void removeSession(ServerSession session) {
  +	    sessions.remove(session.getId().toString());
  +	    recycled.put(session);
  +	    session.setValid(false);
  +	    session.recycle();
  +	    //	    session.removeAllAttributes();
  +	}
   
  -	ts.setCreationTime(System.currentTimeMillis());
  -	ts.setMaxInactiveInterval(getMaxInactiveInterval());
  -	session.getTimeStamp().setParent( session );
  +	public ServerSession getNewSession(Request req, Context ctx) {
  +	    // Recycle or create a Session instance
  +	    ServerSession session = (ServerSession)recycled.get();
  +	    if (session == null) {
  +		session = new ServerSession();
  +		session.setManager( this );
  +	    }
  +	    session.setContext( ctx );
   
  -	//	System.out.println("New session: " + newId );
  -	sessions.put( newId, session );
  -	expirer.addManagedObject( session.getTimeStamp());
  -	return (session);
  -    }
  +	    session.setState( ServerSession.STATE_NEW, req );
   
  -    public void removeAllSessions() {
  -	Enumeration ids = sessions.keys();
  -	while (ids.hasMoreElements()) {
  -	    String id = (String) ids.nextElement();
  -	    ServerSession session = (ServerSession) sessions.get(id);
  -	    if (!session.getTimeStamp().isValid())
  -		continue;
  -	    removeSession( session );
  +	    // The id will be set by one of the modules
  +	    String newId=session.getId().toString();
  +	    
  +	    // What if the newId belongs to an existing session ?
  +	    // This shouldn't happen ( maybe we can try again ? )
  +	    ServerSession oldS=findSession( newId );
  +	    if( oldS!=null) {
  +		// that's what the original code did
  +		oldS.setState( ServerSession.STATE_EXPIRED );
  +	    }
  +	    
  +	    sessions.put( newId, session );
  +	    return (session);
   	}
  -    }
   
  +    }
   }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/modules/session/SessionExpirer.java
  
  Index: SessionExpirer.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.tomcat.modules.session;
  
  import java.io.*;
  import java.util.Random;
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.util.compat.*;
  import org.apache.tomcat.util.threads.*;
  import org.apache.tomcat.core.*;
  import java.util.*;
  import org.apache.tomcat.util.collections.SimplePool;
  import org.apache.tomcat.util.log.*;
  import org.apache.tomcat.util.buf.*;
  import java.security.*;
  
  
  /**
   * This module handles session expiration ( independent of the
   * session storage and reloading ). 
   *
   * For scalability it uses a single thread per module ( you can
   * use per/context interceptors instead of global to change that )
   * It is derived from SimpleSessionStrore, refactored to keep the
   * code clear.
   *
   * @author costin@eng.sun.com
   * @author hans@gefionsoftware.com
   * @author pfrieden@dChain.com
   * @author Shai Fultheim [shai@brm.com]
   */
  public final class SessionExpirer  extends BaseInterceptor {
      int manager_note;
  
      int checkInterval = 60;
      Expirer expirer=new Expirer();
      
      public SessionExpirer() {
      }
  
      // -------------------- Configuration properties --------------------
  
      /**
       * Set the check interval (in seconds) for this Manager.
       *
       * @param checkInterval The new check interval
       */
      public void setCheckInterval( int secs ) {
  	checkInterval=secs;
      }
  
      // -------------------- Tomcat request events --------------------
      public void engineInit( ContextManager cm ) throws TomcatException {
  	expirer.setCheckInterval( checkInterval );
  	expirer.setExpireCallback( new SessionExpireCallback(this, debug) );
  	expirer.start();
      }
  
      public void engineShutdown( ContextManager cm ) throws TomcatException {
  	expirer.stop();
      }
  
      public int sessionState( Request req, ServerSession session, int state ) {
  	TimeStamp ts=session.getTimeStamp();
  
  	if( state==ServerSession.STATE_NEW ) {
  	    ts.setNew(true);
  	    ts.setValid(true);
  	    
  	    ts.setCreationTime(System.currentTimeMillis());
  	    Context ctx=session.getContext();
  	    ts.setMaxInactiveInterval( ctx.getSessionTimeOut() * 60000 );
  
  	    session.getTimeStamp().setParent( session );
  
  	    expirer.addManagedObject( ts );
  	}  else if( state==ServerSession.STATE_EXPIRED ) {
  	    expirer.removeManagedObject( ts );
  	}
  	return state;
      }
  
      // -------------------- Internal methods --------------------
  
      // Handle expire events
      static class SessionExpireCallback implements Expirer.ExpireCallback {
  	SessionExpirer se;
  	int debug;
  	
  	SessionExpireCallback( SessionExpirer se, int debug ) {
  	    this.se=se;
  	    this.debug=debug;
  	}
  	
  	public void expired(TimeStamp o ) {
  	    ServerSession sses=(ServerSession)o.getParent();
  	    if( debug > 0  ) {
  		se.log( "Session expired " + sses);
  	    }
  	    sses.setState( ServerSession.STATE_EXPIRED );
  	}
      }
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/modules/session/SessionIdGenerator.java
  
  Index: SessionIdGenerator.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.tomcat.modules.session;
  
  import java.io.*;
  import java.util.Random;
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.util.compat.*;
  import org.apache.tomcat.util.threads.*;
  import org.apache.tomcat.core.*;
  import java.util.*;
  import org.apache.tomcat.util.collections.SimplePool;
  import org.apache.tomcat.util.log.*;
  import org.apache.tomcat.util.buf.*;
  import java.security.*;
  
  
  /**
   * Generate session IDs. Will use a random generator and the
   * load balancing route.
   *
   * This class generates a unique 10+ character id. This is good
   * for authenticating users or tracking users around.
   * <p>
   * This code was borrowed from Apache JServ.JServServletManager.java.
   * It is what Apache JServ uses to generate session ids for users.
   * Unfortunately, it was not included in Apache JServ as a class
   * so I had to create one here in order to use it.
   *
   * @author costin@eng.sun.com
   * @author hans@gefionsoftware.com
   * @author pfrieden@dChain.com
   * @author Shai Fultheim [shai@brm.com]
   * @author James Duncan Davidson [duncan@eng.sun.com]
   * @author Jason Hunter [jhunter@acm.org]
   * @author Jon S. Stevens <a href="mailto:jon@latchkey.com">jon@latchkey.com</a>
   */
  public final class SessionIdGenerator  extends BaseInterceptor {
  
      String randomClassName=null;
      Random randomSource=null;
      
      static Jdk11Compat jdk11Compat=Jdk11Compat.getJdkCompat();
      
      public SessionIdGenerator() {
      }
  
      // -------------------- Configuration properties --------------------
  
      public final void setRandomClass(String randomClass) {
  	this.randomClassName=randomClass;
  	randomSource=createRandomClass( randomClassName );
      }
  
      
      // -------------------- Tomcat request events --------------------
  
      public int sessionState( Request req, ServerSession sess, int state ) {
  	if( state==ServerSession.STATE_NEW ) {
  	    String jsIdent=req.getJvmRoute();
  	    String newId=createNewId( jsIdent );
  	    sess.getId().setString( newId );
  	}
  	return state;
      }
  
      //--------------------  Tomcat context events --------------------
  
  
      /** Init session management stuff for this context. 
       */
      public void engineInit(ContextManager cm) throws TomcatException {
  	if( randomSource==null ) {
  	    String randomClass=(String)cm.getProperty("randomClass" );
  	    if( randomClass==null ) {
  		randomClass="java.security.SecureRandom";
  	    }
  	    setRandomClass(randomClass);
  	}
      }
      
  
      // -------------------- Internal methods --------------------
  
      String createNewId(String jsIdent) {
          /**
           * When using a SecurityManager and a JSP page or servlet triggers
           * creation of a new session id it must be performed with the 
           * Permissions of this class using doPriviledged because the parent
           * JSP or servlet may not have sufficient Permissions.
           */
  	String newId;
          if( System.getSecurityManager() == null ) {
  	    newId= SessionIdGenerator.getIdentifier(randomSource, jsIdent);
  	    return newId;
  	}
  	// We're in a sandbox...
  	PriviledgedIdGenerator di = new
  	    PriviledgedIdGenerator(randomSource,jsIdent);
  	try {
  	    newId= (String)jdk11Compat.doPrivileged(di);
  	} catch( Exception ex ) {
  	    newId=null;
  	}
  	return newId;
      }
  
      // Sandbox support
      static class PriviledgedIdGenerator extends Action {
  	private Random randomSource;
  	private String jsIdent;
  	public PriviledgedIdGenerator(Random rs, String ident) {
  	    randomSource = rs;
  	    jsIdent = ident;
  	}           
  	public Object run() {
  	    return SessionIdGenerator.getIdentifier(randomSource,
  						    jsIdent);
  	}           
      }    
  
      private Random createRandomClass( String s ) {
  	Random randomSource=null;
  	String className = s;
  	if (className != null) {
  	    try {
  		Class randomClass = Class.forName(className);
  		long time=System.currentTimeMillis();
  		randomSource = (java.util.Random)randomClass.newInstance();
  		time = System.currentTimeMillis() - time;
  		// Since this can take a lot of time, let the user know
  		log( "Created random " + s + " in " + time );
  	    } catch (Exception e) {
  		e.printStackTrace();
  	    }
  	}
  	if (randomSource == null)
  	    randomSource = new java.security.SecureRandom();
  	return randomSource;
      }
  
      /*
       * Create a suitable string for session identification
       * Use synchronized count and time to ensure uniqueness.
       * Use random string to ensure timestamp cannot be guessed
       * by programmed attack.
       *
       * format of id is <6 chars random><3 chars time><1+ char count>
       */
      static private int session_count = 0;
      static private long lastTimeVal = 0;
  
      // MAX_RADIX is 36
      /*
       * we want to have a random string with a length of
       * 6 characters. Since we encode it BASE 36, we've to
       * modulo it with the following value:
       */
      public final static long maxRandomLen = 2176782336L; // 36 ** 6
  
      /*
       * The session identifier must be unique within the typical lifespan
       * of a Session, the value can roll over after that. 3 characters:
       * (this means a roll over after over an day which is much larger
       *  than a typical lifespan)
       */
      public final static long maxSessionLifespanTics = 46656; // 36 ** 3
  
      /*
       *  millisecons between different tics. So this means that the
       *  3-character time string has a new value every 2 seconds:
       */
      public final static long ticDifference = 2000;
  
      // ** NOTE that this must work together with get_jserv_session_balance()
      // ** in jserv_balance.c
      static synchronized public String getIdentifier (Random randomSource,
  						     String jsIdent)
      {
          StringBuffer sessionId = new StringBuffer();
  	if( randomSource==null)
  	    throw new RuntimeException( "No random source " );
  	
          // random value ..
          long n = randomSource.nextLong();
          if (n < 0) n = -n;
          n %= maxRandomLen;
          // add maxLen to pad the leading characters with '0'; remove
          // first digit with substring.
          n += maxRandomLen;
          sessionId.append (Long.toString(n, Character.MAX_RADIX)
                    .substring(1));
  
          long timeVal = (System.currentTimeMillis() / ticDifference);
          // cut..
          timeVal %= maxSessionLifespanTics;
          // padding, see above
          timeVal += maxSessionLifespanTics;
  
          sessionId.append (Long.toString (timeVal, Character.MAX_RADIX)
                    .substring(1));
  
          /*
           * make the string unique: append the session count since last
           * time flip.
           */
          // count sessions only within tics. So the 'real' session count
          // isn't exposed to the public ..
          if (lastTimeVal != timeVal) {
            lastTimeVal = timeVal;
            session_count = 0;
          }
          sessionId.append (Long.toString (++session_count,
                       Character.MAX_RADIX));
  
          if (jsIdent != null && jsIdent.length() > 0) {
              return sessionId.toString()+"."+jsIdent;
          }
          return sessionId.toString();
      }
  
  }