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/02/07 08:09:17 UTC

cvs commit: jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/util AdaptiveClassLoader.java SimpleFileFilter.java

craigmcc    00/02/06 23:09:17

  Modified:    proposals/catalina/src/share/org/apache/tomcat/core
                        LocalStrings.properties
  Added:       proposals/catalina/src/share/org/apache/tomcat/core
                        StandardLoader.java
               proposals/catalina/src/share/org/apache/tomcat/util
                        AdaptiveClassLoader.java SimpleFileFilter.java
  Log:
  Initial implementation of the Loader interface, which uses the Apache
  JServ AdaptiveClassLoader implementation.  This will enable Catalina to
  support auto-reload detection in Containers (not yet implemented).
  
  The AdaptiveClassLoader and SimpleFileFilter classes were copied from the
  Apache JServ CVS hierarchy and the license updated.  Any thoughts on
  whether this is the correct name to deal with such a situation (rather
  than creating a dependency on the Apache JServ JAR file)?
  
  Revision  Changes    Path
  1.6       +5 -0      jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/core/LocalStrings.properties
  
  Index: LocalStrings.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/core/LocalStrings.properties,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- LocalStrings.properties	2000/02/07 06:08:57	1.5
  +++ LocalStrings.properties	2000/02/07 07:09:17	1.6
  @@ -18,6 +18,11 @@
   standardHost.notStarted=Host has not yet been started
   standardHost.nullName=Host name is required
   standardHost.unfoundContext=Cannot find context for request URI $0
  +standardLoader.alreadyStarted=Loader has already been started
  +standardLoader.exists=Repository $0 does not exist
  +standardLoader.notStarted=Loader has not yet been started
  +standardLoader.read=Repository $0 cannot be read
  +standardLoader.type=Repository $0 is not a supported file type
   standardResources.malformedPath=Path must start with '/'
   standardResources.malformedURL=Malformed URL
   standardWrapper.noChild=Wrapper cannot have a child Container
  
  
  
  1.1                  jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/core/StandardLoader.java
  
  Index: StandardLoader.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/core/StandardLoader.java,v 1.1 2000/02/07 07:09:17 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/02/07 07:09:17 $
   *
   * ====================================================================
   *
   * 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.core;
  
  
  import java.beans.PropertyChangeListener;
  import java.beans.PropertyChangeSupport;
  import java.io.File;
  import java.io.IOException;
  import java.util.Enumeration;
  import java.util.Vector;
  import java.util.zip.ZipException;
  import java.util.zip.ZipFile;
  import org.apache.tomcat.Container;
  import org.apache.tomcat.Lifecycle;
  import org.apache.tomcat.LifecycleException;
  import org.apache.tomcat.Loader;
  import org.apache.tomcat.util.AdaptiveClassLoader;
  import org.apache.tomcat.util.StringManager;
  
  
  /**
   * Standard implementation of the <b>Loader</b> interface that wraps the
   * <code>AdaptiveClassLoader</code> implementation imported from the
   * Apache JServ project.  This class loader supports detection of modified
   * Java classes, which can be used to implement auto-reload support.
   * <p>
   * This class loader is configured by adding the pathnames of directories,
   * JAR files, and ZIP files with the <code>addRepository()</code> method,
   * prior to calling <code>start()</code>.  When a new class is required,
   * these repositories will be consulted first to locate the class.  If it
   * is not present, the system class loader will be used instead.
   *
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2000/02/07 07:09:17 $
   */
  
  public final class StandardLoader
      implements Lifecycle, Loader {
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * The class loader being managed by this Loader component.
       */
      private AdaptiveClassLoader classLoader = null;
  
  
      /**
       * The Container with which this Loader has been associated.
       */
      private Container container = null;
  
  
      /**
       * The descriptive information about this Loader implementation.
       */
      private static final String info =
  	"org.apache.tomcat.core.StandardLoader/1.0";
  
  
      /**
       * The set of class repositories for this class loader.  The contents of
       * this list must be directories, JAR files, and ZIP files that contain
       * the classes to be loaded by this class loader.
       */
      private Vector repositories = new Vector();
  
  
      /**
       * The string manager for this package.
       */
      protected StringManager sm =
  	StringManager.getManager(Constants.Package);
  
  
      /**
       * Has this component been started?
       */
      private boolean started = false;
  
  
      /**
       * The property change support for this component.
       */
      protected PropertyChangeSupport support = new PropertyChangeSupport(this);
  
  
      // ------------------------------------------------------------- Properties
  
  
      /**
       * Return the Java class loader to be used by this Container.
       */
      public ClassLoader getClassLoader() {
  
  	return (classLoader);
  
      }
  
  
      /**
       * Return the Container with which this Logger has been associated.
       */
      public Container getContainer() {
  
  	return (container);
  
      }
  
  
      /**
       * Set the Container with which this Logger has been associated.
       *
       * @param container The associated Container
       */
      public void setContainer(Container container) {
  
  	Container oldContainer = this.container;
  	this.container = container;
  	support.firePropertyChange("container", oldContainer, this.container);
  
      }
  
  
      /**
       * Return descriptive information about this Loader implementation and
       * the corresponding version number, in the format
       * <code>&lt;description&gt;/&lt;version&gt;</code>.
       */
      public String getInfo() {
  
  	return (info);
  
      }
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Add a property change listener to this component.
       *
       * @param listener The listener to add
       */
      public void addPropertyChangeListener(PropertyChangeListener listener) {
  
  	support.addPropertyChangeListener(listener);
  
      }
  
  
      /**
       * Add a new repository to the set of repositories for this class loader.
       *
       * @param repository Repository to be added
       */
      public void addRepository(String repository) {
  
  	if (!repositories.contains(repository))
  	    repositories.addElement(repository);
  
      }
  
  
      /**
       * Return the set of repositories defined for this class loader.
       * If none are defined, a zero-length array is returned.
       */
      public String[] findRepositories() {
  
  	synchronized (repositories) {
  	    String results[] = new String[repositories.size()];
  	    for (int i = 0; i < results.length; i++)
  		results[i] = (String) repositories.elementAt(i);
  	    return (results);
  	}
  
      }
  
  
      /**
       * Has the internal repository associated with this Loader been modified,
       * such that the loaded classes should be reloaded?
       */
      public boolean modified() {
  
  	if (classLoader != null)
  	    return (classLoader.shouldReload());
  	else
  	    return (false);
  
      }
  
  
      /**
       * Cause the underlying class loader (and therefore all of the classes
       * loaded by that class loader) to be thrown away, and creates a new one.
       *
       * @exception IllegalStateException if a change to the repositories for
       *  this class loader has rendered restart impossible
       */
      public void reload() {
  
  	try {
  	    stop();
  	    start();
  	} catch (Throwable t) {
  	    throw new IllegalStateException("reload: " + t);
  	}
  
      }
  
  
      /**
       * Remove a property change listener from this component.
       *
       * @param listener The listener to remove
       */
      public void removePropertyChangeListener(PropertyChangeListener listener) {
  
  	support.removePropertyChangeListener(listener);
  
      }
  
  
      /**
       * Remove a repository from the set of repositories for this loader.
       *
       * @param repository Repository to be removed
       */
      public void removeRepository(String repository) {
  
  	repositories.removeElement(repository);
  
      }
  
  
      // ------------------------------------------------------ Lifecycle Methods
  
  
      /**
       * Start this component, initializing our associated class loader.
       *
       * @exception LifecycleException if a lifecycle error occurs
       */
      public void start() throws LifecycleException {
  
  	// Validate and update our current component state
  	if (started)
  	    throw new LifecycleException
  		(sm.getString("standardLoader.alreadyStarted"));
  	started = true;
  
  	// Validate the specified repositories
  	Vector files = new Vector();
  	Enumeration names = repositories.elements();
  	while (names.hasMoreElements()) {
  	    File file = new File((String) names.nextElement());
  	    files.addElement(file);
  	    String path = null;
  	    try {
  		path = file.getCanonicalPath();
  	    } catch (IOException e) {
  		path = "UNKNOWN";
  	    }
  	    if (!file.exists())
  		throw new LifecycleException
  		    (sm.getString("standardLoader.exists", path));
  	    else if (!file.canRead())
  		throw new LifecycleException
  		    (sm.getString("standardLoader.read", path));
  	    else if (!checkType(file))
  		throw new LifecycleException
  		    (sm.getString("standardLoader.type", path));
  	}
  
  	// Construct a new class loader instance for our use
  	try {
  	    classLoader = new AdaptiveClassLoader(files);
  	} catch (IllegalArgumentException e) {
  	    throw new LifecycleException("start: ", e);
  	}
  
      }
  
  
      /**
       * Stop this component, finalizing our associated class loader.
       *
       * @exception LifecycleException if a lifecycle error occurs
       */
      public void stop() throws LifecycleException {
  
  	// Validate and update our current component state
  	if (!started)
  	    throw new LifecycleException
  		(sm.getString("standardLoader.notStarted"));
  	started = false;
  
  	// Throw away our current class loader
  	classLoader = null;
  
      }
  
  
      // ------------------------------------------------------- Private Methods
  
  
      /**
       * Is the specified file an appropriate entry for a repository?
       *
       * @param file The repository entry to be validated
       */
      private boolean checkType(File file) {
  
  	// Directories are acceptable
  	if (file.isDirectory())
  	    return (true);
  
  	// ZIP and JAR files that we can recognize are acceptable
  	boolean isArchive = true;
  	ZipFile zipFile = null;
  	try {
  	    zipFile = new ZipFile(file);
  	} catch (ZipException e) {
  	    isArchive = false;
  	} catch (IOException e) {
  	    isArchive = false;
  	}
  	if (zipFile != null) {
  	    try {
  		zipFile.close();
  	    } catch (IOException e) {
  		;
  	    }
  	}
  	return (isArchive);
  
      }
  
  
  }
  
  
  
  
  1.1                  jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/util/AdaptiveClassLoader.java
  
  Index: AdaptiveClassLoader.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/util/AdaptiveClassLoader.java,v 1.1 2000/02/07 07:09:17 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/02/07 07:09:17 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  
  package org.apache.tomcat.util;
  
  import java.io.*;
  import java.net.*;
  import java.text.*;
  import java.util.*;
  import java.util.zip.*;
  
  
  /**
   * A class loader that loads classes from directories and/or zip-format
   * file such as JAR file. It tracks the modification time of the classes
   * it loads to permit reloading through re-instantiation.
   * <P>
   * When the classloader reports its creator that one of the classes it
   * has loaded has changed on disk, it should discard the classloader
   * and create a new instance using <CODE>reinstantiate</CODE>.
   * The classes are then reloaded into the new classloader as required.
   *
   * <P>The classloader can also load resources, which are a means
   * for packaging application data such as images within a jar file
   * or directory.
   *
   * <P>The classloader always first tries to load classes and resources
   * from the system, and uses it's own path if that fails. This is also
   * done if an empty repository is passed at construction.
   *
   * <P><B>How autoreload works:</B></P>
   *
   * <P>The Java VM considers two classes the same if they have the same
   * fully-qualified name <B>and</B> if they were loaded from the same
   * <CODE>ClassLoader</CODE>.
   *
   * <P>There is no way for a classloader to 'undefine' a class once it
   * has been loaded.  However, the servlet engine can discard a
   * classloader and the classes it contains, causing the
   *
   * <P>The <CODE>JServServletManager</CODE> creates a new instance of
   * the classloader each time it detects that any of the loaded classes
   * have changed.
   *
   * <P>Before terminating, all servlets are destroyed.
   *
   * <P>According to the Java Language Specification (JLS), classes may
   * be garbage-collected when there are no longer any instances of that
   * class and the <CODE>java.lang.Class</CODE> object is finalizable.
   * It is intended that this be the case when a <CODE>JServClassLoader</CODE>
   * is discarded.
   *
   * <P>Many VM releases did not implement class garbage collection
   * properly.  In such a VM, the memory usage will continue to grow if
   * autoreloading is enable.  Running the VM with
   * <CODE>-verbosegc</CODE> (or the corresponding option for
   * non-Javasoft VMs) may give some debugging information.
   *
   * <P>It is important that the <CODE>destroy</CODE> method be
   * implemented properly, as servlets may be destroyed and
   * reinitialized several times in the life of a VM.
   *
   * <b>IMPLEMENTATION NOTE</b>:  This class loader implementation was
   * copied from the Apache JServ project, as of CVS version 1.18 (last
   * modified 25 January 2000).
   *
   * @author Francis J. Lacoste
   * @author Martin Pool
   * @author Jim Heintz
   * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
   * @version $Revision: 1.1 $ $Date: 2000/02/07 07:09:17 $
   * @see java.lang.ClassLoader
   */
  public final class AdaptiveClassLoader extends ClassLoader {
  
      /**
       * Generation counter, incremented for each classloader as they are
       * created.
       */
      static private int generationCounter = 0;
  
      /**
       * Generation number of the classloader, used to distinguish between
       * different instances.
       */
      private int generation;
  
      /**
       * Cache of the loaded classes. This contains ClassCacheEntry keyed
       * by class names.
       */
      private Hashtable cache;
  
      /**
       * The classpath which this classloader searches for class definitions.
       * Each element of the vector should be either a directory, a .zip
       * file, or a .jar file.
       * <p>
       * It may be empty when only system classes are controlled.
       */
      private Vector repository;
  
      /**
       * Private class used to maintain information about the classes that
       * we loaded.
       */
      private static class ClassCacheEntry {
  
          /**
           * The actual loaded class
           */
          Class loadedClass;
  
          /**
           * The file from which this class was loaded; or null if
           * it was loaded from the system.
           */
          File origin;
  
          /**
           * The time at which the class was loaded from the origin
           * file, in ms since the epoch.
           */
          long lastModified;
  
          /**
           * Check whether this class was loaded from the system.
           */
          public boolean isSystemClass() {
              return origin == null;
          }
      }
  
      //------------------------------------------------------- Constructors
  
      /**
       * Creates a new class loader that will load classes from specified
       * class repositories.
       *
       * @param classRepository An set of File classes indicating
       *        directories and/or zip/jar files. It may be empty when
       *        only system classes are loaded.
       * @throw java.lang.IllegalArgumentException if the objects contained
       *        in the vector are not a file instance or the file is not
       *        a valid directory or a zip/jar file.
       */
      public AdaptiveClassLoader(Vector classRepository)
          throws IllegalArgumentException
      {
          // Create the cache of loaded classes
          cache = new Hashtable();
  
          // Verify that all the repository are valid.
          Enumeration e = classRepository.elements();
          while(e.hasMoreElements()) {
              Object o = e.nextElement();
              File file;
              File[] files;
              int i;
  
              // Check to see if element is a File instance.
              try {
                  file = (File) o;
              } catch (ClassCastException objectIsNotFile) {
                  throw new IllegalArgumentException("Object " + o
                      + "is not a valid \"File\" instance");
              }
  
              files=SimpleFileFilter.fileOrFiles(file);
              if (files!=null)
              {
                  for (i=0;i<files.length;i++)
                  {
                      file=files[i];
                      // Check to see if we have proper access.
                      if (!file.exists()) {
                          throw new IllegalArgumentException("Repository "
                          + file.getAbsolutePath() + " doesn't exist!");
                      } else if (!file.canRead()) {
                          throw new IllegalArgumentException(
                          "Do not have read access for file "
                          + file.getAbsolutePath());
                      }
  
                      // Check that it is a directory or zip/jar file
                      if (!(file.isDirectory() || isZipOrJarArchive(file))) {
                          throw new IllegalArgumentException(
                             file.getAbsolutePath()
                             + " is not a directory or zip/jar file"
                             + " or if it's a zip/jar file then it is corrupted.");
                      }
                  }
              }
          }
  
          // Store the class repository for use
          this.repository = classRepository;
  
          // Increment and store generation counter
          this.generation = generationCounter++;
      }
  
      //------------------------------------------------------- Methods
  
      /**
       * Test if a file is a ZIP or JAR archive.
       *
       * @param file the file to be tested.
       * @return true if the file is a ZIP/JAR archive, false otherwise.
       */
      private boolean isZipOrJarArchive(File file) {
          boolean isArchive = true;
          ZipFile zipFile = null;
  
          try {
              zipFile = new ZipFile(file);
          } catch (ZipException zipCurrupted) {
              isArchive = false;
          } catch (IOException anyIOError) {
              isArchive = false;
          } finally {
              if (zipFile != null) {
                  try {
                      zipFile.close();
                  } catch (IOException ignored) {}
              }
          }
  
          return isArchive;
      }
  
      /**
       * Check to see if a given class should be reloaded because of a
       * modification to the original class.
       *
       * @param className The name of the class to check for modification.
       */
      public synchronized boolean shouldReload(String classname) {
  
          ClassCacheEntry entry = (ClassCacheEntry) cache.get(classname);
  
          if (entry == null) {
              // class wasn't even loaded
              return false;
          } else if (entry.isSystemClass()) {
              // System classes cannot be reloaded
              return false;
          } else {
              boolean reload =
                  (entry.origin.lastModified() != entry.lastModified);
              return reload;
          }
      }
  
      /**
       * Check whether the classloader should be reinstantiated.
       * <P>
       * The classloader must be replaced if there is any class whose
          * origin file has changed since it was last loaded.
       */
      public synchronized boolean shouldReload() {
  
          // Check whether any class has changed
          Enumeration e = cache.elements();
          while (e.hasMoreElements()) {
              ClassCacheEntry entry = (ClassCacheEntry) e.nextElement();
  
          if (entry.isSystemClass()) continue;
  
              // XXX: Because we want the classloader to be an accurate
              // reflection of the contents of the repository, we also
              // reload if a class origin file is now missing.  This
              // probably makes things a bit more fragile, but is OK in
              // a servlet development situation. <mb...@pharos.com.au>
  
              long msOrigin = entry.origin.lastModified();
  
              if (msOrigin == 0) {
                  // class no longer exists
                  return true;
              }
  
              if (msOrigin != entry.lastModified) {
                  // class is modified
                  return true;
              }
          }
  
          // No changes, no need to reload
          return false;
      }
  
      /**
       * Re-instantiate this class loader.
       * <p>
       * This method creates a new instance
       * of the class loader that will load classes form the same path
       * as this one.
       */
      public AdaptiveClassLoader reinstantiate() {
          return new AdaptiveClassLoader(repository);
      }
  
      //------------------------------------ Implementation of Classloader
  
      /*
       * XXX: The javadoc for java.lang.ClassLoader says that the
       * ClassLoader should cache classes so that it can handle repeated
       * requests for the same class.  On the other hand, the JLS seems
       * to imply that each classloader is only asked to load each class
       * once.  Is this a contradiction?
       *
       * Perhaps the second call only applies to classes which have been
       * garbage-collected?
       */
  
      /**
       * Resolves the specified name to a Class. The method loadClass()
       * is called by the virtual machine.  As an abstract method,
       * loadClass() must be defined in a subclass of ClassLoader.
       *
       * @param      name the name of the desired Class.
       * @param      resolve true if the Class needs to be resolved;
       *             false if the virtual machine just wants to determine
       *             whether the class exists or not
       * @return     the resulting Class.
       * @exception  ClassNotFoundException  if the class loader cannot
       *             find a the requested class.
       */
      protected synchronized Class loadClass(String name, boolean resolve)
          throws ClassNotFoundException
      {
          // The class object that will be returned.
          Class c = null;
  
          // Use the cached value, if this class is already loaded into
          // this classloader.
          ClassCacheEntry entry = (ClassCacheEntry) cache.get(name);
  
          if (entry != null) {
              // Class found in our cache
              c = entry.loadedClass;
              if (resolve) resolveClass(c);
              return c;
          }
  
          if (!securityAllowsClass(name)) {
              return loadSystemClass(name, resolve);
          }
  
          // Attempt to load the class from the system
          try {
              c = loadSystemClass(name, resolve);
              if (c != null) {
                  if (resolve) resolveClass(c);
                  return c;
              }
          } catch (Exception e) {
              c = null;
          }
  
          // Try to load it from each repository
          Enumeration repEnum = repository.elements();
  
          // Cache entry.
          ClassCacheEntry classCache = new ClassCacheEntry();
          while (repEnum.hasMoreElements()) {
              byte[] classData=null;
  
              File file = (File) repEnum.nextElement();
              File[] files=SimpleFileFilter.fileOrFiles(file);
              for (int i=0;i<files.length;i++)
              {
                  file=files[i];
                  try {
                      if (file.isDirectory()) {
                          classData =
                              loadClassFromDirectory(file, name, classCache);
                      } else {
                          classData =
                              loadClassFromZipfile(file, name, classCache);
                      }
                  } catch(IOException ioe) {
                      // Error while reading in data, consider it as not found
                      classData = null;
                  }
              }
                
              if (classData != null) {
                  // Define the class
                  c = defineClass(name, classData, 0, classData.length);
                  // Cache the result;
                  classCache.loadedClass = c;
                  // Origin is set by the specific loader
                  classCache.lastModified = classCache.origin.lastModified();
                  cache.put(name, classCache);
      
                  // Resolve it if necessary
                  if (resolve) resolveClass(c);
                  
                  return c;
              }
          }
  
          // If not found in any repository
          throw new ClassNotFoundException(name);
      }
  
      /**
       * Load a class using the system classloader.
       *
       * @exception  ClassNotFoundException  if the class loader cannot
       *             find a the requested class.
       * @exception  NoClassDefFoundError  if the class loader cannot
       *             find a definition for the class.
       */
      private Class loadSystemClass(String name, boolean resolve)
          throws NoClassDefFoundError, ClassNotFoundException
      {
          Class c = findSystemClass(name);
          // Throws if not found.
  
          // Add cache entry
          ClassCacheEntry cacheEntry = new ClassCacheEntry();
          cacheEntry.origin = null;
          cacheEntry.loadedClass = c;
          cacheEntry.lastModified = Long.MAX_VALUE;
          cache.put(name, cacheEntry);
  
          if (resolve) resolveClass(c);
  
          return c;
      }
  
      /**
       * Checks whether a classloader is allowed to define a given class,
       * within the security manager restrictions.
       */
      // XXX: Should we perhaps also not allow classes to be dynamically
      // loaded from org.apache.jserv.*?  Would it introduce security
      // problems if people could override classes here?
      // <mbp@humbug.org.au 1998-07-29>
      private boolean securityAllowsClass(String className) {
          try {
              SecurityManager security = System.getSecurityManager();
  
              if (security == null) {
                  // if there's no security manager then all classes
                  // are allowed to be loaded
                  return true;
              }
  
              int lastDot = className.lastIndexOf('.');
              // Check if we are allowed to load the class' package
              security.checkPackageDefinition((lastDot > -1)
                  ? className.substring(0, lastDot) : "");
              // Throws if not allowed
              return true;
          } catch (SecurityException e) {
              return false;
          }
      }
  
      /**
       * Tries to load the class from a directory.
       *
       * @param dir The directory that contains classes.
       * @param name The classname
       * @param cache The cache entry to set the file if successful.
       */
      private byte[] loadClassFromDirectory(File dir, String name,
          ClassCacheEntry cache)
              throws IOException
      {
          // Translate class name to file name
          String classFileName =
              name.replace('.', File.separatorChar) + ".class";
  
          // Check for garbage input at beginning of file name
          // i.e. ../ or similar
          if (!Character.isJavaIdentifierStart(classFileName.charAt(0))) {
              // Find real beginning of class name
              int start = 1;
              while (!Character.isJavaIdentifierStart(
                  classFileName.charAt(start++)));
              classFileName = classFileName.substring(start);
          }
  
          File classFile = new File(dir, classFileName);
  
          if (classFile.exists()) {
              cache.origin = classFile;
              InputStream in = new FileInputStream(classFile);
              try {
                  return loadBytesFromStream(in, (int) classFile.length());
              } finally {
                  in.close();
              }
          } else {
              // Not found
              return null;
          }
      }
  
      /**
       * Tries to load the class from a zip file.
       *
       * @param file The zipfile that contains classes.
       * @param name The classname
       * @param cache The cache entry to set the file if successful.
       */
      private byte[] loadClassFromZipfile(File file, String name,
              ClassCacheEntry cache)
          throws IOException
      {
          // Translate class name to file name
          String classFileName = name.replace('.', '/') + ".class";
  
          ZipFile zipfile = new ZipFile(file);
  
          try {
              ZipEntry entry = zipfile.getEntry(classFileName);
              if (entry != null) {
                  cache.origin = file;
                  return loadBytesFromStream(zipfile.getInputStream(entry),
                      (int) entry.getSize());
              } else {
                  // Not found
                  return null;
              }
          } finally {
              zipfile.close();
          }
      }
  
      /**
       * Loads all the bytes of an InputStream.
       */
      private byte[] loadBytesFromStream(InputStream in, int length)
          throws IOException
      {
          byte[] buf = new byte[length];
          int nRead, count = 0;
  
          while ((length > 0) && ((nRead = in.read(buf,count,length)) != -1)) {
              count += nRead;
              length -= nRead;
          }
  
          return buf;
      }
  
      /**
       * Get an InputStream on a given resource.  Will return null if no
       * resource with this name is found.
       * <p>
       * The JServClassLoader translate the resource's name to a file
       * or a zip entry. It looks for the resource in all its repository
       * entry.
       *
       * @see     java.lang.Class#getResourceAsStream(String)
       * @param   name    the name of the resource, to be used as is.
       * @return  an InputStream on the resource, or null if not found.
       */
      public InputStream getResourceAsStream(String name) {
          // Try to load it from the system class
          InputStream s = getSystemResourceAsStream(name);
  
          if (s == null) {
              // Try to find it from every repository
              Enumeration repEnum = repository.elements();
              while (repEnum.hasMoreElements()) {
                  File file = (File) repEnum.nextElement();
                  if (file.isDirectory()) {
                      s = loadResourceFromDirectory(file, name);
                  }
                  else if(name.endsWith(".initArgs")) {
                      String parentFile = file.getParent();
                      if (parentFile != null) {
                          File dir = new File(parentFile);
                          s = loadResourceFromDirectory(dir, name);
                      }
                  } else {
                      s = loadResourceFromZipfile(file, name);
                  }
  
                  if (s != null) {
                      break;
                  }
              }
          }
          return s;
      }
  
      /**
       * Loads resource from a directory.
       */
      private InputStream loadResourceFromDirectory(File dir, String name) {
          // Name of resources are always separated by /
          String fileName = name.replace('/', File.separatorChar);
          File resFile = new File(dir, fileName);
  
          if (resFile.exists()) {
              try {
                  return new FileInputStream(resFile);
              } catch (FileNotFoundException shouldnothappen) {
                  return null;
              }
          } else {
              return null;
          }
      }
  
      /**
       * Loads resource from a zip file
       */
      private InputStream loadResourceFromZipfile(File file, String name) {
          ZipFile zipfile = null;
          InputStream resourceStream = null;
          try {
              zipfile = new ZipFile(file);
              ZipEntry entry = zipfile.getEntry(name);
  
              if (entry != null) {
                  long length = entry.getSize();
                  resourceStream = zipfile.getInputStream(entry);
                  byte[] data = loadBytesFromStream(resourceStream, (int)length);
                  return new ByteArrayInputStream(data);
              } else {
                  return null;
              }
          } catch(IOException e) {
              return null;
          } finally {
              if ( resourceStream != null ) {
                  try {
                      resourceStream.close();
                  } catch ( IOException ignored ) {
                  }
              }
              if ( zipfile != null ) {
                  try {
                      zipfile.close();
                  } catch ( IOException ignored ) {
                  }
              }
          }
      }
  
      /**
       * Find a resource with a given name.  The return is a URL to the
       * resource. Doing a getContent() on the URL may return an Image,
       * an AudioClip,or an InputStream.
       * <p>
       * This classloader looks for the resource only in the directory
       * repository for this resource.
       *
       * @param   name    the name of the resource, to be used as is.
       * @return  an URL on the resource, or null if not found.
       */
      public URL getResource(String name) {
  
          // First ask the primordial class loader to fetch it from the classpath
          URL u = getSystemResource(name);
          if (u != null) {
              return u;
          }
  
          if (name == null) {
              return null;
          }
  
          // We got here so we have to look for the resource in our list of repository elements
          Enumeration repEnum = repository.elements();
          while (repEnum.hasMoreElements()) {
              File file = (File) repEnum.nextElement();
              // Construct a file://-URL if the repository is a directory
              if (file.isDirectory()) {
                  String fileName = name.replace('/', File.separatorChar);
                  File resFile = new File(file, fileName);
                  if (resFile.exists()) {
                      // Build a file:// URL form the file name
                      try {
                          return new URL("file", null, resFile.getAbsolutePath());                    	
                      } catch(java.net.MalformedURLException badurl) {
                          badurl.printStackTrace();
                          return null;
                      }
                  }
              }
              else {
                  // a jar:-URL *could* change even between minor releases, but
                  // didn't between JVM's 1.1.6 and 1.3beta. Tested on JVM's from
                  // IBM, Blackdown, Microsoft, Sun @ Windows and Sun @ Solaris
                  try {
                      ZipFile zf = new ZipFile(file.getAbsolutePath());
                      ZipEntry ze = zf.getEntry(name);
  
                      if (ze != null) {
                          try {
                              return new URL("jar:file:" + file.getAbsolutePath() + "!/" + name);
                          } catch(java.net.MalformedURLException badurl) {
                              badurl.printStackTrace();
                              return null;
                          }
                      }
                  } catch (IOException ioe) {
                      ioe.printStackTrace();
                      return null;
                  }
              }   
          }
  
          // Not found
          return null;
      }
  }
  
  
  
  1.1                  jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/util/SimpleFileFilter.java
  
  Index: SimpleFileFilter.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/util/SimpleFileFilter.java,v 1.1 2000/02/07 07:09:17 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/02/07 07:09:17 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  
  package org.apache.tomcat.util;
  
  import java.io.*;
  
  /**
   * Class that implements the java.io.FilenameFilter
   * interface.
   *
   * <b>IMPLEMENTATION NOTE</b>:  This class was copied from the
   * Apache JServ project, based on CVS version 1.3.
   *
   * @author <a href="mailto:mjenning@islandnet.com">Mike Jennings</a>
   * @version $Revision: 1.1 $
   */
  public final class SimpleFileFilter implements FilenameFilter {
       private String[] extensions;
  
       public SimpleFileFilter(String ext)
       {
           this(new String[]{ext});
       }
  
       public SimpleFileFilter(String[] exts) {
           extensions=new String[exts.length];
           for (int i=0;i<exts.length;i++) {
               extensions[i]=exts[i].toLowerCase();
           }
       }
  
       /** filenamefilter interface method */
       public boolean accept(File dir,String _name) {
           String name=_name.toLowerCase();
           for (int i=0;i<extensions.length;i++) {
               if (name.endsWith(extensions[i])) return true;
           }
           return false;
       }
  
       /** 
        * this method checks to see if an asterisk
        * is imbedded in the filename, if it is, it
        * does an "ls" or "dir" of the parent directory
        * returning a list of files that match
        * eg. /usr/home/mjennings/*.jar
        * would expand out to all of the files with a .jar
        * extension in the /usr/home/mjennings directory 
        */
        public static File[] fileOrFiles(File f) {
            if (f==null) return null;
  
            String parentstring=f.getParent();
            File parent;
            if (parentstring!=null)
              parent=new File(parentstring);
            else
              parent=new File("");
            
            String fname=f.getName();
            File[] files;
            if (fname.length() > 0 && fname.charAt(0)=='*') {
                String filter=fname.substring(1,fname.length());
                String []names = parent.list( new SimpleFileFilter( filter ) );
                if ( names == null )
                  return null;
                files = new File[ names.length ];
                for ( int i = 0; i < names.length; i++ ) {
                files[i] = new File( parent.getPath() + File.separator + names[i] );
                }
                return files;
            } else {
                files=new File[1];
                files[0]=f;
                return files;
            }
        }
  }
  
  
  

Re: cvs commit: jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/utilAdaptiveClassLoader.java SimpleFileFilter.java

Posted by "Craig R. McClanahan" <cm...@mytownnet.com>.
costin@eng.sun.com wrote:

> Hi Craig,
>
> Is it ok if we move or copy those files to the main branch ?
> ( I also have them in my working space, and I think Sam said something
> about class reloading too. )
>

I agree -- I'm sharing stuff out of the main org.apache.tomcat.util branch
anyway; I didn't want to impact anything while developing.

>
> It would be very good to share some of the code and try to work togheter
> on common functionality, and ClassLoader is one example. I don't think
> there are so big differences between Catalina requirements for class
> loading and the current requirements and it is not dependent on the
> internal architecture.
>

Regarding functionality, I'm planning on adding a boolean property to the
Context component declaring whether auto-reload support should be enabled or
not (default=false), plus there needs to be a way to configure the
repositories list for the class loader component.  In Catalina, I've already
modified my component that owns a particular servlet definition (Wrapper) to
load servlet classes via the custom class loader -- you'll need to do
something similar on the main branch.

Could you go ahead and move these two files?  It's not real convenient for
me to do that from work.

>
> Costin
>

Craig



Re: cvs commit: jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/util AdaptiveClassLoader.java SimpleFileFilter.java

Posted by co...@eng.sun.com.
Hi Craig,

Is it ok if we move or copy those files to the main branch ?
( I also have them in my working space, and I think Sam said something
about class reloading too. )

It would be very good to share some of the code and try to work togheter
on common functionality, and ClassLoader is one example. I don't think
there are so big differences between Catalina requirements for class
loading and the current requirements and it is not dependent on the
internal architecture. 



Costin




>                proposals/catalina/src/share/org/apache/tomcat/util
>                         AdaptiveClassLoader.java SimpleFileFilter.java