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/04/26 20:57:38 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/util FileUtil.java

costin      00/04/26 11:57:37

  Modified:    src/share/org/apache/tomcat/context DefaultCMSetter.java
                        WebXmlReader.java
               src/share/org/apache/tomcat/core ContextManager.java
                        ServletWriterFacade.java package.html
               src/share/org/apache/tomcat/request SimpleMapper1.java
               src/share/org/apache/tomcat/startup Tomcat.java
               src/share/org/apache/tomcat/util FileUtil.java
  Log:
  - Renamed TomcatHome to InstallDir in ContextManager ( it generates
  a lot of confusion ). Old methods still exist, but will be removed if
  nobody is using them.
  
  - moved deprecated methods at the end, preparing for removal.
  
  - Added comments in ContextManager and more comments in package.html about
  how it works.
  
  Problem to be resolved:
  Port/HostName do not belong here - tomcat can't guess what is the real
  name and port for the default server ( unless it's working as a standalone
  http server).  The only use for those properties is in generating the
  temp. work dir name. I think we should remove them ( the adapter will provide
  the real values for request ).
  
  Revision  Changes    Path
  1.30      +2 -2      jakarta-tomcat/src/share/org/apache/tomcat/context/DefaultCMSetter.java
  
  Index: DefaultCMSetter.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/context/DefaultCMSetter.java,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -u -r1.29 -r1.30
  --- DefaultCMSetter.java	2000/04/25 17:54:10	1.29
  +++ DefaultCMSetter.java	2000/04/26 18:57:34	1.30
  @@ -114,10 +114,10 @@
   	// ( getHome returns the "instance" of tomcat, it may have it's own log, work, webapps,
   	//   getTomcatHome is where tomcat is installed )
   	//  Note: home defaults to tomcat.home if none is set...
  -	File f=new File( cm.getTomcatHome() + "/conf/web.xml");
  +	File f=new File( cm.getInstallDir() + "/conf/web.xml");
   	if( ! f.exists() ) {
   	    throw new TomcatException( "Wrong tomcat home " +
  -	                               cm.getTomcatHome());
  +	                               cm.getInstallDir());
   	}
   	// update the workdir
   	String workDir=cm.getWorkDir();
  
  
  
  1.18      +1 -1      jakarta-tomcat/src/share/org/apache/tomcat/context/WebXmlReader.java
  
  Index: WebXmlReader.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/context/WebXmlReader.java,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- WebXmlReader.java	2000/03/28 00:04:02	1.17
  +++ WebXmlReader.java	2000/04/26 18:57:34	1.18
  @@ -36,7 +36,7 @@
   
   	    // try the default ( installation ) 
   	    if( ! default_xml.exists() ) {
  -		String tchome=ctx.getContextManager().getTomcatHome();
  +		String tchome=ctx.getContextManager().getInstallDir();
   		if( tchome != null )
   		    default_xml=new File( tchome + "/conf/web.xml");
   	    }
  
  
  
  1.75      +525 -393  jakarta-tomcat/src/share/org/apache/tomcat/core/ContextManager.java
  
  Index: ContextManager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/ContextManager.java,v
  retrieving revision 1.74
  retrieving revision 1.75
  diff -u -r1.74 -r1.75
  --- ContextManager.java	2000/04/25 17:54:13	1.74
  +++ ContextManager.java	2000/04/26 18:57:35	1.75
  @@ -72,83 +72,194 @@
   import java.net.*;
   import java.util.*;
   
  +/* XXX The main function of CM is to serve as an entry point into
  +   tomcat and manage a list of resources that are part of the servlet
  +   processing. ( manage == keep a list and provide centralized access ).
  +
  +   It also have helper functions for common callbacks - but we need to
  +   review and change that.
  +*/
  +/*
  + * It is possible to extend and override some of the methods ( this is not
  + * "final" ), but this is an extreme case and shouldn't be used - if you want
  + * to extend the server you should use interceptors.
  + * Another extreme case is having more than one ContextManager instances.
  + * Each will corespond to a separate servlet engine/container that will work
  + * independent of each other in the same VM ( each having possible multiple
  + * virtual hosts, etc). Both uses are not forbiden, but shouldn't be used
  + * unless there is real need for that - and if that happen we should 
  + * add interfaces to express the use cases.
  + */
  +
   
   /**
  - * A collection class representing the Contexts associated with a particular
  - * Server.  The managed Contexts can be accessed by path.
  + * ContextManager is the entry point and "controler" of the servlet execution.
  + * It maintains a list of WebApplications and a list of global event interceptors
  + * that are set up to handle the actual execution.
  + * 
  + * The ContextManager is a helper that will direct the request processing flow
  + * from its arrival from the server/protocl adapter ( in service() ).
  + * It is also responsible for controlling the request processing steps, from
  + * request parsing and mapping, auth, autorization, pre/post service, actual
  + * invocation and logging.
  + * 
  + * It will also store properties that are global to the servlet container - 
  + * like root directory, install dir, work dir. 
    *
  - * It also store global default properties - the server name and port ( returned by
  - * getServerName(), etc) and workdir.
    *
    * @author James Duncan Davidson [duncan@eng.sun.com]
    * @author James Todd [gonzo@eng.sun.com]
    * @author Harish Prabandham
  + * @author costin@eng.sun.com
    */
  -public class ContextManager {
  +public class ContextManager { 
  +
       /**
        * The string constants for this ContextManager.
        */
       private StringManager sm = StringManager.getManager("org.apache.tomcat.core");
   
  +    /** Global interceptors - all requests that will be served by this
  +	engine will pass those filters
  +    */
       private Vector requestInterceptors = new Vector();
       private Vector contextInterceptors = new Vector();
       
  -    // cache - faster access
  +    // cache - faster access to interceptors, using [] instead of Vector
       ContextInterceptor cInterceptors[];
       RequestInterceptor rInterceptors[];
       
  -    /**
  -     * The set of Contexts associated with this ContextManager,
  -     * keyed by context paths.
  -     * @deprecated - the server shouldn't make any assumptions about
  -     *  the key.
  +    /** Adapters for the incoming protocol
        */
  -    private Hashtable contexts = new Hashtable();
  +    Vector connectors=new Vector();
  +
       /** Contexts managed by this server
        */
       private Vector contextsV=new Vector();
   
  -    public static final String DEFAULT_HOSTNAME="localhost";
  -    public static final int DEFAULT_PORT=8080;
  -    public static final String DEFAULT_WORK_DIR="work";
  -    
  -    /**
  -     * The virtual host name for the Server this ContextManager
  -     * is associated with.
  -     */
  -    String hostname;
  +    int debug=0;
   
  -    /**
  -     * The port number being listed to by the Server this ContextManager
  -     * is associated with.
  +    // Global properties for this tomcat instance:
  +    
  +    /** Private workspace for this server
        */
  -    int port;
  -
  -    int debug=0;
       String workDir;
   
  -    // Instalation directory  
  +    /** The base directory where this instance runs.
  +     *  It can be different from the install directory to
  +     *  allow one install per system and multiple users
  +     */
       String home;
  -    String tomcatHome;
   
  -    Vector connectors=new Vector();
  +    /** The directory where tomcat is installed
  +     */  
  +    String installDir;
   
  +    /** Default work dir, relative to home
  +     */
  +    public static final String DEFAULT_WORK_DIR="work";
       
       /**
        * Construct a new ContextManager instance with default values.
        */
       public ContextManager() {
       }
  +
  +
  +    // -------------------- Setable properties: tomcat directories  --------------------
  +
  +    /** 
  +     *  The home of the tomcat instance - you can have multiple
  +     *  users running tomcat, with a shared install directory.
  +     *  Every instance will have its own logs, webapps directory
  +     *  and local config, all relative to this directory.
  +     */
  +    public void setHome(String home) {
  +	this.home=FileUtil.getCanonicalPath( home ); 
  +	log( "Setting home to " + this.home );
  +    }
  +    
  +    /** 
  +     *  The home of the tomcat instance - you can have multiple
  +     *  users running tomcat, with a shared install directory.
  +     *  Every instance will have its own logs, webapps directory
  +     *  and local config, all relative to this directory.
  +     *
  +     *  If no home is configured we'll try the install dir
  +     *  XXX clean up the order and process of guessing - maybe we can
  +     *  just throw error instead of guessing wrong.
  +     */
  +    public String getHome() {
  +	if(home!=null) return home;
  +
  +	// If none defined, assume tomcat.home is used as base.
  +	if( installDir != null )
  +	    home=FileUtil.getCanonicalPath( installDir );
  +
  +	if(home!=null) return home;
  +
  +	// try at least the system property
  +	home=FileUtil.getCanonicalPath( System.getProperty("tomcat.home") );	
  +	if(home!=null) return home;
  +	
  +	home=FileUtil.getCanonicalPath( "." ); // try current dir - we should throw an exception
  +	return home;
  +    }
  +
  +    /** Get installation directory, where libraries and default files
  +     *	are located.  If path specified is relative, 
  +     *  evaluate it relative to the current working directory.
  +     */
  +    public String getInstallDir() {
  +	if(installDir!= null) return installDir;
  +	
  +	installDir=System.getProperty("tomcat.home");
  +	if(installDir!= null) return installDir;
  +
  +	// If the property is not set ( for example JNI worker ) assume
  +	// at least home is set up corectly.
  +	installDir=getHome();
  +	return installDir;
  +    }
  +
  +    /** Set installation directory, where libraries and default files
  +     *	are located.  If path specified is relative, 
  +     *  evaluate it relative to the current working directory.
  +     */
  +    public void setInstallDir( String tH ) {
  +	installDir=tH;
  +    }
  +    
  +    /**
  +     * WorkDir property - where all working files will be created
  +     */ 
  +    public void setWorkDir( String wd ) {
  +	if(debug>0) log("set work dir " + wd);
  +	this.workDir=wd;
  +    }
  +
  +    /**
  +     * WorkDir property - where all working files will be created
  +     */ 
  +    public String getWorkDir() {
  +	if( workDir==null)
  +	    workDir=getHome() + File.separator + DEFAULT_WORK_DIR;
  +	return workDir;
  +    }
   
  -    // -------------------- Context repository --------------------
  +    // -------------------- Support functions --------------------
   
  -    /** Set default settings ( interceptors, connectors, loader, manager )
  +    /**
  +     *  Set default settings ( interceptors, connectors, loader, manager )
        *  It is called from init if no connector is set up  - note that we
        *  try to avoid any "magic" - you either set up everything ( using
        *  server.xml or alternatives) or you don't set up and then defaults
        *  will be used.
        * 
        *  Set interceptors or call setDefaults before adding contexts.
  +     *
  +     *  This is mostly used to allow "0 config" case ( you just want the
  +     *  reasonable defaults and nothing else ).
        */
       public void setDefaults() {
   	if(connectors.size()==0) {
  @@ -177,40 +288,30 @@
   	}
       }
        
  -    
  -    /**
  -     * Get the names of all the contexts in this server.
  -     * @deprecated Path is not "unique key".
  -     */
  -    public Enumeration getContextNames() {
  -        return contexts.keys();
  -    }
  -
  -    /** Return the list of contexts managed by this server
  -     */
  -    public Enumeration getContexts() {
  -	return contextsV.elements();
  -    }
  -    
       /** Init() is called after the context manager is set up
  -     *  and configured. 
  +     *  and configured. It will init all internal components
  +     *  to be ready for start.
  +     *
  +     *  There is a difference between Context and Adapters - the
  +     *  adapter has start/stop, the Context has init/shutdown(destroy
  +     *  may be a better name ? ). ( Initializing is different from starting.)
        */
       public void init()  throws TomcatException {
  -	String cp=System.getProperty( "java.class.path");
  -	log( "Tomcat install = " + getTomcatHome());
  +	log( "Tomcat install = " + getInstallDir());
   	log( "Tomcat home = " + home);
  -	if(debug>0 ) log( "Tomcat classpath = " +  cp );
  -	//	long time=System.currentTimeMillis();
  +	if(debug>0 ) log( "Tomcat classpath = " +  System.getProperty( "java.class.path" ));
  +
  +	setAccount( ACC_INIT_START, System.currentTimeMillis());
  +	
   	ContextInterceptor cI[]=getContextInterceptors();
   	for( int i=0; i< cI.length; i++ ) {
   	    cI[i].engineInit( this );
   	}
  -	
  +
       	// init contexts
   	Enumeration enum = getContexts();
  -	Context context=null;
   	while (enum.hasMoreElements()) {
  -	    context = (Context)enum.nextElement();
  +	    Context context = (Context)enum.nextElement();
   	    try {
   		initContext( context );
   	    } catch (TomcatException ex ) {
  @@ -222,18 +323,32 @@
   		}
   	    }
   	}
  -	//	log("Time to initialize: "+ (System.currentTimeMillis()-time), Logger.INFORMATION);
  +	setAccount( ACC_INIT_END, System.currentTimeMillis() );
       }
   
  +    /** Will shutdown all contexts
  +     */
  +    public void shutdown() throws TomcatException {
  +	Enumeration enum = getContexts();
  +	while (enum.hasMoreElements()) {
  +	    removeContext((Context)enum.nextElement());
  +	}
  +
  +	ContextInterceptor cI[]=getContextInterceptors();
  +	for( int i=0; i< cI.length; i++ ) {
  +	    cI[i].engineShutdown( this );
  +	}
  +    }
       
       /**
  -     * Initializes this context to take on requests. This action
  +     * Initializes this context to be able to accept requests. This action
        * will cause the context to load it's configuration information
        * from the webapp directory in the docbase.
        *
  -     * <p>This method may only be called once and must be called
  -     * before any requests are handled by this context and after setContextManager()
  -     * is called.
  +     * <p>This method must be called
  +     * before any requests are handled by this context. It will be called
  +     * after the context was added, typically when the engine starts
  +     * or after the admin added a new context.
        */
       public void initContext( Context ctx ) throws TomcatException {
   	ContextInterceptor cI[]=getContextInterceptors();
  @@ -241,11 +356,16 @@
   	    cI[i].contextInit( ctx );
   	}
       }
  -    
  +
  +    /** Stop the context and release all resources.
  +     */
       public void shutdownContext( Context ctx ) throws TomcatException {
  -	// shut down and servlet
  +	// XXX This is here by accident, it should be moved as part
  +	// of a normal context interceptor that will handle all standard
  +	// start/stop actions
  +	
  +	// shut down and servlets
   	Enumeration enum = ctx.getServletNames();
  -
   	while (enum.hasMoreElements()) {
   	    String key = (String)enum.nextElement();
   	    ServletWrapper wrapper = ctx.getServletByName( key );
  @@ -259,42 +379,32 @@
   	}
       }
       
  -    /** Will start the connectors and begin serving requests
  +    /** Will start the connectors and begin serving requests.
  +     *  It must be called after init.
        */
  -    public void start() throws Exception {//TomcatException {
  +    public void start() throws Exception {// XXX TomcatException {
   	Enumeration connE=getConnectors();
   	while( connE.hasMoreElements() ) {
   	    ((ServerConnector)connE.nextElement()).start();
   	}
       }
   
  -    public void stop() throws Exception {//TomcatException {
  +    /** Will stop all connectors
  +     */
  +    public void stop() throws Exception {// XXX TomcatException {
   	if(debug>0) log("Stopping context manager ");
   	Enumeration connE=getConnectors();
   	while( connE.hasMoreElements() ) {
   	    ((ServerConnector)connE.nextElement()).stop();
  -	}
  -
  -	ContextInterceptor cI[]=getContextInterceptors();
  -	Enumeration enum = getContextNames();
  -	while (enum.hasMoreElements()) {
  -	    removeContext((String)enum.nextElement());
   	}
  +	shutdown();
       }
   
  -    /**
  -     * Gets a context by it's name, or <code>null</code> if there is
  -     * no such context.
  -     *
  -     * @param name Name of the requested context
  -     * @deprecated Use an external iterator to find the context that
  -     *  matches your conditions.
  -     *
  +    // -------------------- Contexts -------------------- 
  +    /** Return the list of contexts managed by this server
        */
  -    public Context getContext(String name) {
  -	// System.out.println("Using deprecated getContext");
  -	//	/*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
  -	return (Context)contexts.get(name);
  +    public Enumeration getContexts() {
  +	return contextsV.elements();
       }
       
       /**
  @@ -327,27 +437,6 @@
   	contextsV.addElement( ctx );
       }
       
  -    /**
  -     * Shut down and removes a context from service.
  -     *
  -     * @param name Name of the Context to be removed
  -     * @deprecated Use removeContext( Context ).
  -     */
  -    public void removeContext(String name) throws TomcatException {
  -	Context context = (Context)contexts.get(name);
  -	log( "Removing context " + context.toString());
  -
  -	ContextInterceptor cI[]=getContextInterceptors();
  -	for( int i=0; i< cI.length; i++ ) {
  -	    cI[i].removeContext( this, context );
  -	}
  -
  -	if(context != null) {
  -	    shutdownContext( context );
  -	    contexts.remove(name);
  -	}
  -    }
  -
       /** Shut down and removes a context from service
        */
       public void removeContext( Context context ) throws TomcatException {
  @@ -363,7 +452,9 @@
   	shutdownContext( context );
   	contextsV.removeElement(context);
       }
  -    
  +
  +    /** Notify interceptors that a new container was added.
  +     */
       public void addContainer( Container container )
       	throws TomcatException
       {
  @@ -373,6 +464,8 @@
   	}
       }
   
  +    /** Notify interceptors that a container was removed.
  +     */
       public void removeContainer( Container container )
   	throws TomcatException
       {
  @@ -449,236 +542,210 @@
   	}
   	return cInterceptors;
       }
  +    // -------------------- Request processing / subRequest --------------------
  +    // -------------------- Main request processing methods -------------------- 
   
  -    public void addLogger(Logger logger) {
  -	// Will use this later once I feel more sure what I want to do here.
  -	// -akv
  -	// firstLog=false;
  -	//	if("tc_log".equals( logger.getName()) cmLog=logger;
  +    /** Prepare the req/resp pair for use in tomcat.
  +     *  Call it after you create the request/response objects
  +     */
  +    public void initRequest( Request req, Response resp ) {
  +	// used to be done in service(), but there is no need to do it
  +	// every time. 
  +	// We may add other special calls here.
  +	// XXX Maybe make it a callback? 
  +	resp.setRequest( req );
  +	req.setResponse( resp );
  +	req.setContextManager( this );
       }
  +    
  +    /** This is the entry point in tomcat - the connectors ( or any other
  +     *  component able to generate Request/Response implementations ) will
  +     *  call this method to get it processed.
  +     *  XXX make sure the alghoritm is right, deal with response codes
  +     */
  +    public void service( Request rrequest, Response rresponse ) {
  +	try {
  +	    /* assert rrequest/rresponse are set up
  +	       corectly - have cm, and one-one relation
  +	    */
  +	    // wront request - parsing error
  +	    int status=rresponse.getStatus();
   
  +	    if( status < 400 )
  +		status= processRequest( rrequest );
  +	    
  +	    if(status==0)
  +		status=authenticate( rrequest, rresponse );
  +	    if(status == 0)
  +		status=authorize( rrequest, rresponse );
  +	    if( status == 0 ) {
  +		rrequest.getWrapper().handleRequest(rrequest, rresponse);
  +	    } else {
  +		// something went wrong
  +		handleError( rrequest, rresponse, null, status );
  +	    }
  +	} catch (Throwable t) {
  +	    handleError( rrequest, rresponse, t, 0 );
  +	}
  +	try {
  +	    rresponse.finish();
  +	    rrequest.recycle();
  +	    rresponse.recycle();
  +	} catch( Throwable ex ) {
  +	    if(debug>0) log( "Error closing request " + ex);
  +	}
  +	return;
  +    }
   
  -    // -------------------- Defaults for all contexts --------------------
  -    /** The root directory of tomcat
  +    /** Will find the ServletWrapper for a servlet, assuming we already have
  +     *  the Context. This is also used by Dispatcher and getResource - where the Context
  +     *  is already known. 
        */
  -    public String getHome() {
  -	if(home!=null) return home;
  +    public int processRequest( Request req ) {
  +	if(debug>9) log("ProcessRequest: "+req.toString());
   
  -	// If none defined, assume tomcat.home is used as base.
  -	home=getCanonicalPath( tomcatHome );
  -	if(home!=null) return home;
  +	for( int i=0; i< requestInterceptors.size(); i++ ) {
  +	    ((RequestInterceptor)requestInterceptors.elementAt(i)).contextMap( req );
  +	}
   
  -	// try at least the system property
  -	home=getCanonicalPath( System.getProperty("tomcat.home") );	
  -	if(home!=null) return home;
  +	for( int i=0; i< requestInterceptors.size(); i++ ) {
  +	    ((RequestInterceptor)requestInterceptors.elementAt(i)).requestMap( req );
  +	}
  +
  +	if(debug>9) log("After processing: "+req.toString());
   	
  -	home=getCanonicalPath( "." ); // try current dir - we should throw an exception
  -	return home;
  +	return 0;
       }
  -    
  -    /** Tomcat installation directory, where libraries and default files are located
  +
  +    /** Call all authentication callbacks. If any of them is able to identify the user
  +     *  it will set the principal in req.
        */
  -    public String getTomcatHome() {
  -	if(tomcatHome!= null) return tomcatHome;
  -	
  -	tomcatHome=System.getProperty("tomcat.home");
  -	if(tomcatHome!= null) return tomcatHome;
  +    public int authenticate( Request req, Response res ) {
  +	for( int i=0; i< requestInterceptors.size(); i++ ) {
  +	    ((RequestInterceptor)requestInterceptors.elementAt(i)).authenticate( req, res );
  +	}
  +	return 0;
  +    }
   
  -	// If the property is not set ( for example JNI worker ) assume
  -	// at least home is set up corectly.
  -	tomcatHome=getHome();
  -	return tomcatHome;
  +    /** Call all authorization callbacks. The "require valid user" attributes are probably
  +     *  set during the mapping stage ( for efficiency), but it can be done here too.
  +     */
  +    int authorize( Request req, Response res ) {
  +	for( int i=0; i< requestInterceptors.size(); i++ ) {
  +	    int err = ((RequestInterceptor)requestInterceptors.elementAt(i)).authorize( req, res );
  +	    if ( err != 0 ) return err;
  +	}
  +	return 0;
       }
   
  -    public void setTomcatHome( String tH ) {
  -	tomcatHome=tH;
  +    /** Call beforeBody callbacks. Before body allows you do do various actions before
  +	the first byte of the response is sent. After all those callbacks are called
  +	tomcat may send the status and headers
  +    */
  +    int doBeforeBody( Request req, Response res ) {
  +	for( int i=0; i< requestInterceptors.size(); i++ ) {
  +	    ((RequestInterceptor)requestInterceptors.elementAt(i)).beforeBody( req, res );
  +	}
  +	return 0;
       }
   
  -    // Used in few places.
  -    static String getCanonicalPath(String name ) {
  -	if( name==null ) return null;
  -        File f = new File(name);
  -        try {
  -            return  f.getCanonicalPath();
  -        } catch (IOException ioe) {
  -	    ioe.printStackTrace();
  -	    return name; // oh well, we tried...
  -        }
  +    /** Call beforeCommit callbacks. This allows interceptors to manipulate the
  +	buffer before it gets sent.
  +	XXX Add a standard way to access the body. The method was not used too
  +	much, we need a review and maybe change in parameters.
  +    */
  +    int doBeforeCommit( Request req, Response res ) {
  +	for( int i=0; i< requestInterceptors.size(); i++ ) {
  +	    ((RequestInterceptor)requestInterceptors.elementAt(i)).beforeCommit( req, res );
  +	}
  +	return 0;
       }
  -    
  -    /** 
  -     * Set installation directory.  If path specified is relative, 
  -     * evaluate it relative to the current working directory.
  -     *
  -     * This is used for the home attribute and it's used to find webapps
  -     * and conf. Note that libs are probably already configured, so it will
  -     * not affect that.
  -     */
  -    public void setHome(String home) {
  -	this.home=getCanonicalPath( home ); 
  -	log( "Setting home to " + this.home );
  -    }
  -    
  -    /**
  -     * Sets the port number on which this server listens.
  -     *
  -     * @param port The new port number
  -     * @deprecated 
  -     */
  -    public void setPort(int port) {
  -	this.port=port;
  -    }
  -
  -    /**
  -     * Gets the port number on which this server listens.
  -     * @deprecated 
  -     */
  -    public int getPort() {
  -	if(port==0) port=DEFAULT_PORT;
  -	return port;
  -    }
   
  -    /**
  -     * Sets the virtual host name of this server.
  -     *
  -     * @param host The new virtual host name
  -     * @deprecated 
  -     */
  -    public void setHostName( String host) {
  -	this.hostname=host;
  -    }
  -
  -    /**
  -     * Gets the virtual host name of this server.
  -     * @deprecated 
  -     */
  -    public String getHostName() {
  -	if(hostname==null)
  -	    hostname=DEFAULT_HOSTNAME;
  -	return hostname;
  -    }
  -
  -    /**
  -     * WorkDir property - where all temporary files will be created
  -     */ 
  -    public void setWorkDir( String wd ) {
  -	if(debug>0) log("set work dir " + wd);
  -	this.workDir=wd;
  -    }
  -
  -    public String getWorkDir() {
  -	if( workDir==null)
  -	    workDir=DEFAULT_WORK_DIR;
  -	return workDir;
  -    }
  -
  -    // -------------------- Request processing / subRequest --------------------
  -    
  -    /** Common for all connectors, needs to be shared in order to avoid
  -	code duplication
  +    /** Call afterBody callbacks. It is called after the servlet finished sending the
  +	response ( either closeing the stream or ending ). You can deal with connection
  +	reuse or do other actions
       */
  -    public void service( Request rrequest, Response rresponse ) {
  -	try {
  -	    /* assert rrequest/rresponse are set up
  -	       corectly - have cm, and one-one relation
  -	    */
  -
  -	    // wront request - parsing error
  -	    int status=rresponse.getStatus();
  -
  -	    if( status < 400 )
  -		status= processRequest( rrequest );
  -	    
  -	    if(status==0)
  -		status=authenticate( rrequest, rresponse );
  -	    if(status == 0)
  -		status=authorize( rrequest, rresponse );
  -	    if( status == 0 ) {
  -		rrequest.getWrapper().handleRequest(rrequest, rresponse);
  -	    } else {
  -		// something went wrong
  -		handleError( rrequest, rresponse, null, status );
  -	    }
  -	} catch (Throwable t) {
  -	    handleError( rrequest, rresponse, t, 0 );
  -	}
  -	try {
  -	    rresponse.finish();
  -	    rrequest.recycle();
  -	    rresponse.recycle();
  -	} catch( Throwable ex ) {
  -	    if(debug>0) log( "Error closing request " + ex);
  +    int doAfterBody( Request req, Response res ) {
  +	for( int i=0; i< requestInterceptors.size(); i++ ) {
  +	    ((RequestInterceptor)requestInterceptors.elementAt(i)).afterBody( req, res );
   	}
  -	//	log( "Done with request " + rrequest );
  -	//	System.out.print("C");
  -	return;
  +	return 0;
       }
  +    
  +    // -------------------- Sub-Request mechanism --------------------
   
  -    /** Will find the ServletWrapper for a servlet, assuming we already have
  -     *  the Context. This is used by Dispatcher and getResource - where the Context
  -     *  is already known.
  +    /** Create a new sub-request in a given context, set the context "hint"
  +     *  This is a particular case of sub-request that can't get out of
  +     *  a context ( and we know the context before - so no need to compute it again)
  +     *
  +     *  Note that session and all stuff will still be computed.
        */
  -    public int processRequest( Request req ) {
  -	if(debug>9) log("ProcessRequest: "+req.toString());
  +    public Request createRequest( Context ctx, String urlPath ) {
  +	// assert urlPath!=null
   
  -	for( int i=0; i< requestInterceptors.size(); i++ ) {
  -	    ((RequestInterceptor)requestInterceptors.elementAt(i)).contextMap( req );
  -	}
  +	// deal with paths with parameters in it
  +	String contextPath=ctx.getPath();
  +	String origPath=urlPath;
   
  -	for( int i=0; i< requestInterceptors.size(); i++ ) {
  -	    ((RequestInterceptor)requestInterceptors.elementAt(i)).requestMap( req );
  +	// append context path
  +	if( !"".equals(contextPath) && !"/".equals(contextPath)) {
  +	    if( urlPath.startsWith("/" ) )
  +		urlPath=contextPath + urlPath;
  +	    else
  +		urlPath=contextPath + "/" + urlPath;
  +	} else {
  +	    // root context
  +	    if( !urlPath.startsWith("/" ) )
  +		urlPath= "/" + urlPath;
   	}
  -
  -	if(debug>9) log("After processing: "+req.toString());
  -
  -	
  -	return 0;
  -    }
   
  -    int authenticate( Request req, Response res ) {
  -	for( int i=0; i< requestInterceptors.size(); i++ ) {
  -	    ((RequestInterceptor)requestInterceptors.elementAt(i)).authenticate( req, res );
  -	}
  -	return 0;
  +	if( debug >4 ) log("createRequest " + origPath + " " + urlPath  );
  +	Request req= createRequest( urlPath );
  +	String host=ctx.getHost();
  +	if( host != null) req.setServerName( host );
  +	return req;
       }
   
  -    int authorize( Request req, Response res ) {
  -	for( int i=0; i< requestInterceptors.size(); i++ ) {
  -	    int err = ((RequestInterceptor)requestInterceptors.elementAt(i)).authorize( req, res );
  -	    if ( err != 0 ) return err;
  +    /** Create a new sub-request, deal with query string
  +     */
  +    public Request createRequest( String urlPath ) {
  +	String queryString=null;
  +	int i = urlPath.indexOf("?");
  +	int len=urlPath.length();
  +	if (i>-1) {
  +	    if(i<len)
  +		queryString =urlPath.substring(i + 1, urlPath.length());
  +	    urlPath = urlPath.substring(0, i);
   	}
  -	return 0;
  -    }
  -
  +	///*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
   
  -    int doBeforeBody( Request req, Response res ) {
  -	for( int i=0; i< requestInterceptors.size(); i++ ) {
  -	    ((RequestInterceptor)requestInterceptors.elementAt(i)).beforeBody( req, res );
  -	}
  -	return 0;
  -    }
  +	/** Creates an "internal" request
  +	 */
  +	RequestImpl lr = new RequestImpl();
  +	//	RequestAdapterImpl reqA=new RequestAdapterImpl();
  +	//lr.setRequestAdapter( reqA);
  +	lr.setRequestURI( urlPath );
  +	lr.setQueryString( queryString );
  +	//	lr.processQueryString();
   
  -    int doBeforeCommit( Request req, Response res ) {
  -	for( int i=0; i< requestInterceptors.size(); i++ ) {
  -	    ((RequestInterceptor)requestInterceptors.elementAt(i)).beforeCommit( req, res );
  -	}
  -	return 0;
  +	//	lr.setContext( ctx );
  +	
  +	// XXX set query string too 
  +	return lr;
       }
   
  -    int doAfterBody( Request req, Response res ) {
  -	for( int i=0; i< requestInterceptors.size(); i++ ) {
  -	    ((RequestInterceptor)requestInterceptors.elementAt(i)).afterBody( req, res );
  -	}
  -	return 0;
  -    }
  +    // -------------------- Error handling --------------------
       
  +    /** General error handling mechanism. It will try to find an error handler
  +     * or use the default handler.
  +     * XXX very complex - need a rewrite. 
  +     */
       void handleError( Request req, Response res , Throwable t, int code ) {
   	Context ctx = req.getContext();
   	if(ctx==null) {
   	    ctx=getContext("");
   	}
   	if(ctx.getDebug() > 4 ) ctx.log("In error handler " + code + " " + t +  " / " + req );
  -	//	/*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
   	String path=null;
   	ServletWrapper errorServlet=null;
   
  @@ -687,7 +754,6 @@
   	    errorServlet=ctx.getServletByName("tomcat.errorPage");
   	} else if( req.getAttribute("javax.servlet.error.status_code") != null ||
   	    req.getAttribute("javax.servlet.error.exception_type")!=null) {
  -	    //  /*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
   	    if( ctx.getDebug() > 0 ) ctx.log( "Error: exception inside exception servlet " +
   					      req.getAttribute("javax.servlet.error.status_code") + " " +
   					      req.getAttribute("javax.servlet.error.exception_type"));
  @@ -774,98 +840,9 @@
   	}
   
   	return;
  -    }
  -    
  -    // -------------------- Sub-Request mechanism --------------------
  -
  -    /** Create a new sub-request in a given context, set the context "hint"
  -     *  This is a particular case of sub-request that can't get out of
  -     *  a context ( and we know the context before - so no need to compute it again)
  -     *
  -     *  Note that session and all stuff will still be computed.
  -     */
  -    Request createRequest( Context ctx, String urlPath ) {
  -	// assert urlPath!=null
  -
  -	// deal with paths with parameters in it
  -	String contextPath=ctx.getPath();
  -	String origPath=urlPath;
  -
  -	// append context path
  -	if( !"".equals(contextPath) && !"/".equals(contextPath)) {
  -	    if( urlPath.startsWith("/" ) )
  -		urlPath=contextPath + urlPath;
  -	    else
  -		urlPath=contextPath + "/" + urlPath;
  -	} else {
  -	    // root context
  -	    if( !urlPath.startsWith("/" ) )
  -		urlPath= "/" + urlPath;
  -	}
  -
  -	if( debug >4 ) log("createRequest " + origPath + " " + urlPath  );
  -	Request req= createRequest( urlPath );
  -	String host=ctx.getHost();
  -	if( host != null) req.setServerName( host );
  -	return req;
       }
  -
  -    /** Create a new sub-request, deal with query string
  -     */
  -    public Request createRequest( String urlPath ) {
  -	String queryString=null;
  -	int i = urlPath.indexOf("?");
  -	int len=urlPath.length();
  -	if (i>-1) {
  -	    if(i<len)
  -		queryString =urlPath.substring(i + 1, urlPath.length());
  -	    urlPath = urlPath.substring(0, i);
  -	}
  -	///*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
  -
  -	/** Creates an "internal" request
  -	 */
  -	RequestImpl lr = new RequestImpl();
  -	//	RequestAdapterImpl reqA=new RequestAdapterImpl();
  -	//lr.setRequestAdapter( reqA);
  -	lr.setRequestURI( urlPath );
  -	lr.setQueryString( queryString );
  -	//	lr.processQueryString();
  -
  -	//	lr.setContext( ctx );
  -	
  -	// XXX set query string too 
  -	return lr;
  -    }
  -
  -    /** Prepare the req/resp pair for use in tomcat.
  -	Call it after you create the request/response objects
  -     */
  -    public void initRequest( Request req, Response resp ) {
  -	// used to be done in service(), but there is no need to do it
  -	// every time. 
  -	// We may add other special calls here.
  -	// XXX Maybe make it a callback? 
  -	resp.setRequest( req );
  -	req.setResponse( resp );
  -	req.setContextManager( this );
  -    }
  -
  -    // -------------------- Support for notes
  +    // -------------------- Support for notes --------------------
           
  -    // used to allow interceptors to set specific per/request, per/container
  -    // and per/CM informations.
  -    
  -    // This will allow us to remove all "specialized" methods in
  -    // Request and Container/Context, without losing the functionality.
  -    
  -    // Remember - Interceptors are not supposed to have internal state
  -    // and minimal configuration, all setup is part of the "core", under
  -    // central control.
  -
  -    // We use indexed notes instead of attributes for performance -
  -    // this is internal to tomcat and most of the time in critical path
  -
       /** Note id counters. Synchronized access is not necesarily needed
        *  ( the initialization is in one thread ), but anyway we do it
        */
  @@ -881,6 +858,18 @@
       
       String noteDescription[][]=new String[3][MAX_NOTES];
   
  +    /** used to allow interceptors to set specific per/request, per/container
  +     * and per/CM informations.
  +     *
  +     * This will allow us to remove all "specialized" methods in
  +     * Request and Container/Context, without losing the functionality.
  +     * Remember - Interceptors are not supposed to have internal state
  +     * and minimal configuration, all setup is part of the "core", under
  +     *  central control.
  +     *  We use indexed notes instead of attributes for performance -
  +     * this is internal to tomcat and most of the time in critical path
  +     */
  +    
       /** Create a new note id. Interceptors will get an Id at init time for
        *  all notes that it needs. 
        *
  @@ -899,7 +888,7 @@
   	return noteDescription[noteType][noteId];
       }
       
  -    // -------------------- Per-server notes
  +    // -------------------- Per-server notes -------------------- 
       Object notes[]=new Object[MAX_NOTES];
   
       public void setNote( int pos, Object value ) {
  @@ -913,9 +902,16 @@
       // -------------------- Logging and debug --------------------
       boolean firstLog = true;
       Logger cmLog = null;
  -
  -    
       
  +
  +    public void addLogger(Logger logger) {
  +	// Will use this later once I feel more sure what I want to do here.
  +	// -akv
  +	// firstLog=false;
  +	//	if("tc_log".equals( logger.getName()) cmLog=logger;
  +    }
  +
  +
       public void setDebug( int level ) {
   	if( level != 0 ) System.out.println( "Setting level to " + level);
   	debug=level;
  @@ -926,10 +922,7 @@
       }
       
       public final void log(String msg) {
  -	//	if( msg.startsWith( "<l:" ))
   	doLog( msg );
  -	//else
  -	//doLog("<l:tc>" + msg + "</l:tc>");
       }
   
       public final void doLog(String msg) {
  @@ -956,6 +949,145 @@
   	}
       }
   
  +    // -------------------- Accounting --------------------
  +    // XXX Can be implemented as note !
  +
  +    public static final int ACC_INIT_START=0;
  +    public static final int ACC_INIT_END=0;
  +    
  +    public static final int ACCOUNTS=7;
  +    long accTable[]=new long[ACCOUNTS];
  +
  +    public void setAccount( int pos, long value ) {
  +	accTable[pos]=value;
  +    }
  +
  +    public long getAccount( int pos ) {
  +	return accTable[pos];
  +    }
  +
  +    // -------------------- DEPRECATED --------------------
  +    // XXX host and port are used only to construct a unique
  +    // work-dir for contexts, using them and the path
  +    // Since nobody sets them - I guess we can just drop them
  +    // anyway.
  +    // XXX ask and find if there is any other use!
  +    public static final String DEFAULT_HOSTNAME="localhost";
  +    public static final int DEFAULT_PORT=8080;
  +    String hostname;
  +    int port;
  +
  +    /**
  +     * Sets the port number on which this server listens.
  +     *
  +     * @param port The new port number
  +     * @deprecated 
  +     */
  +    public void setPort(int port) {
  +	/*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
  +	this.port=port;
  +    }
  +
  +    /**
  +     * Gets the port number on which this server listens.
  +     * @deprecated 
  +     */
  +    public int getPort() {
  +	//	/*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
  +	if(port==0) port=DEFAULT_PORT;
  +	return port;
  +    }
  +
  +    /**
  +     * Sets the virtual host name of this server.
  +     *
  +     * @param host The new virtual host name
  +     * @deprecated 
  +     */
  +    public void setHostName( String host) {
  +	/*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
  +	this.hostname=host;
  +    }
  +
  +    /**
  +     * Gets the virtual host name of this server.
  +     * @deprecated 
  +     */
  +    public String getHostName() {
  +	//	/*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
  +	if(hostname==null)
  +	    hostname=DEFAULT_HOSTNAME;
  +	return hostname;
  +    }
  +    // -------------------- DEPRECATED --------------------
  +    
  +    /**
  +     * The set of Contexts associated with this ContextManager,
  +     * keyed by context paths.
  +     * @deprecated - the server shouldn't make any assumptions about
  +     *  the key.
  +     */
  +    private Hashtable contexts = new Hashtable();
  +
  +    /**
  +     * Get the names of all the contexts in this server.
  +     * @deprecated Path is not "unique key".
  +     */
  +    public Enumeration getContextNames() {
  +	/*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
  +        return contexts.keys();
  +    }
  +
  +    /**
  +     * Gets a context by it's name, or <code>null</code> if there is
  +     * no such context.
  +     *
  +     * @param name Name of the requested context
  +     * @deprecated Use an external iterator to find the context that
  +     *  matches your conditions.
  +     *
  +     */
  +    public Context getContext(String name) {
  +	// System.out.println("Using deprecated getContext");
  +	//	/*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
  +	return (Context)contexts.get(name);
  +    }
  +    
  +    /**
  +     * Shut down and removes a context from service.
  +     *
  +     * @param name Name of the Context to be removed
  +     * @deprecated Use removeContext( Context ).
  +     */
  +    public void removeContext(String name) throws TomcatException {
  +	/*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
  +	Context context = (Context)contexts.get(name);
  +	log( "Removing context " + context.toString());
  +
  +	ContextInterceptor cI[]=getContextInterceptors();
  +	for( int i=0; i< cI.length; i++ ) {
  +	    cI[i].removeContext( this, context );
  +	}
  +
  +	if(context != null) {
  +	    shutdownContext( context );
  +	    contexts.remove(name);
  +	}
  +    }
  +
  +    /** @deprecated
  +     */
  +    public void setTomcatHome( String s ) {
  +	//	/*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
  +	setInstallDir( s );
  +    }
  +
  +    /** @deprecated
  +     */
  +    public String getTomcatHome() {
  +	///*DEBUG*/ try {throw new Exception(); } catch(Exception ex) {ex.printStackTrace();}
  +	return getInstallDir();
  +    }
       
       
   }
  
  
  
  1.2       +306 -1    jakarta-tomcat/src/share/org/apache/tomcat/core/ServletWriterFacade.java
  
  Index: ServletWriterFacade.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/ServletWriterFacade.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ServletWriterFacade.java	2000/04/17 21:02:27	1.1
  +++ ServletWriterFacade.java	2000/04/26 18:57:35	1.2
  @@ -72,7 +72,7 @@
    *  that we spend too much time in that area ).
    *
    *  This will also help us control the multi-buffering ( since all writers have
  - *  8k or more of un-recyclable buffers). Recycling is good !
  + *  8k or more of un-recyclable buffers). 
    *
    * @author Costin Manolache [costin@eng.sun.com]
    */
  @@ -137,3 +137,308 @@
   
   }
   
  +// -------------------- From Crimson !
  +
  +//
  +// Delegating to a converter module will always be slower than
  +// direct conversion.  Use a similar approach for any other
  +// readers that need to be particularly fast; only block I/O
  +// speed matters to this package.  For UTF-16, separate readers
  +// for big and little endian streams make a difference, too;
  +// fewer conditionals in the critical path!
  +//
  +abstract class BaseReader extends Reader
  +{
  +    protected InputStream	instream;
  +    protected byte		buffer [];
  +    protected int		start, finish;
  +    
  +    BaseReader (InputStream stream)
  +    {
  +	super (stream);
  +	
  +	instream = stream;
  +	buffer = new byte [8192];
  +    }
  +
  +    public boolean ready () throws IOException
  +    {
  +	return instream == null
  +	    || (finish - start) > 0
  +	    ||  instream.available () != 0;
  +	}
  +
  +    // caller shouldn't read again
  +    public void close () throws IOException
  +    {
  +	if (instream != null) {
  +	    instream.close ();
  +	    start = finish = 0;
  +	    buffer = null;
  +	    instream = null;
  +	}
  +    }
  +}
  +
  +//
  +// We want this reader, to make the default encoding be as fast
  +// as we can make it.  JDK's "UTF8" (not "UTF-8" till JDK 1.2)
  +// InputStreamReader works, but 20+% slower speed isn't OK for
  +// the default/primary encoding.
  +//
  +final class Utf8Reader extends BaseReader
  +{
  +    // 2nd half of UTF-8 surrogate pair
  +    private char		nextChar;
  +    
  +    Utf8Reader (InputStream stream)
  +    {
  +	super (stream);
  +    }
  +    
  +    public int read (char buf [], int offset, int len) throws IOException
  +    {
  +	int i = 0, c = 0;
  +	
  +	if (len <= 0)
  +	    return 0;
  +	
  +	// avoid many runtime bounds checks ... a good optimizer
  +	// (static or JIT) will now remove checks from the loop.
  +	if ((offset + len) > buf.length || offset < 0)
  +	    throw new ArrayIndexOutOfBoundsException ();
  +	
  +	// Consume remaining half of any surrogate pair immediately
  +	if (nextChar != 0) {
  +	    buf [offset + i++] = nextChar;
  +	    nextChar = 0;
  +	}
  +	
  +	while (i < len) {
  +	    // stop or read data if needed
  +	    if (finish <= start) {
  +		if (instream == null) {
  +		    c = -1;
  +		    break;
  +		}
  +		start = 0;
  +		finish = instream.read (buffer, 0, buffer.length);
  +		if (finish <= 0) {
  +		    this.close ();
  +		    c = -1;
  +		    break;
  +		}
  +	    }
  +	    
  +	    //
  +	    // RFC 2279 describes UTF-8; there are six encodings.
  +	    // Each encoding takes a fixed number of characters
  +	    // (1-6 bytes) and is flagged by a bit pattern in the
  +	    // first byte.  The five and six byte-per-character
  +	    // encodings address characters which are disallowed
  +	    // in XML documents, as do some four byte ones.
  +	    // 
  +	    
  +	    //
  +	    // Single byte == ASCII.  Common; optimize.
  +	    //
  +	    c = buffer [start] & 0x0ff;
  +	    if ((c & 0x80) == 0x00) {
  +		// 0x0000 <= c <= 0x007f
  +		start++;
  +		buf [offset + i++] = (char) c;
  +		continue;
  +	    }
  +		
  +	    //
  +	    // Multibyte chars -- check offsets optimistically,
  +	    // ditto the "10xx xxxx" format for subsequent bytes
  +	    //
  +	    int		off = start;
  +		
  +	    try {
  +		// 2 bytes
  +		if ((buffer [off] & 0x0E0) == 0x0C0) {
  +		    c  = (buffer [off++] & 0x1f) << 6;
  +		    c +=  buffer [off++] & 0x3f;
  +
  +		    // 0x0080 <= c <= 0x07ff
  +
  +		    // 3 bytes
  +		} else if ((buffer [off] & 0x0F0) == 0x0E0) {
  +		    c  = (buffer [off++] & 0x0f) << 12;
  +		    c += (buffer [off++] & 0x3f) << 6;
  +		    c +=  buffer [off++] & 0x3f;
  +
  +		    // 0x0800 <= c <= 0xffff
  +
  +		    // 4 bytes
  +		} else if ((buffer [off] & 0x0f8) == 0x0F0) {
  +		    c  = (buffer [off++] & 0x07) << 18;
  +		    c += (buffer [off++] & 0x3f) << 12;
  +		    c += (buffer [off++] & 0x3f) << 6;
  +		    c +=  buffer [off++] & 0x3f;
  +
  +		    // 0x0001 0000  <= c  <= 0x001f ffff
  +
  +		    // Unicode supports c <= 0x0010 ffff ...
  +		    if (c > 0x0010ffff)
  +			throw new CharConversionException (
  +							   "UTF-8 encoding of character 0x00"
  +							   + Integer.toHexString (c)
  +							   + " can't be converted to Unicode."
  +							   );
  +
  +		    else if (c > 0xffff) {
  +			// Convert UCS-4 char to surrogate pair (UTF-16)
  +			c -= 0x10000;
  +			nextChar = (char) (0xDC00 + (c & 0x03ff));
  +			c = 0xD800 + (c >> 10);
  +		    }
  +		    // 5 and 6 byte versions are XML WF errors, but
  +		    // typically come from mislabeled encodings
  +		} else
  +		    throw new CharConversionException (
  +						       "Unconvertible UTF-8 character"
  +						       + " beginning with 0x"
  +						       + Integer.toHexString (
  +									      buffer [start] & 0xff)
  +						       );
  +
  +	    } catch (ArrayIndexOutOfBoundsException e) {
  +		// off > length && length >= buffer.length
  +		c = 0;
  +	    }
  +
  +	    //
  +	    // if the buffer held only a partial character,
  +	    // compact it and try to read the rest of the
  +	    // character.  worst case involves three
  +	    // single-byte reads -- quite rare.
  +	    //
  +	    if (off > finish) {
  +		System.arraycopy (buffer, start,
  +				  buffer, 0, finish - start);
  +		finish -= start;
  +		start = 0;
  +		off = instream.read (buffer, finish,
  +				     buffer.length - finish);
  +		if (off < 0) {
  +		    this.close ();
  +		    throw new CharConversionException (
  +						       "Partial UTF-8 char");
  +		}
  +		finish += off;
  +		continue;
  +	    }
  +
  +	    //
  +	    // check the format of the non-initial bytes
  +	    //
  +	    for (start++; start < off; start++) {
  +		if ((buffer [start] & 0xC0) != 0x80) {
  +		    this.close ();
  +		    throw new CharConversionException (
  +						       "Malformed UTF-8 char -- "
  +						       + "is an XML encoding declaration missing?"
  +						       );
  +		}
  +	    }
  +
  +	    //
  +	    // If this needed a surrogate pair, consume ASAP
  +	    //
  +	    buf [offset + i++] = (char) c;
  +	    if (nextChar != 0 && i < len) {
  +		buf [offset + i++] = nextChar;
  +		nextChar = 0;
  +	    }
  +	}
  +	if (i > 0)
  +	    return i;
  +	return (c == -1) ? -1 : 0;
  +    }
  +}
  +
  +//
  +// We want ASCII and ISO-8859 Readers since they're the most common
  +// encodings in the US and Europe, and we don't want performance
  +// regressions for them.  They're also easy to implement efficiently,
  +// since they're bitmask subsets of UNICODE.
  +//
  +// XXX haven't benchmarked these readers vs what we get out of JDK.
  +//
  +final class AsciiReader extends BaseReader
  +{
  +    AsciiReader (InputStream in) { super (in); }
  +
  +    public int read (char buf [], int offset, int len) throws IOException
  +    {
  +	int		i, c;
  +
  +	if (instream == null)
  +	    return -1;
  +
  +	// avoid many runtime bounds checks ... a good optimizer
  +	// (static or JIT) will now remove checks from the loop.
  +	if ((offset + len) > buf.length || offset < 0)
  +	    throw new ArrayIndexOutOfBoundsException ();
  +
  +	for (i = 0; i < len; i++) {
  +	    if (start >= finish) {
  +		start = 0;
  +		finish = instream.read (buffer, 0, buffer.length);
  +		if (finish <= 0) {
  +		    if (finish <= 0)
  +			this.close ();
  +		    break;
  +		}
  +	    }
  +	    c = buffer [start++];
  +	    if ((c & 0x80) != 0)
  +		throw new CharConversionException (
  +						   "Illegal ASCII character, 0x"
  +						   + Integer.toHexString (c & 0xff)
  +						   );
  +	    buf [offset + i] = (char) c;
  +	}
  +	if (i == 0 && finish <= 0)
  +	    return -1;
  +	return i;
  +    }
  +}
  +
  +final class Iso8859_1Reader extends BaseReader
  +{
  +    Iso8859_1Reader (InputStream in) { super (in); }
  +    
  +    public int read (char buf [], int offset, int len) throws IOException
  +    {
  +	int		i;
  +	
  +	if (instream == null)
  +	    return -1;
  +	
  +	// avoid many runtime bounds checks ... a good optimizer
  +	// (static or JIT) will now remove checks from the loop.
  +	if ((offset + len) > buf.length || offset < 0)
  +	    throw new ArrayIndexOutOfBoundsException ();
  +	    
  +	for (i = 0; i < len; i++) {
  +	    if (start >= finish) {
  +		start = 0;
  +		finish = instream.read (buffer, 0, buffer.length);
  +		if (finish <= 0) {
  +		    if (finish <= 0)
  +			this.close ();
  +		    break;
  +		}
  +	    }
  +	    buf [offset + i] = (char) (0x0ff & buffer [start++]);
  +	}
  +	if (i == 0 && finish <= 0)
  +	    return -1;
  +	return i;
  +    }
  +}
  + 
  
  
  
  1.3       +77 -43    jakarta-tomcat/src/share/org/apache/tomcat/core/package.html
  
  Index: package.html
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/package.html,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- package.html	2000/04/17 21:02:27	1.2
  +++ package.html	2000/04/26 18:57:35	1.3
  @@ -1,45 +1,79 @@
   <html>
  -<head>
  -<title>The servlet engine implementation</title>
  -<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
  -</head>
  -
  -<body bgcolor="#FFFFFF">
  -<h2>Components</h2>
  -
  -<dl>
  -
  -<dt>Facades
  -<dd>For every object exposed in the servlet specification tomcat use a 
  -Facade. It have few important advantages:
  -<ul>
  -<li>Security - the user should have no way to access the real implementation
  -objects, except one controled way ( HttpRequestFacade.getRealRequest() ).
  -When policy-based security will be added that will allow the user to declare a 
  -"trusted" servlet, and no other servlet will be able to access internal objects.
  -<li>Future evolution of Servlet API - the API changed and will probably change, 
  -by adding new methods and new semantics. Facades makes very simple to make sure
  -we respect the spec, without having to change the core too much.
  -That may allow supporting multiple servlet API versions ( but it's not a goal -
  -each context has it's own classloader and it may be possible to load a different
  -JDK and set of facades ).
  -<li>Decouple the API and the web server. Tomcat.core allows integration with
  -web servers, and it's  modeled after APIs like Apache's, ISAPI and NSAPI.
  -</ul>
  -
  -</dd>
  -
  -<dt>Request,Response
  -<dd>
  -
  -<dt>Context, Container, ServletWrapper
  -<dd>
  -
  -<dt>ContextManager
  -<dd>
  -
  -</dl>
  -
  -
  -</body>
  +  <head>
  +    <title>The servlet engine implementation</title>
  +    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  +  </head>
  +  
  +  <body bgcolor="#FFFFFF">
  +    <h2>Components</h2>
  +
  +    <dl>
  +
  +      <dt>ContextManager</dt>
  +      <dd>
  +    	<ul>
  +      	  <li>maintains a list of adapters</li>
  +      	  <li>is the entry point for adapters and any component that can generate 
  +	    a Request/Response and wants to use servlets.</li>
  +      	  <li>is the control point that handles callback calls and notifications.</li>
  +      	  <li>maintains a list of webapps that are configured in the system.</li>
  +    	</ul>
  +      </dd>
  +      
  +      <dt>Request,Response</dt>
  +      <dd>Internal representations, containing various attributes associated with
  +    	the request during processing.
  +      </dd>
  +
  +      <dt>Interceptors</dt>
  +      <dd> 
  +	Request processing involves several stages - parsing, matching using url
  +	patterns, authentication and authorization, calling the handler and various
  + 	callbacks before, after and during the handler. 
  +	An interceptor defines callbacks ( or hooks ) for one or several actions
  +	and is able to alter that function.
  +	In fact, most of the processing logic is implemented as interceptor 
  +	callbacks, in an event-based system.
  +      </dd>
  +
  +      <dt>Context, Container</dt>
  +      <dd>
  +    	Container represents a group of URLs sharing common properties like handler,
  +    	security, normal properties. Context is a particular case that is associated
  +    	with a web application and keeps all the properties defined in the spec. 
  +    	We are slowly moving toward use of Container, it can also represent group
  +    	of contexts, virtual hosts, etc. 
  +    	It is not a good idea to introduce sub-classes for every type of group, 
  +    	the isA property is not allways clearly cut. 
  +      </dd>
  +      
  +      <dt>ServletWrapper</dt>
  +      <dd>
  +    	A normal servlet or JSP, plus all the surounding logic.
  +      </dd>
  +      
  +      
  +      <dt>Facades</dt>
  +      <dd>For every object exposed in the servlet specification tomcat use a 
  +    	Facade. It have few important advantages:
  +    	<ul>
  +	  <li>Security - the user should have no way to access the real implementation
  +	    objects, except one controled way ( HttpRequestFacade.getRealRequest() ).
  +	    When policy-based security will be added that will allow the user to declare a 
  +	    "trusted" servlet, and no other servlet will be able to access internal objects.</li>
  +	  <li>Future evolution of Servlet API - the API changed and will probably change, 
  +	    by adding new methods and new semantics. Facades makes very simple to make sure
  +	    we respect the spec, without having to change the core too much.
  +	    That may allow supporting multiple servlet API versions ( but it's not a goal -
  +	    each context has it's own classloader and it may be possible to load a different
  +	    JDK and set of facades ).</li>
  +	  <li>Decouple the API and the web server. Tomcat.core allows integration with
  +	    web servers, and it's  modeled after APIs like Apache's, ISAPI and NSAPI.</li>
  +    	</ul>
  +    	
  +      </dd>
  +    </dl>
  +    
  +    
  +  </body>
   </html>
  
  
  
  1.3       +3 -3      jakarta-tomcat/src/share/org/apache/tomcat/request/SimpleMapper1.java
  
  Index: SimpleMapper1.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/request/SimpleMapper1.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- SimpleMapper1.java	2000/04/25 17:54:19	1.2
  +++ SimpleMapper1.java	2000/04/26 18:57:36	1.3
  @@ -172,11 +172,11 @@
   	// beeing late
   	
   	// Add all context that are set in CM
  -	Enumeration enum=cm.getContextNames();
  +	Enumeration enum=cm.getContexts();
   	while( enum.hasMoreElements() ) {
  -	    String name=(String) enum.nextElement();
  +	    
   	    try {
  -		Context ctx=cm.getContext( name );
  +		Context ctx=(Context)enum.nextElement();
   		addContext( cm, ctx );
   	    } catch (TomcatException ex ) {
   		ex.printStackTrace();
  
  
  
  1.23      +1 -1      jakarta-tomcat/src/share/org/apache/tomcat/startup/Tomcat.java
  
  Index: Tomcat.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/startup/Tomcat.java,v
  retrieving revision 1.22
  retrieving revision 1.23
  diff -u -r1.22 -r1.23
  --- Tomcat.java	2000/04/25 17:54:29	1.22
  +++ Tomcat.java	2000/04/26 18:57:36	1.23
  @@ -155,7 +155,7 @@
   	    tchome = ".";	// Assume current working directory
   	}
   	// Home will be identical to tomcat home if default config is used.
  -	cm.setTomcatHome(tchome);
  +	cm.setInstallDir(tchome);
   	return (new File(tchome, DEFAULT_CONFIG));
   
       }
  
  
  
  1.6       +17 -3     jakarta-tomcat/src/share/org/apache/tomcat/util/FileUtil.java
  
  Index: FileUtil.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/FileUtil.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- FileUtil.java	2000/03/16 20:43:26	1.5
  +++ FileUtil.java	2000/04/26 18:57:36	1.6
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/FileUtil.java,v 1.5 2000/03/16 20:43:26 costin Exp $
  - * $Revision: 1.5 $
  - * $Date: 2000/03/16 20:43:26 $
  + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/FileUtil.java,v 1.6 2000/04/26 18:57:36 costin Exp $
  + * $Revision: 1.6 $
  + * $Date: 2000/04/26 18:57:36 $
    *
    * ====================================================================
    *
  @@ -65,6 +65,7 @@
   package org.apache.tomcat.util;
   
   import java.io.File;
  +import java.io.IOException;
   
   /*
    * FileUtil contains utils for dealing with Files. Some of these are 
  @@ -202,5 +203,18 @@
           }
   	return path;
       }
  +
  +    // Used in few places.
  +    public static String getCanonicalPath(String name ) {
  +	if( name==null ) return null;
  +        File f = new File(name);
  +        try {
  +            return  f.getCanonicalPath();
  +        } catch (IOException ioe) {
  +	    ioe.printStackTrace();
  +	    return name; // oh well, we tried...
  +        }
  +    }
  +    
   
   }