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...@locus.apache.org on 2000/09/25 08:10:56 UTC

cvs commit: jakarta-tomcat/src/facade22/org/apache/tomcat/facade HttpSessionFacade.java Servlet22Manager.java

costin      00/09/24 23:10:55

  Modified:    src/share/org/apache/tomcat/session ServerSession.java
                        ServerSessionManager.java SessionSerializer.java
                        StandardSessionInterceptor.java
               src/facade22/org/apache/tomcat/facade HttpSessionFacade.java
                        Servlet22Manager.java
  Added:       src/share/org/apache/tomcat/util/threads Expirer.java
                        TimeStamp.java
  Removed:     src/share/org/apache/tomcat/session TimeStamp.java
  Log:
  - moved the code that handle expiration in a tomcat.util class.
  It will be used by ContextManager to unload unused Contexts and servlets,
  and may be used for other tasks ( it's a general-purpose tool )
  
  - same refactoring as in core - replaced indirect calls, methods and properties
   moved to the object where it belongs, split complex objects in simpler ones,
  moved generic code in util.
  
  - the session manager code is now much narrowly focused, and it should be
  easier ( in 3.4 + ) to add more sophisticated managers ( database, etc). It's
  also much cleanly separated from upper layer.
  
  ( some thing may be broken, but most of the refactoring is done.
   )
  
  Revision  Changes    Path
  1.6       +21 -85    jakarta-tomcat/src/share/org/apache/tomcat/session/ServerSession.java
  
  Index: ServerSession.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/session/ServerSession.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- ServerSession.java	2000/09/24 23:03:13	1.5
  +++ ServerSession.java	2000/09/25 06:10:48	1.6
  @@ -66,7 +66,7 @@
   import java.util.Hashtable;
   import java.util.Vector;
   import org.apache.tomcat.util.*;
  -import javax.servlet.http.*;
  +import org.apache.tomcat.util.threads.*;
   
   /**
    * Server representation of a Session.
  @@ -88,10 +88,17 @@
   
       TimeStamp ts=new TimeStamp();
       boolean distributable=false;
  -    
  -    public ServerSession() {
  +    ServerSessionManager ssm;
  +
  +    /** Only ServerSessionManager can create ServerSessions
  +     */
  +    ServerSession(ServerSessionManager ssm) {
  +	this.ssm=ssm;
       }
   
  +    public ServerSessionManager getSessionManager() {
  +	return ssm;
  +    }
       // ----------------------------------------------------- Session Properties
       /** The time stamp associated with this session
        */
  @@ -106,45 +113,6 @@
   	return id;
       }
   
  -    public boolean isDistributable() {
  -	return distributable;
  -    }
  -
  -    public void setDistributable( boolean b ) {
  -	distributable=b;
  -    }
  -    
  -    // --------------------
  -
  -    /**
  -     * Perform the internal processing required to invalidate this session,
  -     * without triggering an exception if the session has already expired.
  -     */
  -    public void expire() {
  -
  -	// Remove this session from our manager's active sessions
  -// 	if (manager != null) 
  -// 	    manager.remove(this);
  -
  -	// Unbind any objects associated with this session
  -	Vector results = new Vector();
  -	Enumeration attrs = getAttributeNames();
  -	while (attrs.hasMoreElements()) {
  -	    String attr = (String) attrs.nextElement();
  -	    results.addElement(attr);
  -	}
  -	Enumeration names = results.elements();
  -	while (names.hasMoreElements()) {
  -	    String name = (String) names.nextElement();
  -	    removeAttribute(name);
  -	}
  -
  -	// Mark this session as invalid
  -	ts.setValid(false);
  -
  -    }
  -
  -
       // -------------------- Attribute access --------------------
   
       public Object getAttribute(String name) {
  @@ -160,55 +128,23 @@
   	return attributes.size();
       }
   
  -    public void removeAttribute(String name) {
  -	synchronized (attributes) {
  -	    Object object = attributes.get(name);
  -	    if (object == null)
  -		return;
  -	    attributes.remove(name);
  -	    //	    System.out.println( "Removing attribute " + name );
  -// 	    if (object instanceof HttpSessionBindingListener) {
  -// 		((HttpSessionBindingListener) object).valueUnbound
  -// 		    (new HttpSessionBindingEvent((HttpSession) this, name));
  -// 	    }
  +    public void removeAllAttributes() {
  +	Enumeration attrs = getAttributeNames();
  +	while (attrs.hasMoreElements()) {
  +	    String attr = (String) attrs.nextElement();
  +	    removeAttribute(attr);
   	}
       }
   
  -    public void setAttribute(String name, Object value) {
  -	synchronized (attributes) {
  -	    removeAttribute(name);
  -	    attributes.put(name, value);
  -// 	    if (value instanceof HttpSessionBindingListener)
  -// 		((HttpSessionBindingListener) value).valueBound
  -// 		    (new HttpSessionBindingEvent((HttpSession) this, name));
  -	}
  +    public void removeAttribute(String name) {
  +	// Hashtable is already synchronized
  +	attributes.remove(name);
       }
  -
   
  -
  -    /** Normal serialization can be used for this object, but before
  -	serializing you _must_ call prepareSerialize() to remove all
  -	non-serializable attributes and notify about their removal.
  -    */
  -    private void prepareSerialize()
  -    {
  -	for (Enumeration e = attributes.keys(); e.hasMoreElements() ; ) {
  -	    String key = (String) e.nextElement();
  -	    Object value = attributes.get(key);
  -	    if (! ( value instanceof Serializable)) {
  -		if (value instanceof HttpSessionBindingListener ) {
  -// 		    try {
  -// 			((HttpSessionBindingListener)value)
  -// 			    .valueUnbound(new
  -// 				HttpSessionBindingEvent(this, key));
  -// 		    } catch (Exception f) {
  -// 			// ignored
  -// 		    }
  -		}
  -		attributes.remove( key );
  -	    }
  -	}
  +    public void setAttribute(String name, Object value) {
  +	attributes.put(name, value);
       }
  +
   
       /**
        * Release all object references, and initialize instance variables, in
  
  
  
  1.7       +57 -158   jakarta-tomcat/src/share/org/apache/tomcat/session/ServerSessionManager.java
  
  Index: ServerSessionManager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/session/ServerSessionManager.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- ServerSessionManager.java	2000/09/24 23:03:13	1.6
  +++ ServerSessionManager.java	2000/09/25 06:10:49	1.7
  @@ -69,28 +69,14 @@
   import org.apache.tomcat.util.*;
   import org.apache.tomcat.util.threads.*;
   import org.apache.tomcat.helper.*;
  -import org.apache.tomcat.core.Request;
  +import org.apache.tomcat.core.*;
   
   /**
    *
    * 
    */
  -public final class ServerSessionManager  implements ThreadPoolRunnable
  +public final class ServerSessionManager  
   {
  -    // ----------------------------------------------------- Instance Variables
  -    /**
  -     * 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;
  -
  -    /**
  -     * The default maximum inactive interval for Sessions created by
  -     * this Manager.
  -     */
  -    protected int maxInactiveInterval = 60;
  -
       /** The set of previously recycled Sessions for this Manager.
        */
       protected SimplePool recycled = new SimplePool();
  @@ -101,6 +87,7 @@
        */
       protected Hashtable sessions = new Hashtable();
   
  +    protected Expirer expirer;
       /**
        * The interval (in seconds) between checks for expired sessions.
        */
  @@ -111,100 +98,53 @@
        */
       protected int maxActiveSessions = -1;
   
  -    /**
  -     * The string manager for this package.
  -     */
  -    private static StringManager sm =
  -        StringManager.getManager("org.apache.tomcat.resources");
  -
  +    long maxInactiveInterval;
  +    
       protected Reaper reaper;
   
       public ServerSessionManager() {
       }
   
  -    // ------------------------------------------------------------- Properties
  -    /**
  -     * Return the distributable flag for the sessions supported by
  -     * this Manager.
  -     */
  -    public boolean getDistributable() {
  -	return (this.distributable);
  +    public void setExpirer( Expirer ex ) {
  +	expirer = ex;
       }
   
  -    /**
  -     * 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) {
  -	this.distributable = distributable;
  +    public Expirer getExpirer() {
  +	return expirer;
       }
  -
  -    /**
  -     * Return the default maximum inactive interval (in seconds)
  -     * for Sessions created by this Manager.
  -     */
  -    public int getMaxInactiveInterval() {
  -	return (this.maxInactiveInterval);
  +    
  +    // ------------------------------------------------------------- Properties
  +    public int getMaxActiveSessions() {
  +	return maxActiveSessions;
       }
   
  -    /**
  -     * 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) {
  -	this.maxInactiveInterval = interval;
  +    public void setMaxActiveSessions(int max) {
  +	maxActiveSessions = max;
       }
   
  -    /**
  -     * Used by context to configure the session manager's inactivity timeout.
  -     *
  -     * The SessionManager may have some default session time out, the
  -     * Context on the other hand has it's timeout set by the deployment
  -     * descriptor (web.xml). This method lets the Context conforgure the
  -     * session manager according to this value.
  -     *
  -     * @param minutes The session inactivity timeout in minutes.
  -     */
  -    public void setSessionTimeOut(int minutes) {
  -        if(-1 != minutes) {
  -            // The manager works with seconds...
  -            setMaxInactiveInterval(minutes * 60);
  -        }
  -    }
  +    // --------------------------------------------------------- Public Methods
   
  -    /**
  -     * Return the check interval (in seconds) for this Manager.
  -     */
  -    public int getCheckInterval() {
  -	return (this.checkInterval);
  +    public void setMaxInactiveInterval( long l ) {
  +	maxInactiveInterval=l;
       }
  -
  +    
       /**
  -     * Set the check interval (in seconds) for this Manager.
  -     *
  -     * @param checkInterval The new check interval
  +     * 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 void setCheckInterval(int checkInterval) {
  -	this.checkInterval = checkInterval;
  +    public long getMaxInactiveInterval() {
  +	return maxInactiveInterval;
       }
   
  -    public int getMaxActiveSessions() {
  -	return maxActiveSessions;
  -    }
   
  -    public void setMaxActiveSessions(int max) {
  -	maxActiveSessions = max;
  +    Hashtable getSessions() {
  +	return sessions;
       }
  -
  -    // --------------------------------------------------------- Public Methods
  -
  -    public Hashtable getSessions() {
  -	return this.sessions;
  +    
  +    void setSessions(Hashtable s) {
  +	sessions=s;
       }
   
       public ServerSession findSession(String id) {
  @@ -212,6 +152,19 @@
   	return (ServerSession)sessions.get(id);
       }
   
  +    /**
  +     * 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.removeAllAttributes();
  +	expirer.removeManagedObject( session.getTimeStamp());
  +	session.getTimeStamp().setValid(false);
  +    }
  +
       public ServerSession getNewSession() {
   	if ((maxActiveSessions >= 0) &&
   	    (sessions.size() >= maxActiveSessions))
  @@ -220,10 +173,10 @@
   	// Recycle or create a Session instance
   	ServerSession session = (ServerSession)recycled.get();
   	if (session == null) {
  -	    session = new ServerSession();
  +	    session = new ServerSession(this);
   	    recycled.put( session );
   	}
  -
  +	
   	// XXX can return MessageBytes !!!
   	String newId=SessionUtil.generateSessionId();
   
  @@ -232,88 +185,34 @@
   	ServerSession oldS=findSession( newId );
   	if( oldS!=null) {
   	    // that's what the original code did
  -	    remove( oldS );
  +	    removeSession( oldS );
   	}
   
   	// Initialize the properties of the new session and return it
   	session.getId().setString( newId );
  -	
  -	session.getTimeStamp().setNew(true);
  -	session.getTimeStamp().setValid(true);
  -	session.getTimeStamp().setCreationTime(System.currentTimeMillis());
  -	session.getTimeStamp().setMaxInactiveInterval(maxInactiveInterval);
  +
  +	TimeStamp ts=session.getTimeStamp();
  +	ts.setNew(true);
  +	ts.setValid(true);
  +
  +	ts.setCreationTime(System.currentTimeMillis());
  +	ts.setMaxInactiveInterval(getMaxInactiveInterval());
  +	session.getTimeStamp().setParent( session );
   
   	//	System.out.println("New session: " + newId );
   	sessions.put( newId, session );
  -
  +	expirer.addManagedObject( session.getTimeStamp());
   	return (session);
       }
   
  -    public void handleReload(Request req, ClassLoader newLoader) {
  -	sessions = SessionSerializer.doSerialization( newLoader, sessions);
  -    }
  -
  -    public void start() {
  -	// Start the background reaper thread
  -	reaper=new Reaper("StandardManager");
  -	reaper.addCallback( this, checkInterval * 1000 );
  -	reaper.startReaper();
  -    }
  -
  -    public void stop() {
  -	reaper.stopReaper();
  -
  -	// expire all active sessions
  +    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;
  -	    session.expire();
  -	}
  -    }
  -
  -    /**
  -     * Remove this Session from the active Sessions for this Manager.
  -     *
  -     * @param session Session to be removed
  -     */
  -    void remove(ServerSession session) {
  -	sessions.remove(session.getId().toString());
  -	recycled.put(session);
  -    }
  -
  -
  -    // -------------------------------------------------------- Private Methods
  -
  -    // ThreadPoolRunnable impl
  -
  -    public Object[] getInitData() {
  -	return null;
  -    }
  -
  -    public void runIt( Object td[] ) {
  -	//	System.out.println("Expiring " + this);
  -	long timeNow = System.currentTimeMillis();
  -	Enumeration ids = sessions.keys();
  -	while (ids.hasMoreElements()) {
  -	    String id = (String) ids.nextElement();
  -	    ServerSession session = (ServerSession) sessions.get(id);
  -	    TimeStamp ts=session.getTimeStamp();
  -	    
  -	    if (!ts.isValid())
  -		continue;
  -	    
  -	    int maxInactiveInterval = ts.getMaxInactiveInterval();
  -	    if (maxInactiveInterval < 0)
  -		continue;
  -	    
  -	    int timeIdle = // Truncate, do not round up
  -		(int) ((timeNow - ts.getLastAccessedTime()) / 1000L);
  -
  -	    if (timeIdle >= maxInactiveInterval)
  -		session.expire();
  +	    removeSession( session );
   	}
       }
   
  
  
  
  1.9       +3 -3      jakarta-tomcat/src/share/org/apache/tomcat/session/SessionSerializer.java
  
  Index: SessionSerializer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/session/SessionSerializer.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- SessionSerializer.java	2000/09/24 18:10:12	1.8
  +++ SessionSerializer.java	2000/09/25 06:10:49	1.9
  @@ -81,8 +81,8 @@
       /**
          This is the method that does the serialization.
       */
  -    public static final Hashtable doSerialization(ClassLoader cl,
  -					     Hashtable sessions)
  +    public static final Object doSerialization(ClassLoader cl,
  +					       Object sessions)
       {
   	// get the hashtable of sessions
   	try {
  @@ -101,7 +101,7 @@
   	    ObjectInputStream oOut= new ACLObjectInputStream(cl, bIn);
   			
   	    // unserialize the sessions
  -	    sessions = (Hashtable) oOut.readObject();
  +	    sessions = oOut.readObject();
   
   	    return sessions;
   	} catch (Exception e) {
  
  
  
  1.11      +69 -51    jakarta-tomcat/src/share/org/apache/tomcat/session/StandardSessionInterceptor.java
  
  Index: StandardSessionInterceptor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/session/StandardSessionInterceptor.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- StandardSessionInterceptor.java	2000/09/24 23:03:13	1.10
  +++ StandardSessionInterceptor.java	2000/09/25 06:10:49	1.11
  @@ -58,13 +58,14 @@
    */ 
   package org.apache.tomcat.session;
   
  -import java.io.IOException;
  +import java.io.*;
   import java.util.Enumeration;
   import java.util.Hashtable;
   import java.util.Vector;
   import javax.servlet.http.Cookie;
   import javax.servlet.http.HttpSession;
   import org.apache.tomcat.util.*;
  +import org.apache.tomcat.util.threads.*;
   import org.apache.tomcat.core.*;
   
   
  @@ -87,13 +88,27 @@
    */
   public final class StandardSessionInterceptor  extends BaseInterceptor {
       int manager_note;
  +
  +    int checkInterval = 60;
  +    int maxActiveSessions = -1;
       
       public StandardSessionInterceptor() {
       }
   
       // -------------------- 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;
  +    }
       
       
       // -------------------- Tomcat request events --------------------
  @@ -145,15 +160,38 @@
       public void reload( Request req, Context ctx ) {
   	ClassLoader newLoader = ctx.getClassLoader();
   	ServerSessionManager sM = getManager( ctx );    
  -	sM.handleReload(req, newLoader);
  -	if (req.getSession(false) != null) {
  -	    // replace the current session in the current request
  -	    Hashtable sessions=sM.getSessions();
  -	    HttpSession newSession = 
  -		(HttpSession)sessions.get(req.getRequestedSessionId());
  -	    req.setSession(newSession);
  -	    req.setSessionId( newSession.getId());
  +
  +	// remove all non-serializable objects from session
  +	Enumeration sessionEnum=sM.getSessions().keys();
  +	while( sessionEnum.hasMoreElements() ) {
  +	    ServerSession session = (ServerSession)sessionEnum.nextElement();
  +	    Enumeration e = session.getAttributeNames();
  +	    while( e.hasMoreElements() )   {
  +		String key = (String) e.nextElement();
  +		Object value = session.getAttribute(key);
  +		// XXX XXX We don't have to change loader for objects loaded
  +		// by the parent loader ?
  +		if (! ( value instanceof Serializable)) {
  +		    session.removeAttribute( key );
  +		    // XXX notification!!
  +		}
  +	    }
   	}
  +
  +	// XXX We should change the loader for each object, and
  +	// avoid accessing object's internals
  +	// XXX PipeStream !?!
  +	Hashtable orig= sM.getSessions();
  +	Object newS = SessionSerializer.doSerialization( newLoader, orig);
  +	sM.setSessions( (Hashtable)newS );
  +	
  +	// Update the request session id
  +	String reqId=req.getRequestedSessionId();
  +	ServerSession sS=sM.findSession( reqId );
  +	if ( sS != null) {
  +	    req.setSession(sS);
  +	    req.setSessionId( reqId );
  +	}
       }
       
       public int newSessionRequest( Request request, Response response) {
  @@ -170,26 +208,6 @@
   	return 0;
       }
   
  -    /** Called after request - we need to release the session object.
  -     *  This is used to prevent removal of session objects during execution,
  -     *	and may be used by interceptors that want to limit or count the
  -     *	sessions.
  -     */
  -    public int postService(  Request rrequest, Response response ) {
  -	// Not used, maybe add it back later if we need to
  -
  -	// 	Context ctx=rrequest.getContext();
  -	// 	if( ctx==null ) return 0; 
  -
  -	// 	ServerSessionManager sm= getManager( ctx );
  -	// 	HttpSession sess=(HttpSession)rrequest.getSession(false);
  -	// 	if( sess == null ) return 0;
  -	//	sm.release( sess );
  -	return 0;
  -    }
  -
  -
  -    
       //--------------------  Tomcat context events --------------------
       
       /** Init session management stuff for this context. 
  @@ -200,18 +218,25 @@
   	
   	if( sm == null ) {
   	    sm=new ServerSessionManager();
  -	    setManager(ctx, sm);
  -	}
  -
  -	// init is called after all context properties are set.
  -	sm.setSessionTimeOut( ctx.getSessionTimeOut() );
  -	sm.setDistributable( ctx.isDistributable() );
  -
  -	try {
  -	    sm.start();
  -	} catch(IllegalStateException ex ) {
  -	    throw new TomcatException( ex );
  -	}
  +	    ctx.getContainer().setNote( manager_note, sm );
  +	    sm.setMaxInactiveInterval( (long)ctx.getSessionTimeOut() *
  +				       60 * 1000 );
  +	    // debug
  +	    sm.setMaxInactiveInterval( 60000 );
  +	}
  +	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();
  +		    ServerSessionManager ssm=sses.getSessionManager();
  +		    ssm.removeSession( sses );
  +		}
  +	    });
  +	expirer.start();
  +	sm.setExpirer(expirer);
       }
       
       /** Notification of context shutdown.
  @@ -223,12 +248,8 @@
       {
   	if( ctx.getDebug() > 0 ) ctx.log("Removing sessions from " + ctx );
   	ServerSessionManager sm=getManager(ctx);
  -	try {
  -	    if( sm != null )
  -		sm.stop();
  -	} catch(IllegalStateException ex ) {
  -	    throw new TomcatException( ex );
  -	}
  +	sm.getExpirer().stop();
  +	sm.removeAllSessions();
       }
   
       // -------------------- Internal methods --------------------
  @@ -236,9 +257,6 @@
   	return (ServerSessionManager)ctx.getContainer().getNote(manager_note);
       }
   
  -    private void setManager( Context ctx, Object sm ) {
  -	ctx.getContainer().setNote( manager_note, sm );
  -    }
   
   
   }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/threads/Expirer.java
  
  Index: Expirer.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.util.threads;
  
  import java.io.IOException;
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.Vector;
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.util.threads.*;
  
  /**
   * Expire unused objects. 
   * 
   */
  public final class Expirer  implements ThreadPoolRunnable
  {
      // We can use Event/Listener, but this is probably simpler
      // and more efficient
      public static interface ExpireCallback {
  	public void expired( TimeStamp o );
      }
      
      private int checkInterval = 60;
      private Reaper reaper;
      ExpireCallback expireCallback;
  
      public Expirer() {
      }
  
      // ------------------------------------------------------------- Properties
      public int getCheckInterval() {
  	return checkInterval;
      }
  
      public void setCheckInterval(int checkInterval) {
  	this.checkInterval = checkInterval;
      }
  
      public void setExpireCallback( ExpireCallback cb ) {
  	expireCallback=cb;
      }
      
      // -------------------- Managed objects --------------------
      static final int INITIAL_SIZE=8;
      TimeStamp managedObjs[]=new TimeStamp[INITIAL_SIZE];
      int managedLen=managedObjs.length;
      int managedCount=0;
      
      public void addManagedObject( TimeStamp ts ) {
  	synchronized( managedObjs ) {
  	    if( managedCount > managedLen ) {
  		// What happens if expire is on the way ? Nothing,
  		// expire will do it's job on the old array ( GC magic )
  		// and the expired object will be marked as such
  		// Same thing would happen ( and did ) with Hashtable
  		TimeStamp newA[]=new TimeStamp[ 2 * managedLen ];
  		System.arraycopy( managedObjs, 0, newA, 0, managedLen);
  		managedObjs = newA;
  	    }
  	    managedObjs[managedCount++]=ts;
  	}
      }
  
      public void removeManagedObject( TimeStamp ts ) {
  	for( int i=0; i< managedCount; i++ ) {
  	    if( ts == managedObjs[i] ) {
  		synchronized( managedObjs ) {
  		    managedObjs[ i ] = managedObjs[managedCount-1];
  		    managedCount--;
  		}
  		return;
  	    }
  	}
      }
      
      // --------------------------------------------------------- Public Methods
  
      public void start() {
  	// Start the background reaper thread
  	if( reaper==null) {
  	    reaper=new Reaper("Expirer");
  	    reaper.addCallback( this, checkInterval * 1000 );
  	}
  	
  	reaper.startReaper();
      }
  
      public void stop() {
  	reaper.stopReaper();
      }
  
  
      // -------------------------------------------------------- Private Methods
  
      // ThreadPoolRunnable impl
  
      public Object[] getInitData() {
  	return null;
      }
  
      public void runIt( Object td[] ) {
  	long timeNow = System.currentTimeMillis();
  	if( dL > 2 ) debug( "Checking " + timeNow );
  	for( int i=0; i< managedCount; i++ ) {
  	    TimeStamp ts=managedObjs[i];
  	    
  	    if (ts==null || !ts.isValid())
  		continue;
  	    
  	    long maxInactiveInterval = ts.getMaxInactiveInterval();
  	    if( dL > 3 ) debug( "TS: " + maxInactiveInterval + " " +
  				ts.getLastAccessedTime());
  	    if (maxInactiveInterval < 0)
  		continue;
  	    
  	    long timeIdle = timeNow - ts.getLastAccessedTime();
  	    
  	    if (timeIdle >= maxInactiveInterval) {
  		if( expireCallback != null ) {
  		    if( dL > 0 )
  			debug( ts + " " + timeIdle + " " +
  			       maxInactiveInterval );
  		    expireCallback.expired( ts );
  		}
  	    }
  	}
      }
  
      private static final int dL=0;
      private void debug( String s ) {
  	System.out.println("Expirer: " + s );
      }
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/threads/TimeStamp.java
  
  Index: TimeStamp.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.util.threads;
  
  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;
  
  
  /**
   * Marks creation and access times, plus validity.
   *
   * Used for objects that expire - originally Sessions, but 
   * soon Contexts and Servlets ( scalability issues, large server support ).
   * 
   * @author Costin Manolache
   */
  public final class TimeStamp implements  Serializable {
      private long creationTime = 0L;
      private long lastAccessedTime = creationTime;
      private long thisAccessedTime = creationTime;
      private boolean isNew = true;
      private long maxInactiveInterval = -1;
      private boolean isValid = false;
  
      Object parent;
      
      public TimeStamp() {
      }
  
      // -------------------- Active methods --------------------
  
      /**
       *  Access notification. This method takes a time parameter in order
       *  to allow callers to efficiently manage expensive calls to
       *  System.currentTimeMillis() 
       */
      public void touch(long time) {
  	this.lastAccessedTime = this.thisAccessedTime;
  	this.thisAccessedTime = time;
  	this.isNew=false;
      }
  
      // -------------------- Property access --------------------
  
      /** Returns the owner of this stamp ( the object that is
       *  time-stamped
       */
      public void setParent( Object o ) {
  	parent=o;
      }
  
      public Object getParent() {
  	return parent;
      }
  
      public void setCreationTime(long time) {
  	this.creationTime = time;
  	this.lastAccessedTime = time;
  	this.thisAccessedTime = time;
      }
  
  
      public long getLastAccessedTime() {
  	return lastAccessedTime;
      }
  
      /** Inactive interval in millis - the time is computed
       *  in millis, convert to secs in the upper layer
       */
      public long getMaxInactiveInterval() {
  	return maxInactiveInterval;
      }
  
      public void setMaxInactiveInterval(long interval) {
  	maxInactiveInterval = interval;
      }
  
      public boolean isValid() {
  	return isValid;
      }
  
      public void setValid(boolean isValid) {
  	this.isValid = isValid;
      }
  
      public boolean isNew() {
  	return isNew;
      }
  
      public void setNew(boolean isNew) {
  	this.isNew = isNew;
      }
  
      public long getCreationTime() {
  	return creationTime;
      }
  
      // -------------------- Maintainance --------------------
  
      public void recycle() {
  	creationTime = 0L;
  	lastAccessedTime = 0L;
  	maxInactiveInterval = -1;
  	isNew = true;
  	isValid = false;
      }
  
  }
  
  
  
  
  1.4       +27 -8     jakarta-tomcat/src/facade22/org/apache/tomcat/facade/HttpSessionFacade.java
  
  Index: HttpSessionFacade.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/facade22/org/apache/tomcat/facade/HttpSessionFacade.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- HttpSessionFacade.java	2000/09/24 20:01:43	1.3
  +++ HttpSessionFacade.java	2000/09/25 06:10:53	1.4
  @@ -113,6 +113,7 @@
       // -------------------- public facade --------------------
   
       public String getId() {
  +	checkValid();
   	return realSession.getId().toString();
       }
   
  @@ -151,7 +152,10 @@
        */
       public void invalidate() {
   	checkValid();
  -	realSession.expire();
  +	ServerSessionManager ssm=realSession.getSessionManager();
  +	ssm.removeSession( realSession );
  +	realSession.removeAllAttributes();
  +	realSession.getTimeStamp().setValid( false );
       }
   
       /**
  @@ -179,12 +183,19 @@
       public void setAttribute(String name, Object value) {
   	checkValid();
   
  -	if (realSession.isDistributable() &&
  -	  !(value instanceof Serializable))
  -	    throw new IllegalArgumentException
  -		(sm.getString("standardSession.setAttribute.iae"));
  +	ServerSessionManager ssm=realSession.getSessionManager();
  +	// Original code - it's up to session manager to decide
  +	// what it can handle. 
  +	// 	if (ssm.isDistributable() &&
  +	// 	  !(value instanceof Serializable))
  +	// 	    throw new IllegalArgumentException
  +	// 		(sm.getString("standardSession.setAttribute.iae"));
   	
   	realSession.setAttribute( name, value );
  +	if (value instanceof HttpSessionBindingListener)
  +	    ((HttpSessionBindingListener) value).valueBound
  +		(new HttpSessionBindingEvent( this, name));
  +
       }
   
       /**
  @@ -228,7 +239,7 @@
        * @deprecated
        */
       public void removeValue(String name) {
  -	realSession.removeAttribute(name);
  +	removeAttribute(name);
       }
   
       /**
  @@ -247,16 +258,24 @@
        */
       public void removeAttribute(String name) {
   	checkValid();
  +	Object object=realSession.getAttribute( name );
   	realSession.removeAttribute(name);
  +	if (object instanceof HttpSessionBindingListener) {
  +	    ((HttpSessionBindingListener) object).valueUnbound
  +		(new HttpSessionBindingEvent( this, name));
  +	}
  +
       }
   
       public void setMaxInactiveInterval(int interval) {
  -	realSession.getTimeStamp().setMaxInactiveInterval( interval );
  +	realSession.getTimeStamp().setMaxInactiveInterval( interval * 1000 );
       }
   
       public int getMaxInactiveInterval() {
   	checkValid();
  -	return realSession.getTimeStamp().getMaxInactiveInterval();
  +	// We use long because it's better to do /1000 here than
  +	// every time the internal code does expire
  +	return (int)realSession.getTimeStamp().getMaxInactiveInterval()/1000;
       }
   
       // duplicated code, private
  
  
  
  1.4       +4 -5      jakarta-tomcat/src/facade22/org/apache/tomcat/facade/Servlet22Manager.java
  
  Index: Servlet22Manager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/facade22/org/apache/tomcat/facade/Servlet22Manager.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- Servlet22Manager.java	2000/09/24 18:10:55	1.3
  +++ Servlet22Manager.java	2000/09/25 06:10:54	1.4
  @@ -87,14 +87,13 @@
       private Servlet22Manager() {
       }
   
  -    // XXX XXX XXX The key to open the gate - this is the first attempt,
  -    // we need a safer mechanism ! 
  -    // 
  -    /** The only way to construct a FacadeManager is if you have a valid Context.
  +    /** The only way to construct a FacadeManager is if you have a valid
  +     *  Context.
        *  XXX make sure Context can't be constructed without right permission.
        *  ( all internal tomcat objects have to be revisited ).
        *
  -     * The FacadeManager will work only for the Context used at construction time.
  +     * The FacadeManager will work only for the Context used at construction
  +     * time.
        */
       public Servlet22Manager(Context ctx)  {
   	//	checkAccess();