You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by cr...@locus.apache.org on 2000/05/09 04:35:52 UTC

cvs commit: jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader FileClassLoader.java Reloader.java LocalStrings.properties StandardLoader.java

craigmcc    00/05/08 19:35:50

  Modified:    proposals/catalina/src/conf server.xml
               proposals/catalina/src/share/org/apache/tomcat/loader
                        LocalStrings.properties StandardLoader.java
  Added:       proposals/catalina/src/share/org/apache/tomcat/loader
                        FileClassLoader.java Reloader.java
  Log:
  Begin factoring of the Loader implementation so that the actual ClassLoader
  used can be pluggable, with optional support for auto-reloading.  This code
  is not yet complete and not yet in use.
  
  Revision  Changes    Path
  1.20      +2 -0      jakarta-tomcat/proposals/catalina/src/conf/server.xml
  
  Index: server.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/conf/server.xml,v
  retrieving revision 1.19
  retrieving revision 1.20
  diff -u -r1.19 -r1.20
  --- server.xml	2000/05/06 00:20:33	1.19
  +++ server.xml	2000/05/09 02:35:42	1.20
  @@ -51,6 +51,8 @@
         <Context path="/examples" docBase="webapps/examples" debug="0">
           <Listener className="org.apache.tomcat.startup.ContextConfig"
   	          debug="0"/>
  +	<Loader  className="org.apache.tomcat.loader.StandardLoader"
  +	         debug="3"/>
           <Manager className="org.apache.tomcat.session.StandardManager"
   	         checkInterval="15" maxActiveSessions="200"
                    pathname="sessions/localhost_examples.sessions"/>
  
  
  
  1.2       +10 -0     jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/LocalStrings.properties
  
  Index: LocalStrings.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/LocalStrings.properties,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- LocalStrings.properties	2000/04/26 02:14:53	1.1
  +++ LocalStrings.properties	2000/05/09 02:35:47	1.2
  @@ -1,5 +1,15 @@
  +fileClassLoader.canRead=Repository {0} cannot be read
  +fileClassLoader.exists=Repository {0} does not exist
  +fileClassLoader.jarFile=Cannot read JAR file {0}
  +standardLoader.addRepository=Adding repository {0}
   standardLoader.alreadyStarted=Loader has already been started
   standardLoader.exists=Repository {0} does not exist
  +standardLoader.includes= Include path {0}
   standardLoader.notStarted=Loader has not yet been started
   standardLoader.read=Repository {0} cannot be read
  +standardLoader.reload=Reloading this Loader
  +standardLoader.reloadError=Error reloading this loader
  +standardLoader.removeRepository=Removing repository {0}
  +standardLoader.starting=Starting this Loader
  +standardLoader.stopping=Stopping this Loader
   standardLoader.type=Repository {0} is not a supported file type
  
  
  
  1.3       +82 -6     jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/StandardLoader.java
  
  Index: StandardLoader.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/StandardLoader.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- StandardLoader.java	2000/04/29 05:59:55	1.2
  +++ StandardLoader.java	2000/05/09 02:35:47	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/StandardLoader.java,v 1.2 2000/04/29 05:59:55 craigmcc Exp $
  - * $Revision: 1.2 $
  - * $Date: 2000/04/29 05:59:55 $
  + * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/StandardLoader.java,v 1.3 2000/05/09 02:35:47 craigmcc Exp $
  + * $Revision: 1.3 $
  + * $Date: 2000/05/09 02:35:47 $
    *
    * ====================================================================
    *
  @@ -79,6 +79,7 @@
   import org.apache.tomcat.LifecycleException;
   import org.apache.tomcat.LifecycleListener;
   import org.apache.tomcat.Loader;
  +import org.apache.tomcat.Logger;
   import org.apache.tomcat.loader.AdaptiveClassLoader;
   import org.apache.tomcat.util.LifecycleSupport;
   import org.apache.tomcat.util.StringManager;
  @@ -97,7 +98,7 @@
    * is not present, the system class loader will be used instead.
    *
    * @author Craig R. McClanahan
  - * @version $Revision: 1.2 $ $Date: 2000/04/29 05:59:55 $
  + * @version $Revision: 1.3 $ $Date: 2000/05/09 02:35:47 $
    */
   
   public final class StandardLoader
  @@ -219,7 +220,10 @@
        */
       public void setDebug(int debug) {
   
  +        int oldDebug = this.debug;
   	this.debug = debug;
  +	support.firePropertyChange("debug", new Integer(oldDebug),
  +				   new Integer(this.debug));
   
       }
   
  @@ -258,8 +262,13 @@
        */
       public void addRepository(String repository) {
   
  -	if (!repositories.contains(repository))
  +        if (debug >= 1)
  +	    log(sm.getString("standardLoader.addRepository", repository));
  +	if (!repositories.contains(repository)) {
   	    repositories.addElement(repository);
  +	    if (started)
  +	        reload();
  +	}
   
       }
   
  @@ -303,10 +312,14 @@
        */
       public void reload() {
   
  +        if (debug >= 1)
  +	    log(sm.getString("standardLoader.reload"));
  +
   	try {
   	    stop();
   	    start();
   	} catch (Throwable t) {
  +	    log(sm.getString("standardLoader.reloadError"), t);
   	    throw new IllegalStateException("reload: " + t);
   	}
   
  @@ -332,7 +345,13 @@
        */
       public void removeRepository(String repository) {
   
  -	repositories.removeElement(repository);
  +        if (debug >= 1)
  +	    log(sm.getString("standardLoader.removeRepository", repository));
  +	if (repositories.contains(repository)) {
  +	    repositories.removeElement(repository);
  +	    if (started)
  +	        reload();
  +	}
   
       }
   
  @@ -375,6 +394,8 @@
   	if (started)
   	    throw new LifecycleException
   		(sm.getString("standardLoader.alreadyStarted"));
  +	if (debug >= 1)
  +	    log(sm.getString("standardLoader.starting"));
   	lifecycle.fireLifecycleEvent(START_EVENT, null);
   	started = true;
   
  @@ -399,6 +420,8 @@
   	    else if (!checkType(file))
   		throw new LifecycleException
   		    (sm.getString("standardLoader.type", path));
  +	    if (debug >= 1)
  +	        log(sm.getString("standardLoader.includes", path));
   	}
   
   	// Construct a new class loader instance for our use
  @@ -422,6 +445,8 @@
   	if (!started)
   	    throw new LifecycleException
   		(sm.getString("standardLoader.notStarted"));
  +	if (debug >= 1)
  +	    log(sm.getString("standardLoader.stopping"));
   	lifecycle.fireLifecycleEvent(STOP_EVENT, null);
   	started = false;
   
  @@ -463,6 +488,57 @@
   	    }
   	}
   	return (isArchive);
  +
  +    }
  +
  +
  +    /**
  +     * Log a message on the Logger associated with our Container (if any)
  +     *
  +     * @param message Message to be logged
  +     */
  +    private void log(String message) {
  +
  +	Logger logger = null;
  +	if (container != null)
  +	    logger = container.getLogger();
  +	if (logger != null)
  +	    logger.log("StandardLoader[" + container.getName() + "]: "
  +		       + message);
  +	else {
  +	    String containerName = null;
  +	    if (container != null)
  +		containerName = container.getName();
  +	    System.out.println("StandardLoader[" + containerName
  +			       + "]: " + message);
  +	}
  +
  +    }
  +
  +
  +    /**
  +     * Log a message on the Logger associated with our Container (if any)
  +     *
  +     * @param message Message to be logged
  +     * @param throwable Associated exception
  +     */
  +    private void log(String message, Throwable throwable) {
  +
  +	Logger logger = null;
  +	if (container != null)
  +	    logger = container.getLogger();
  +	if (logger != null)
  +	    logger.log("StandardLoader[" + container.getName() + "] "
  +		       + message, throwable);
  +	else {
  +	    String containerName = null;
  +	    if (container != null)
  +		containerName = container.getName();
  +	    System.out.println("StandardLoader[" + containerName
  +			       + "]: " + message);
  +	    System.out.println("" + throwable);
  +	    throwable.printStackTrace(System.out);
  +	}
   
       }
   
  
  
  
  1.1                  jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/FileClassLoader.java
  
  Index: FileClassLoader.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/FileClassLoader.java,v 1.1 2000/05/09 02:35:46 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/05/09 02:35:46 $
   *
   * ====================================================================
   *
   * 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.loader;
  
  
  import java.io.BufferedInputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.InputStream;
  import java.io.IOException;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.Vector;
  import java.util.jar.JarEntry;
  import java.util.jar.JarException;
  import java.util.jar.JarFile;
  import org.apache.tomcat.util.StringManager;
  
  
  /**
   * Implementation of <b>java.lang.ClassLoader</b> that knows how to load
   * classes from disk directories and JAR files.  It also implements
   * the <code>Reloader</code> interface, to provide automatic reloading
   * support to <code>StandardLoader</code>.
   * <p>
   * This code was partially based on the <code>AdaptiveClassLoader</code>
   * module originally copied from Apache JServ, and used in Tomcat 3.x.
   *
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2000/05/09 02:35:46 $
   */
  
  public final class FileClassLoader
      extends ClassLoader
      implements Reloader{
  
  
      // ---------------------------------------------------------- Constructors
  
  
      /**
       * Construct a new ClassLoader instance with no defined repositories.
       */
      public FileClassLoader() {
  
          super();
  
      }
  
  
      /**
       * Construct a new ClassLoader instance with the specified initial list of
       * repositories.  Each element of this list must be a String that contains
       * a pathname to a directory, a JAR file, or a ZIP file.
       *
       * @param repositories Initial list of repositories
       *
       * @exception IllegalArgumentException if one of the specified repositories
       *  does not exist, or is not of the appropriate type
       */
      public FileClassLoader(Enumeration repositories) {
  
          super();
  	while (repositories.hasMoreElements())
  	    addRepository((String) repositories.nextElement());
  
      }
  
  
      // ---------------------------------------------------- Instance Variables
  
  
      /**
       * The set of CacheEntries for classes loaded by this class loader,
       * keyed by the fully qualified class name.
       */
      private Hashtable cache = new Hashtable();
  
  
      /**
       * The debugging detail level of this component.
       */
      private int debug = 0;
  
  
      /**
       * The set of File entries for repositories that are directories,
       * keyed by repository name.
       */
      private Hashtable directories = new Hashtable();
  
  
      /**
       * The set of File entries for repositories that are JARs,
       * keyed by repository name.
       */
      private Hashtable jarFiles = new Hashtable();
  
  
      /**
       * The set of repositories associated with this class loader.  The order
       * of the elements in this Vector controls the order in which repositories
       * are examined when new classes or resources are requested.
       */
      private Vector repositories = new Vector();
  
  
      /**
       * The string manager for this package.
       */
      private static final StringManager sm =
          StringManager.getManager(Constants.Package);
  
  
      // ------------------------------------------------------------ Properties
  
  
      /**
       * Return the debugging detail level of this component.
       */
      public int getDebug() {
  
          return (this.debug);
  
      }
  
  
      /**
       * Set the debugging detail level of this component.
       *
       * @param debug The new debugging detail level
       */
      public void setDebug(int debug) {
  
          this.debug = debug;
  
      }
  
  
      // ------------------------------------------------------- Reloader Methods
  
  
      /**
       * Add a new repository to the set of places this ClassLoader can look for
       * classes to be loaded.
       *
       * @param repository Name of a source of classes to be loaded, such as a
       *  directory pathname, a JAR file pathname, or a ZIP file pathname
       *
       * @exception IllegalArgumentException if the specified repository is
       *  invalid or does not exist
       */
      public void addRepository(String repository) {
  
          if (repositories.contains(repository))
  	    return;
  
  	// Validate the existence, readability, and type of this repository
  	File file = new File(repository);
  	if (!file.exists())
  	    throw new IllegalArgumentException
  	        (sm.getString("fileClassLoader.exists", repository));
  	if (!file.canRead())
  	    throw new IllegalArgumentException
  	        (sm.getString("fileClassLoader.canRead", repository));
  	if (file.isDirectory()) {
  	    directories.put(repository, file);
  	} else {
  	    JarFile jarFile = null;
  	    try {
  	        jarFile = new JarFile(file);
  		Enumeration entries = jarFile.entries();
  		while (entries.hasMoreElements()) {
  		    JarEntry entry = (JarEntry) entries.nextElement();
  		    InputStream is = jarFile.getInputStream(entry);
  		    is.close();
  		    break;
  		}
  		jarFile.close();
  	    } catch (Throwable t) {
  	        throw new IllegalArgumentException
  		    (sm.getString("fileClassLoader.jarFile", repository));
  	    }
  	    jarFiles.put(repository, file);
  	}
  
  	// Add this repository to the set we know about
  	repositories.addElement(repository);
  
      }
  
  
      /**
       * Return an enumeration of the current repositories for this class
       * loader.  If there are no repositories, an empty Enumeration is
       * returned.
       */
      public Enumeration findRepositories() {
  
          return (repositories.elements());
  
      }
  
  
      /**
       * Have one or more classes or resources been modified so that a reload
       * is appropriate?
       */
      public boolean modified() {
  
          return (false);  // FIXME - modified()
  
      }
  
  
      /**
       * Create a new ClassLoader instance based on our current repositories.
       */
      public Reloader reload() {
  
          return (null);  // FIXME - reload()
  
      }
  
  
      /**
       * Remove the specified repository from the set of places this ClassLoader
       * can look for classes to be loaded.  Any classes already loaded from this
       * repository will remain, but no future loads from here will take place.
       *
       * @param repository Name of the repository to remove
       */
      public void removeRepository(String repository) {
  
          if (repositories.contains(repository))
  	    repositories.removeElement(repository);
  
      }
  
  
      // --------------------------------------------------- ClassLoader Methods
  
  
      /**
       * Find a resource with a given name.  The return is a URL to the resource.
       * Doing a <code>getContent()</code> on the URL may return an Image, an
       * AudioClip, or an InputStream.
       *
       * @param name The name of the resource, to be used as is
       */
      public URL getResource(String name) {
  
  	// Search for this resource in all of our repositories
          Enumeration repositories = this.repositories.elements();
          while (repositories.hasMoreElements()) {
  	    String repository = (String) repositories.nextElement();
  	    File directory = (File) directories.get(repository);
  	    if (directory != null) {
  	        URL theURL =
  		  loadResourceFromDirectory(directory, name);
  		if (theURL != null)
  		    return (theURL);
  	    }
  	    File jarFile = (File) jarFiles.get(repository);
  	    if (jarFile != null) {
  	        URL theURL =
  		  loadResourceFromJarFile(jarFile, name);
  		if (theURL != null)
  		    return (theURL);
  	    }
  	}
  
          // Load this resource from the system class path
          return (getSystemResource(name));
  
      }
  
  
      /**
       * Get an InputStream on a given resource.  Will return <code>null</code>
       * if no resource with this name is found.
       *
       * @param name The name of the resource
       */
      public InputStream getResourceAsStream(String name) {
  
  	// Search for this resource in all of our repositories
          Enumeration repositories = this.repositories.elements();
          while (repositories.hasMoreElements()) {
  	    String repository = (String) repositories.nextElement();
  	    File directory = (File) directories.get(repository);
  	    if (directory != null) {
  	        InputStream theStream =
  		  loadStreamFromDirectory(directory, name);
  		if (theStream != null)
  		    return (theStream);
  	    }
  	    File jarFile = (File) jarFiles.get(repository);
  	    if (jarFile != null) {
  	        InputStream theStream =
  		  loadStreamFromJarFile(jarFile, name);
  		if (theStream != null)
  		    return (theStream);
  	    }
  	}
  
  	// Load this resource from the system class path
  	return (getSystemResourceAsStream(name));
  
      }
  
  
      /**
       * Requests the class loader to load and resolve a class with the
       * specified name.  The <code>loadClass</code> method is called by the
       * Java Virtual Machine when a class loaded by a class loader first
       * references another class.  Every subclass of <code>ClassLoader</code>
       * must define this method.
       *
       * @param name Name of the desired Class
       * @param resolve <code>true</code> if this Class needs to be resolved.
       *
       * @return The resulting Class, or <code>null</code> if it was not found
       *
       * @exception ClassNotFoundException if the class loader cannot find
       *  a definition for this class
       */
      public Class loadClass(String name, boolean resolve)
          throws ClassNotFoundException {
  
  	// Has this class already been loaded?
  	CacheEntry entry = (CacheEntry) cache.get(name);
  	if (entry != null) {
  	    Class theClass = entry.loadedClass;
  	    if (resolve)
  	        resolveClass(theClass);
  	    return (theClass);
  	}
  
  	// Search for this class in all of our repositories
  	if (allowed(name)) {
  	    Enumeration repositories = this.repositories.elements();
  	    while (repositories.hasMoreElements()) {
  	        String repository = (String) repositories.nextElement();
  	        File directory = (File) directories.get(repository);
  	        if (directory != null) {
  	            Class theClass =
  		      loadClassFromDirectory(directory, name, resolve);
  		    if (theClass != null)
  		        return (theClass);
  	        }
  		File jarFile = (File) jarFiles.get(repository);
  		if (jarFile != null) {
  		    Class theClass =
  		      loadClassFromJarFile(jarFile, name, resolve);
  		    if (theClass != null)
  		        return (theClass);
  		}
  	    }
  	}
  
  	// Load this class from the system class path
  	Class theClass = loadClassFromSystem(name, resolve);
  	if (theClass != null)
  	    return (theClass);
  	else
  	    throw new ClassNotFoundException(name);
  
      }
  
  
      // ------------------------------------------------------- Private Methods
  
  
  
      /**
       * Should we allow this class to be loaded from someplace other than
       * the system class path?
       *
       * @param name Name of the class we are considering
       */
      private boolean allowed(String name) {
  
          if (name.startsWith("java."))
  	    return (false);
  	else
  	    return (true);
  
      }
  
  
      /**
       * Load the specified class from the specified directory, if it exists;
       * otherwise return <code>null</code>.  If found, a cache entry will be
       * automatically added before returning.
       *
       * @param directory File for the directory to be searched
       * @param name Name of the desired class
       * @param resolve <code>true</code> if this Class needs to be resolved
       */
      private Class loadClassFromDirectory(File directory, String name,
  					 boolean resolve) {
  
          // Translate the class name to a filename
          String filename =
  	  name.replace('.', File.separatorChar) + ".class";
  
  	// Validate the existence and readability of this class file
  	File classFile = new File(directory, filename);
  	if (!classFile.exists() || !classFile.canRead())
  	    return (null);
  
  	// Load the bytes for this class, and define it
  	Class theClass = null;
  	byte buffer[] = new byte[(int) classFile.length()];
  	InputStream is = null;
  	try {
  	    is = new BufferedInputStream(new FileInputStream(classFile));
  	    is.read(buffer);
  	    is.close();
  	    is = null;
  	    theClass = defineClass(name, buffer, 0, buffer.length);
  	    if (resolve)
  	        resolveClass(theClass);
  	    cache.put(name,
  		      new CacheEntry(theClass, classFile,
  				     classFile.lastModified()));
  	    return (theClass);
  	} catch (Throwable t) {
  	    if (is != null) {
  	        try {
  		    is.close();
  		} catch (Throwable u) {
  		    ;
  		}
  	    }
  	    return (null);
  	}
  
      }
  
  
      /**
       * Load the specified class from the specified JAR file, if it exists;
       * otherwise return <code>null</code>.  If found, a cache entry will be
       * automatically added before returning.
       *
       * @param jar JAR file to be searched
       * @param name Name of the desired class
       * @param resolve <code>true</code> if this Class needs to be resolved
       */
      private Class loadClassFromJarFile(File jar, String name,
  				       boolean resolve) {
  
          // Translate the class name to a filename
          String filename =
  	  name.replace('.', '/') + ".class";
  
  	// Load the bytes for this class, and define it
  	JarFile jarFile = null;
  	JarEntry jarEntry = null;
  	Class theClass = null;
  	byte buffer[] = new byte[(int) jarEntry.getSize()];
  	InputStream is = null;
  	try {
  	    jarFile = new JarFile(jar);
  	    jarEntry = jarFile.getJarEntry(filename);
  	    if (jarEntry == null) {
  	        jarFile.close();
  		return (null);
  	    }
  	    is = new BufferedInputStream(jarFile.getInputStream(jarEntry));
  	    is.read(buffer);
  	    is.close();
  	    theClass = defineClass(name, buffer, 0, buffer.length);
  	    if (resolve)
  	        resolveClass(theClass);
  	    cache.put(name,
  		      new CacheEntry(theClass, jar, jar.lastModified()));
  	    return (theClass);
  	} catch (Throwable t) {
  	    if (is != null) {
  	        try {
  		    is.close();
  		} catch (Throwable u) {
  		    ;
  		}
  	    }
  	    if (jarFile != null) {
  	        try {
  		    jarFile.close();
  		} catch (Throwable u) {
  		    ;
  		}
  	    }
  	    return (null);
  	}
  
      }
  
  
      /**
       * Load the specified class from the system class path, if it exists;
       * otherwise return <code>null</code>.  If found, a cache entry will be
       * automatically added before returning.
       *
       * @param name Name of the desired class
       * @param resolve <code>true</code> if this Class needs to be resolved
       */
      private Class loadClassFromSystem(String name, boolean resolve) {
  
          Class theClass = null;
  	try {
  	    theClass = findSystemClass(name);
  	} catch (ClassNotFoundException e) {
  	    return (null);
  	}
  	if (theClass != null) {
  	    if (resolve)
  	        resolveClass(theClass);
  	    cache.put(name,
  		      new CacheEntry(theClass, null, Long.MAX_VALUE));
  	    return (theClass);
  	} else
  	    return (null);
  
      }
  
  
      /**
       * Load the specified resource from the specified directory, if it exists;
       * otherwise return <code>null</code>.
       *
       * @param directory File for the directory to be searched
       * @param name Name of the desired resource
       */
      private URL loadResourceFromDirectory(File directory, String name) {
  
          // Translate the resource name to a filename
          String filename =
  	  name.replace('.', File.separatorChar) + ".class";
  
  	// Validate the existence and readability of this resource file
  	File resourceFile = new File(directory, filename);
  	if (!resourceFile.exists() || !resourceFile.canRead())
  	    return (null);
  
  	// Return a URL that points to this resource file
  	try {
  	    return (new URL("file:" + resourceFile.getAbsolutePath()));
  	} catch (MalformedURLException e) {
  	    return (null);
  	}
  
      }
  
  
      /**
       * Load the specified resource from the specified JAR file, if it exists;
       * otherwise return <code>null</code>.
       *
       * @param jar JAR file to be searched
       * @param name Name of the desired resource
       */
      private URL loadResourceFromJarFile(File jar, String name) {
  
          return (null);  // FIXME - loadResourceFromJarFile()
  
      }
  
  
      /**
       * Load the specified resource from the specified directory, if it exists;
       * otherwise return <code>null</code>.
       *
       * @param directory File for the directory to be searched
       * @param name Name of the desired resource
       */
      private InputStream loadStreamFromDirectory(File directory, String name) {
  
          // Translate the resource name to a filename
          String filename = name.replace('.', File.separatorChar);
  
  	// Validate the existence and readability of this resource file
  	File resourceFile = new File(directory, filename);
  	if (!resourceFile.exists() || !resourceFile.canRead())
  	    return (null);
  
  	// Return an input stream for this resource file
  	try {
  	    return (new FileInputStream(resourceFile));
  	} catch (IOException e) {
  	    return (null);
  	}
  
      }
  
  
      /**
       * Load the specified resource from the specified JAR file, if it exists;
       * otherwise return <code>null</code>.
       *
       * @param directory File for the directory to be searched
       * @param name Name of the desired resource
       */
      private InputStream loadStreamFromJarFile(File jar, String name) {
  
          // Translate the resource name to a filename
          String filename = name.replace('.', '/');
  
  	// Create an input stream for this resource
  	JarFile jarFile = null;
  	JarEntry jarEntry = null;
  	InputStream is = null;
  	try {
  	    jarFile = new JarFile(jar);
  	    jarEntry = jarFile.getJarEntry(filename);
  	    if (jarEntry == null) {
  	        jarFile.close();
  		return (null);
  	    }
  	    is = new BufferedInputStream(jarFile.getInputStream(jarEntry));
  	    jarFile.close();
  	    return (is);
  	} catch (Throwable t) {
  	    if (jarFile != null) {
  	        try {
  		    jarFile.close();
  		} catch (Throwable u) {
  		    ;
  		}
  	    }
  	    return (null);
  	}
  
      }
  
  
      // ------------------------------------------------------- Private Classes
  
  
      /**
       * The cache entry for a particular class loaded by this class loader.
       */
      private static class CacheEntry {
  
          /**
  	 * The "last modified" time of the origin file at the time this class
  	 * was loaded, in milliseconds since the epoch.
  	 */
          long lastModified;
  
          /**
  	 * The actual loaded class.
  	 */
          Class loadedClass;
  
          /**
  	 * The File (for a directory) or JarFile (for a JAR) from which this
  	 * class was loaded, or <code>null</code> if loaded from the system.
  	 */
          Object origin;
  
          /**
  	 * Construct a new instance of this class.
  	 */
          public CacheEntry(Class loadedClass, Object origin,
  			  long lastModified) {
  
  	    this.loadedClass = loadedClass;
  	    this.origin = origin;
  	    this.lastModified = lastModified;
  
  	}
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/Reloader.java
  
  Index: Reloader.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/loader/Reloader.java,v 1.1 2000/05/09 02:35:47 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/05/09 02:35:47 $
   *
   * ====================================================================
   *
   * 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.loader;
  
  
  import java.util.Enumeration;
  
  
  /**
   * Internal interface that <code>ClassLoader</code> implementations may
   * optionally implement to support the auto-reload functionality of
   * <code>StandardLoader</code>.
   *
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2000/05/09 02:35:47 $
   */
  
  public interface Reloader {
  
  
      /**
       * Add a new repository to the set of places this ClassLoader can look for
       * classes to be loaded.
       *
       * @param repository Name of a source of classes to be loaded, such as a
       *  directory pathname, a JAR file pathname, or a ZIP file pathname
       *
       * @exception IllegalArgumentException if the specified repository is
       *  invalid or does not exist
       */
      public void addRepository(String repository);
  
  
      /**
       * Return an enumeration of the current repositories for this class
       * loader.  If there are no repositories, an empty Enumeration is
       * returned.
       */
      public Enumeration findRepositories();
  
  
      /**
       * Have one or more classes or resources been modified so that a reload
       * is appropriate?
       */
      public boolean modified();
  
  
      /**
       * Create a new ClassLoader instance based on our current repositories.
       */
      public Reloader reload();
  
  
      /**
       * Remove the specified repository from the set of places this ClassLoader
       * can look for classes to be loaded.  Any classes already loaded from this
       * repository will remain, but no future loads from here will take place.
       *
       * @param repository Name of the repository to remove
       */
      public void removeRepository(String repository);
  
  
  }