You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@apache.org on 2004/10/06 07:00:04 UTC

cvs commit: jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader Loader.java LoaderProperties.java Module.java ModuleClassLoader.java ModuleListener.java Repository.java package.html

costin      2004/10/05 22:00:04

  Added:       util/loader tomcat-loader.manifest
               util/loader/org/apache/tomcat/util/loader Loader.java
                        LoaderProperties.java Module.java
                        ModuleClassLoader.java ModuleListener.java
                        Repository.java package.html
  Log:
  Refactoring of the startup loader - Bootstrap, CatalinaProperties, StandardClassLoader.
  
  See comments inside.
  
  This has no external dependency  (except JDK1.4 - 1.3 actually ) - jmx, logging, etc are
  all loaded in the common loader.
  
  catalina.properties is still used to define the class paths - or loader.properties while
  both are used.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat-connectors/util/loader/tomcat-loader.manifest
  
  Index: tomcat-loader.manifest
  ===================================================================
  Manifest-version: 1.0
  Extension-Name: org.apache.tomcat.util.loader
  Specification-Vendor: Apache Software Foundation
  Specification-Version: 1.0
  Implementation-Vendor-Id: org.apache
  Implementation-Vendor: Apache Software Foundation
  Implementation-Version: 1.0
  Main-Class: org.apache.tomcat.util.loader.Loader
  
  
  
  1.1                  jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/Loader.java
  
  Index: Loader.java
  ===================================================================
  /*
   * Copyright 1999,2004 The Apache Software Foundation.
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *      http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  
  package org.apache.tomcat.util.loader;
  
  
  import java.io.File;
  import java.lang.reflect.Method;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.StringTokenizer;
  import java.util.Vector;
  
  
  /**
   * Boostrap loader for Catalina.  This application constructs a class loader
   * for use in loading the Catalina internal classes (by accumulating all of the
   * JAR files found in the "server" directory under "catalina.home"), and
   * starts the regular execution of the container.  The purpose of this
   * roundabout approach is to keep the Catalina internal classes (and any
   * other classes they depend on, such as an XML parser) out of the system
   * class path and therefore not visible to application level classes.
   *
   * @author Craig R. McClanahan
   * @author Remy Maucherat
   * @author Costin Manolache
   */ 
  public final class Loader  {
  
      private static final boolean DEBUG=true; //LoaderProperties.getProperty("loader.Loader.debug") != null;
      private static final boolean FLAT=false;//LoaderProperties.getProperty("loader.Loader.flat") != null;
      
      // -------------------------------------------------------------- Constants
  
  
      protected static final String CATALINA_HOME_TOKEN = "${catalina.home}";
      protected static final String CATALINA_BASE_TOKEN = "${catalina.base}";
  
  
      // ------------------------------------------------------- Static Variables
  
  
      /**
       * Daemon object used by main.
       */
      private static Loader daemon = null;
  
      // one should be enough
      ModuleListener listener;
  
  
      // -------------------------------------------------------------- Variables
  
      protected Repository commonRepository = null;
      protected Repository catalinaRepository = null;
      protected Repository sharedRepository = null;
  
      protected ClassLoader catalinaLoader = null;
      private String[] args;
      private Hashtable repositories=new Hashtable();
      
  
  
      // -------------------------------------------------------- Private Methods
  
  
      private void initClassLoaders() {
          try {
              commonRepository = initRepository("common", null);
              catalinaRepository = initRepository("server", commonRepository);
              catalinaLoader = catalinaRepository.getClassLoader();
              sharedRepository = initRepository("shared", commonRepository);
          } catch (Throwable t) {
              log("Class loader creation threw exception", t);
              System.exit(1);
          }
      }
  
      public Repository createRepository(String name, Repository parent) {
          Repository lg=new Repository(this);
  
          lg.setName(name);
         
          lg.setParent( parent );
          
          repositories.put(name, lg);
          
          if( listener != null ) {
              listener.repositoryAdd(lg);
          }
          return lg;
      }
  
      public Enumeration getRepositoryNames() {
          return repositories.keys();
      }
      
      /** Create a module using the NAME.loader property to construct the 
       *  classpath.
       * 
       * @param name
       * @param parent
       * @return
       * @throws Exception
       */
      private Repository initRepository(String name, Repository parent)
          throws Exception 
      {
          String value = LoaderProperties.getProperty(name + ".loader");
  
          Repository lg=createRepository(name, parent );
          if( DEBUG ) log( "Creating loading group " + name + " - " + value);
          
          if ((value == null) || (value.equals("")))
              return lg;
  
          ArrayList unpackedList = new ArrayList();
          ArrayList packedList = new ArrayList();
          ArrayList urlList = new ArrayList();
  
          Vector repo=split( value );
          Enumeration elems=repo.elements();
          while (elems.hasMoreElements()) {
              String repository = (String)elems.nextElement();
  
              // Local repository
              boolean packed = false;
              if (repository.startsWith(CATALINA_HOME_TOKEN)) {
                  repository = LoaderProperties.getCatalinaHome()
                      + repository.substring(CATALINA_HOME_TOKEN.length());
              } else if (repository.startsWith(CATALINA_BASE_TOKEN)) {
                  repository = LoaderProperties.getCatalinaBase()
                      + repository.substring(CATALINA_BASE_TOKEN.length());
              }
  
              // Check for a JAR URL repository
              try {
                  urlList.add(new URL(repository));
                  continue;
              } catch (MalformedURLException e) {
                  // Ignore
              }
  
              if (repository.endsWith("*.jar")) {
                  packed = true;
                  repository = repository.substring
                      (0, repository.length() - "*.jar".length());
              }
              if (packed) {
                  packedList.add(new File(repository));
              } else {
                  unpackedList.add(new File(repository));
              }
          }
  
          File[] unpacked = (File[]) unpackedList.toArray(new File[0]);
          File[] packed = (File[]) packedList.toArray(new File[0]);
          URL[] urls = (URL[]) urlList.toArray(new URL[0]);
  
          // previously: ClassLoaderFactory.createClassLoader
          initRepository(lg, unpacked, packed, urls, parent); //new ModuleGroup();
          
          
          // TODO: JMX registration for the group loader 
          /*
          // Register the server classloader
          ObjectName objectName =
              new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
          mBeanServer.registerMBean(classLoader, objectName);
          */
          
          return lg; // classLoader;
  
      }
  
      /** Small hack to allow a loader.properties file to be passed in CLI, to
       * allow a more convenient method to start with different params from 
       * explorer/kde/etc.
       * 
       * If the first arg ends with ".loader", it is used as loader.properties
       * file and removed from args[].  
       */
      private void processCLI() {
          if( args.length > 0 &&
                  (args[0].toLowerCase().endsWith(".tomcat") ||
                          args[0].toLowerCase().endsWith("loader.properties") )) {
              String props=args[0];
              String args2[]=new String[args.length-1];
              System.arraycopy(args, 1, args2, 0, args2.length);
              args=args2;
              LoaderProperties.setPropertiesFile(props);
          }
      }
      
      /**
       * Initialize:
       *  - detect the home/base directories
       *  - init the loaders / modules
       *  - instantiate the "startup" class(es)
       * 
       */
      public void main()
          throws Exception
      {
          processCLI();
          
          // Set Catalina path
          LoaderProperties.setCatalinaHome();
          LoaderProperties.setCatalinaBase();
  
          initClassLoaders();
          
          Thread.currentThread().setContextClassLoader(catalinaLoader);
  
          securityPreload(catalinaLoader);
  
          // Load our startup classes and call its process() method
          /* Why multiple classes ? 
           * - maybe you want to start more "servers" ( tomcat ,something else )
           * - easy hook for other load on startup modules ( like a jmx hook )
           * - maybe split the loader-specific code from catalina 
           */
          String startupClasses=LoaderProperties.getProperty("loader.auto-startup",
                  "org.apache.catalina.startup.CatalinaModuleListener");
          Vector v=split( startupClasses ); 
          
          for( int i=0; i<v.size(); i++ ) {
              String startupCls=(String)v.elementAt(i);
          
              if (DEBUG)
                  log("Loading startup class " + startupCls);
              
              Class startupClass =
                  catalinaLoader.loadClass(startupCls);
              
              Object startupInstance = startupClass.newInstance();
              
              if( startupInstance instanceof ModuleListener ) {
                  addModuleListener((ModuleListener)startupInstance);
  
                  // it can get args[] and properties from Loader
                  listener.setLoader(this);
                  
                  // all arg processing moved there. Maybe we can make it consistent
                  // for all startup schemes
                  listener.start();
              } else {
                  Class paramTypes[] = new Class[0];
                  Object paramValues[] = new Object[0];
                  Method method =
                      startupInstance.getClass().getMethod("execute", paramTypes);
                  if( method==null ) 
                      method = startupInstance.getClass().getMethod("start", paramTypes);
                  method.invoke(startupInstance, paramValues);
              }
  
          }
      }
  
      public Repository getRepository( String name ) {
          return (Repository)repositories.get(name);
      }
      
      private  static void securityPreload(ClassLoader loader)
          throws Exception {
  
          if( System.getSecurityManager() == null ){
              return;
          }
  
          String value=LoaderProperties.getProperty("security.preload");
          Vector repo=split( value );
          Enumeration elems=repo.elements();
          while (elems.hasMoreElements()) {
              String classN = (String)elems.nextElement();
              try {
                  loader.loadClass( classN);
              } catch( Throwable t ) {
                  // ignore
              }
          }
      }
  
      // ----------------------------------------------------------- Main Program
  
      public String[] getArgs() {
          return args;
      }
  
      /**
       * Main method.
       *
       * @param args Command line arguments to be processed
       */
      public static void main(String args[]) {
          
          try {
              if (daemon == null) {
                  daemon = new Loader();
                  daemon.args=args;
  
                  try {
                      daemon.main();
                  } catch (Throwable t) {
                      t.printStackTrace();
                      return;
                  }
              }
          } catch (Throwable t) {
              t.printStackTrace();
          }
  
      }
  
      public void setCatalinaHome(String s) {
          System.setProperty( "catalina.home", s );
      }
  
      public void setCatalinaBase(String s) {
          System.setProperty( "catalina.base", s );
      }
  
      /**
       * Public as ModuleClassLoader. 
       * 
       * @param cl
       * @return
       */
      public Module getModule(ClassLoader cl ) {
          if( cl instanceof ModuleClassLoader ) {
              return ((ModuleClassLoader)cl).getModule();
          }
          return null;
      }
      
      /**
       * Create and return a new class loader, based on the configuration
       * defaults and the specified directory paths:
       *
       * @param unpacked Array of pathnames to unpacked directories that should
       *  be added to the repositories of the class loader, or <code>null</code> 
       * for no unpacked directories to be considered
       * @param packed Array of pathnames to directories containing JAR files
       *  that should be added to the repositories of the class loader, 
       * or <code>null</code> for no directories of JAR files to be considered
       * @param urls Array of URLs to remote repositories, designing either JAR 
       *  resources or uncompressed directories that should be added to 
       *  the repositories of the class loader, or <code>null</code> for no 
       *  directories of JAR files to be considered
       * @param parent Parent class loader for the new class loader, or
       *  <code>null</code> for the system class loader.
       *
       * @exception Exception if an error occurs constructing the class loader
       */
      private void initRepository(Repository lg, File unpacked[],
              File packed[],  URL urls[],  Repository parent)
          throws Exception 
      {
          StringBuffer sb=new StringBuffer();
  
          // Construct the "class path" for this class loader
          ArrayList list = new ArrayList();
          
          // Add unpacked directories
          if (unpacked != null) {
              for (int i = 0; i < unpacked.length; i++)  {
                  File file = unpacked[i];
                  if (!file.exists() || !file.canRead()) {
                      if (DEBUG)
                          log("  Not found:  "+ file.getAbsolutePath());
                      continue;
                  }
                  if (DEBUG)
                      sb.append(" "+ file.getAbsolutePath());
                  String cPath=file.getCanonicalPath();
                  URL url=null;
                  if( cPath.toLowerCase().endsWith(".jar") ||
                          cPath.toLowerCase().endsWith(".zip") ) {
                      url = new URL("file", null, cPath);
                  } else {
                      url = new URL("file", null, cPath + File.separator);
                  }
                  if( ! FLAT ) {
                      addLoader(lg, parent, new URL[] { url });
                  } else {
                      list.add(url);
                  }
              }
          }
          
          // Add packed directory JAR files
          if (packed != null) {
              for (int i = 0; i < packed.length; i++) {
                  File directory = packed[i];
                  if (!directory.isDirectory() || !directory.exists() ||
                          !directory.canRead()) {
                      if (DEBUG)
                          log("  Not found:  "+ directory.getAbsolutePath());
                      continue;
                  }
                  String filenames[] = directory.list();
                  for (int j = 0; j < filenames.length; j++) {
                      String filename = filenames[j].toLowerCase();
                      if (!filename.endsWith(".jar"))
                          continue;
                      File file = new File(directory, filenames[j]);
                      if (DEBUG)
                          sb.append(" "+ file.getAbsolutePath());
                      URL url = new URL("file", null,
                              file.getCanonicalPath());
                      if( ! FLAT ) {
                          addLoader(lg, parent, new URL[] { url });
                      } else {
                          list.add(url);
                      }
                  }
              }
          }
          
          // Add URLs
          if (urls != null) {
              for (int i = 0; i < urls.length; i++) {
                  if( ! FLAT ) {
                      addLoader(lg, parent, new URL[] { urls[i] });
                  } else {
                      list.add(urls[i]);
                  }
                  if (DEBUG)
                      sb.append(" "+ urls[i]);
              }
          }
          
          // Construct the class loader itself
          
          // TODO: experiment with loading each jar in a separate loader.
          if (DEBUG)
              log("Creating new class loader " + lg.getName() + " " + sb.toString());
          
          
          URL[] array = (URL[]) list.toArray(new URL[list.size()]);
          if( array.length > 0 ) {
              addLoader(lg, parent, array);
          }
      }
      
      /**
       * @param lg
       * @param parent
       * @param list
       */
      private void addLoader(Repository lg, Repository parent, URL array[]) 
          throws Exception
      {
          Module module=new Module();
          
          module.setParent( parent );
          module.setClasspath( array );
          
          lg.addModule(module);
          
      }
  
      private static Vector split( String value ) {
          Vector result=new Vector();
          StringTokenizer tokenizer = new StringTokenizer(value, ",");
          while (tokenizer.hasMoreElements()) {
              String repository = tokenizer.nextToken();
              repository.trim();
              if( ! "".equals(repository) )
                  result.addElement(repository);
          }
          return result;
      }
  
      
      /** Add a module listener. 
       * 
       * To keep the dependencies minimal, the loader package only implements the
       * basic class loading mechanism - but any advanced feature ( management, 
       * policy, etc ) should be implemented by a module.
       * 
       * @param listener
       */
      public void addModuleListener(ModuleListener listener) {
          this.listener=listener;
      }
  
      private static void log(String s) {
          System.err.println("Main: " + s);
      }
      
      private static void log( String msg, Throwable t ) {
          System.err.println("Main: " + msg);
          t.printStackTrace();
      }
  
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/LoaderProperties.java
  
  Index: LoaderProperties.java
  ===================================================================
      /*
   * Copyright 1999,2004 The Apache Software Foundation.
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *      http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  
  package org.apache.tomcat.util.loader;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.InputStream;
  import java.net.URL;
  import java.util.Enumeration;
  import java.util.Properties;
  
  
  /**
   * Load a properties file describing the modules and startup sequence.
   * 
   * The properties file will be named "loader.properties" or 
   * "catalina.properties" ( for backwad compatibility ) and
   * will be searched in:
   *  - TODO 
   * 
   * Properties used:
   *  - TODO
   *
   * loader.* and *.loader properties are used internally by the loader ( 
   *  *.loader is for backward compat with catalina ).
   * All other properties in the config file are set as System properties.
   * 
   *  
   * Based on o.a.catalina.bootstrap.CatalinaProperties - utility class to read 
   * the bootstrap Catalina configuration.
   *
   * @author Remy Maucherat
   * @author Costin Manolache
   */
  public class LoaderProperties {
  
      private static Properties properties = null;
  
      static {
  
          loadProperties();
  
      }
  
      private static boolean DEBUG= getProperty("debug.LoaderProperties")!=null;
      private static String propFile;
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Return specified property value.
       */
      public static String getProperty(String name) {
          return properties.getProperty(name);
      }
  
  
      /**
       * Return specified property value.
       */
      public static String getProperty(String name, String defaultValue) {
          return properties.getProperty(name, defaultValue);
      }
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Load properties.
       * Will try: 
       * - "catalina.config" system property ( a URL )
       * - "catalina.base", "catalina.home", "user.dir" system properties +
       *    "/conf/" "../conf" "/" +  "loader.properties" or "catalina.properties"
       * - /org/apache/catalina/startup/catalina.properties
       * 
       * Properties will be loaded as system properties. 
       * 
       * loader.properties was added to allow coexistence with bootstrap.jar ( the 
       * current scheme ), since the classpaths are slightly different. 
       */
      private static void loadProperties() {
  
          InputStream is = null;
          Throwable error = null;
  
          // TODO: paste the code to do ${} substitution 
          // TODO: add the code to detect where tomcat-properties is loaded from
          if( propFile != null ) {
              try {
                  File properties = new File(propFile);
                  is = new FileInputStream(properties);
                  if( is!=null && DEBUG ) {
                      log("Loaded from loader.properties " + properties );
                  }
              } catch( Throwable t) {
                  System.err.println("Can't find " + propFile);
                  return;
              }
          }
          
          if( is == null ) {
              try {
                  // "catalina.config" system property
                  String configUrl = System.getProperty("catalina.config");
                  if (configUrl != null) {
                      is = (new URL(configUrl)).openStream();
                      if( is!=null && DEBUG ) {
                          log("Loaded from catalina.config " + configUrl );
                      }
                  }
              } catch (Throwable t) {
                  // Ignore
              }
          }
  
          if (is == null) {
              try {
                  File home = new File(getCatalinaBase());
                  File conf = new File(home, "conf");
                  // use conf if exists, or the base directory otherwise
                  if( ! conf.exists() ) conf = new File(home, "../conf");
                  if( ! conf.exists() ) conf=home;
                  File properties = new File(conf, "loader.properties");
                  is = new FileInputStream(properties);
                  if( is!=null && DEBUG ) {
                      log("Loaded from loader.properties " + properties );
                  }
              } catch (Throwable t) {
                  // Ignore
              }
          }
  
          if (is == null) {
              try {
                  File home = new File(getCatalinaBase());
                  File conf = new File(home, "conf");
                  File properties = new File(conf, "catalina.properties");
                  is = new FileInputStream(properties);
                  if( is!=null && DEBUG ) {
                      log("Loaded from catalina.properties " + properties );
                  }
              } catch (Throwable t) {
                  // Ignore
              }
          }
  
          if (is == null) {
              try {
                  is = LoaderProperties.class.getResourceAsStream
                      ("/org/apache/catalina/startup/catalina.properties");
                  if( is!=null && DEBUG ) {
                      log("Loaded from o/a/c/startup/catalina.properties " );
                  }
  
              } catch (Throwable t) {
                  // Ignore
              }
          }
  
          if (is == null) {
              try {
                  is = LoaderProperties.class.getResourceAsStream
                      ("/org/apache/tomcat/util/loader/loader.properties");
                  if( is!=null && DEBUG ) {
                      log("Loaded from o/a/t/u/loader/loader.properties " );
                  }
              } catch (Throwable t) {
                  // Ignore
              }
          }
          
          properties = new Properties();
          
          if (is != null) {
              try {
                  properties.load(is);
                  is.close();
              } catch (Throwable t) {
                  error = t;
              }
          }
  
          if ((is == null) || (error != null)) {
              // Do something
              log("Error: no properties found !!!");
          }
  
          // Register the _unused_ properties as system properties
          if( properties != null ) {
              Enumeration enumeration = properties.propertyNames();
              while (enumeration.hasMoreElements()) {
                  String name = (String) enumeration.nextElement();
                  String value = properties.getProperty(name);
                  if( "security.preload".equals( name )) continue;
                  if( "package.access".equals( name )) continue;
                  if( "package.definition".equals( name )) continue;
                  if( name.endsWith(".loader")) continue;
                  if( name.startsWith("loader.")) continue;
                  if (value != null) {
                      System.setProperty(name, value);
                  }
              }
          }
  
      }
  
  
      /**
       * Get the value of the catalina.home environment variable.
       */
      static String getCatalinaHome() {
          return System.getProperty("catalina.home",
                                    System.getProperty("user.dir"));
      }
      
      
      /**
       * Get the value of the catalina.base environment variable.
       */
      static String getCatalinaBase() {
          return System.getProperty("catalina.base", getCatalinaHome());
      }
  
      
      /**
       * Set the <code>catalina.base</code> System property to the current
       * working directory if it has not been set.
       */
      static void setCatalinaBase() {
  
          if (System.getProperty("catalina.base") != null)
              return;
          if (System.getProperty("catalina.home") != null)
              System.setProperty("catalina.base",
                                 System.getProperty("catalina.home"));
          else
              System.setProperty("catalina.base",
                                 System.getProperty("user.dir"));
  
      }
  
  
      /**
       * Set the <code>catalina.home</code> System property to the current
       * working directory if it has not been set.
       */
      static void setCatalinaHome() {
  
          if (System.getProperty("catalina.home") != null)
              return;
          File bootstrapJar = 
              new File(System.getProperty("user.dir"), "bootstrap.jar");
          File tloaderJar = 
              new File(System.getProperty("user.dir"), "tomcat-loader.jar");
          if (bootstrapJar.exists() || tloaderJar.exists()) {
              try {
                  System.setProperty
                      ("catalina.home", 
                       (new File(System.getProperty("user.dir"), ".."))
                       .getCanonicalPath());
              } catch (Exception e) {
                  // Ignore
                  System.setProperty("catalina.home",
                                     System.getProperty("user.dir"));
              }
          } else {
              System.setProperty("catalina.home",
                                 System.getProperty("user.dir"));
          }
  
      }
  
  
      private static void log(String s ) { 
          System.err.println("LoaderProperties: "+ s);
      }
  
  
      /**
       * @param props
       */
      public static void setPropertiesFile(String props) {
          propFile=props;
          loadProperties();
      }
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/Module.java
  
  Index: Module.java
  ===================================================================
  /*
   * Copyright 1999,2004 The Apache Software Foundation.
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *      http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  
  package org.apache.tomcat.util.loader;
  
  
  import java.io.File;
  import java.io.Serializable;
  import java.lang.reflect.Constructor;
  import java.net.URL;
  
  // Based on org.apache.catalina.Loader - removed most of the catalina-specific
  
  /**
   *  Base representation for "server extensions" ( connectors, realms, etc ), webapps,
   * libraries.   
   * 
   * 
   * @author Costin Manolache
   * 
   * @author Craig R. McClanahan
   * @author Remy Maucherat
   */
  public class Module implements Serializable {
  
      // ----------------------------------------------------------- Constructors
  
  
      /**
       * 
       */
      public Module() {
      }
  
  
      // ----------------------------------------------------- Instance Variables
  
      private static final boolean DEBUG=false; //LoaderProperties.getProperty("loader.Module.debug") != null;
  
      /**
       * The class loader being managed by this Loader component.
       */
      private transient ModuleClassLoader classLoader = null;
  
      /**
       * The "follow standard delegation model" flag that will be used to
       * configure our ClassLoader.
       */
      private boolean delegate = false;
  
      private Class classLoaderClass;
  
      /**
       * The Java class name of the ClassLoader implementation to be used.
       * This class should extend ModuleClassLoader, otherwise, a different 
       * loader implementation must be used.
       */
      private String loaderClass = null;
  //        "org.apache.catalina.loader.WebappClassLoader";
  
      /**
       * The parent class loader of the class loader we will create.
       * Use Repository if the parent is also a repository, otherwise set 
       * the ClassLoader
       */
      private transient ClassLoader parentClassLoader = null;
      private Repository parent;
  
      private Repository repository;
  
      /**
       * The set of repositories associated with this class loader.
       */
      private String repositories[] = new String[0];
      private URL classpath[] ;
  
      private File workDir;
  
      /**
       * Has this component been started?
       */
      private boolean started = false;
  
      boolean hasIndex=false;
  
      // ------------------------------------------------------------- Properties
  
  
      /**
       * Return the Java class loader to be used by this Container.
       */
      public ClassLoader getClassLoader() {
          return classLoader;
      }
  
  
      /**
       * Return the "follow standard delegation model" flag used to configure
       * our ClassLoader.
       */
      public boolean getDelegate() {
          return (this.delegate);
      }
  
  
      /**
       * Set the "follow standard delegation model" flag used to configure
       * our ClassLoader.
       *
       * @param delegate The new flag
       */
      public void setDelegate(boolean delegate) {
          boolean oldDelegate = this.delegate;
          this.delegate = delegate;
          if( classLoader != null ) classLoader.setDelegate(delegate);
      }
  
  
      // --------------------------------------------------------- Public Methods
  
      /**
       * Has the internal repository associated with this Loader been modified,
       * such that the loaded classes should be reloaded?
       */
      public boolean modified() {
          return (classLoader.modified());
      }
      
      public boolean isStarted() {
          return started;
      }
  
      public String getClasspathString() {
          if(classpath==null ) {
              return null;
          }
          StringBuffer sb=new StringBuffer();
          for( int i=0; i<classpath.length; i++ ) {
              if( i>0 ) sb.append(":");
              sb.append( classpath[i].getFile() );
          }
          return sb.toString();
      }
      
      /**
       * Start this component, initializing our associated class loader.
       *
       * @exception LifecycleException if a lifecycle error occurs
       */
      public void start()  {
          // Validate and update our current component state
          if (started)
              throw new RuntimeException
                  ("Already started");
          started = true;
  
          // Construct a class loader based on our current repositories list
          try {
  
              classLoader = createClassLoader();
  
              //classLoader.setResources(container.getResources());
              classLoader.setDelegate(this.delegate);
  
              for (int i = 0; i < repositories.length; i++) {
                  classLoader.addRepository(repositories[i]);
              }
  
              classLoader.start();
  
              getRepository().getLoader().listener.moduleStart(this);
  
          } catch (Throwable t) {
              log( "LifecycleException ", t );
              throw new RuntimeException("start: ", t);
          }
  
      }
  
  
      /**
       * Stop this component, finalizing our associated class loader.
       *
       * @exception LifecycleException if a lifecycle error occurs
       */
      public void stop() {
          if (!started)
              throw new RuntimeException("stop: started=false");
          
          //if (DEBUG) 
          log("stop()", null);
          
          getRepository().getLoader().listener.moduleStop(this);
          
          started = false;
  
          // unregister this classloader in the server group
          if( repository != null ) repository.removeClassLoader(classLoader);
  
          // Throw away our current class loader
          classLoader.stop();
  
          classLoader = null;
  
      }
  
      // ------------------------------------------------------- Private Methods
  
      /** Set the class used to construct the class loader.
       * 
       * The alternative is to set the context class loader to allow loaderClass
       * to be created. 
       * 
       * @param c
       */
      public void setClassLoaderClass( Class c ) {
          classLoaderClass=c;
      }
  
      /**
       * Create associated classLoader.
       */
      ModuleClassLoader createClassLoader()
          throws Exception 
      {
  
          if( classLoader != null ) return classLoader;
          if( classLoaderClass==null && loaderClass!=null) {
              classLoaderClass = Class.forName(loaderClass);
          }
          
          ModuleClassLoader classLoader = null;
  
          if (parentClassLoader == null) {
              if( parent != null ) {
                  parentClassLoader = parent.getClassLoader();
              }
          }
          if (parentClassLoader == null) {
              parentClassLoader = Thread.currentThread().getContextClassLoader();
          }
          
          if( classLoaderClass != null ) {
              Class[] argTypes = { URL[].class, ClassLoader.class };
              Object[] args = { classpath, parentClassLoader };
              Constructor constr = classLoaderClass.getConstructor(argTypes);
              classLoader = (ModuleClassLoader) constr.newInstance(args);
          } else {
              classLoader=new ModuleClassLoader( classpath, parentClassLoader);
          }
          
          classLoader.setModule(this);
          classLoader.setDelegate( delegate );
          
          classLoader.start();
          repository.addClassLoader(classLoader);
          
          return classLoader;
      }
  
  
      /**
       * @param parent
       */
      public void setParent(Repository parent) {
          this.parent=parent;
      }
  
      /**
       * @param array
       */
      public void setClasspath(URL[] array) {
          this.classpath=array;
      }
  
  
      /**
       * @param lg
       */
      public void setRepository(Repository lg) {
          this.repository=lg;
      }
  
      /**
       * Return a String representation of this component.
       */
      public String toString() {
  
          StringBuffer sb = new StringBuffer("ModuleLoader[");
          sb.append(getClasspathString());
          sb.append("]");
          return (sb.toString());
  
      }
  
      private void log( String s ) {
          log(s,null);
      }
      
      private void log(String s, Throwable t ) {
          System.err.println("Module: " + s );
          if( t!=null)
              t.printStackTrace();
      }
  
  
      /**
       * @return
       */
      public Repository getRepository() {
          return repository;
      }
  
  
      /**
       * @return
       */
      public String getName() {
          return classpath[0].getFile(); // this.toString();
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/ModuleClassLoader.java
  
  Index: ModuleClassLoader.java
  ===================================================================
  /*
   * Copyright 1999,2004 The Apache Software Foundation.
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *      http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  
  package org.apache.tomcat.util.loader;
  
  import java.io.File;
  import java.io.IOException;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.net.URLClassLoader;
  import java.util.Enumeration;
  import java.util.Vector;
  
  /*
   * Initially, I started with WebappClassLoader attempting to clean up and
   * refactor it. Because of complexity and very weird ( and likely buggy ) 
   * behavior, I moved the other way, starting with URLClassLoader and adding
   * the functionality from WebappClassLoader. 
   * 
   * The current version has a lot of unimplemented WebappClassLoader features and
   * TODOs - all of them are needed in order to support a single/consistent loader
   * for webapps and server/modules. 
   * 
   * - all ordering options and tricks
   * - local loading - in case it can be made more efficient than URLCL
   * - hook to plugin JNDI finder
   * - ability to add extra permissions to loaded classes
   * - ability to use work dir for anti-jar locking and generated classes ( incl getURLs)
   * 
   * 
   * Things better kept out:
   *  - negative cache - it'll waste space with little benefit, most not found classes
   *  will not be asked multiple times, and most will be in other loaders
   *  - binaryContent cache - it's unlikely same resource will be loaded multiple
   * times, and some may be large  
   * 
   */
  
  /**
   * Simple module class loader. Will search the repository if the class is not
   * found locally.
   * 
   * TODO: findResources() - merge all responses from the repo and parent. 
   *
   * Based on StandardClassLoader and WebappClassLoader.
   *   
   * @author Costin Manolache
   * @author Remy Maucherat
   * @author Craig R. McClanahan
   */
  public class ModuleClassLoader
      extends URLClassLoader
  {
      // Don't use commons logging or configs to debug loading - logging is dependent
      // on loaders and drags a lot of stuff in the classpath 
      //
      private static final boolean DEBUG=false; //LoaderProperties.getProperty("loader.ModuleClassLoader.debug") != null;
      private static final boolean DEBUGNF=false;//LoaderProperties.getProperty("loader.ModuleClassLoader.debugNF") != null;
      
      // ----------------------------------------------------------- Constructors
  
  
      public ModuleClassLoader(URL repositories[], ClassLoader parent) {
          super(repositories, parent);
      }
      
  
      public ModuleClassLoader(URL repositories[]) {
          super(repositories);
      }
  
  
      // ----------------------------------------------------- Instance Variables
  
      protected Repository repository;
  
      /**
       * Should this class loader delegate to the parent class loader
       * <strong>before</strong> searching its own repositories (i.e. the
       * usual Java2 delegation model)?  If set to <code>false</code>,
       * this class loader will search its own repositories first, and
       * delegate to the parent only if the class or resource is not
       * found locally.
       */
      protected boolean delegate = false;
  
      /**
       * Last time a JAR was accessed. 
       * TODO: change to last time the loader was accessed
       */
      protected long lastJarAccessed = 0L;
  
      /**
       * Has this component been started?
       */
      protected boolean started = false;
  
      protected Module module;
  
      // ------------------------------------------------------------- Properties
  
  
      /**
       * Return the "delegate first" flag for this class loader.
       */
  //    boolean getDelegate() {
  //
  //        return (this.delegate);
  //
  //    }
  
  
      /**
       * Set the "delegate first" flag for this class loader.
       *
       * @param delegate The new "delegate first" flag
       */
      void setDelegate(boolean delegate) {
          this.delegate = delegate;
      }
  
      void setRepository(Repository lg ) {
          this.repository=lg;
      }
  
      void setModule(Module webappLoader) {
          this.module=webappLoader;
      }
  
      /** Not public - his can only be called from package.
       *  To get the module from a ClassLoader you need access to the Loader
       * instance.
       * 
       * @return
       */
      Module getModule() {
          return module;
      }
  
      void setWorkDir(File s) {
          // TODO
      }
  
      /**
       * 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
       */
      void addRepository(String repository) {
          // Add this repository to our underlying class loader
          try {
              URL url = new URL(repository);
              super.addURL(url);
          } catch (MalformedURLException e) {
              IllegalArgumentException iae = new IllegalArgumentException
                  ("Invalid repository: " + repository); 
              iae.initCause(e);
              //jdkCompat.chainException(iae, e);
              throw iae;
          }
      }
  
      /**
       * Have one or more classes or resources been modified so that a reload
       * is appropriate?
       * 
       * Not public - call it via Module
       */
      boolean modified() {
          if (DEBUG)
              log("modified() false");
  
          // TODO - check at least the jars 
          return (false);
      }
  
      // ---------------------------------------------------- ClassLoader Methods
  
  
      /**
       * Find the specified class in our local repositories, if possible.  If
       * not found, throw <code>ClassNotFoundException</code>.
       *
       * @param name Name of the class to be loaded
       *
       * @exception ClassNotFoundException if the class was not found
       */
      public Class findClass(String name) throws ClassNotFoundException {
  
          Class clazz = null;
              
          try {
              clazz = super.findClass(name);
          } catch (RuntimeException e) {
              if (DEBUG)
                  log("findClass() -->RuntimeException " + name, e);
              throw e;
          } catch( ClassNotFoundException ex ) {
              if (DEBUGNF)
                  log("findClass() NOTFOUND  " + name);
              throw ex;
          }
              
          if (clazz == null) { // does it ever happen ? 
              if (DEBUGNF)
                  log("findClass() NOTFOUND throw CNFE " + name);
              throw new ClassNotFoundException(name);
          }
  
          // Return the class we have located
          if (DEBUG) {
              if( clazz.getClassLoader() != this ) 
                  log("findClass() FOUND " + clazz + " Loaded by " + clazz.getClassLoader());
              else 
                  log("findClass() FOUND " + clazz );
          }
          return (clazz);
      }
      
      /** Same as findClass, but also checks if the class has been previously 
       * loaded.
       * 
       * In most implementations, findClass() doesn't check with findLoadedClass().
       * In order to implement repository, we need to ask each loader in the group
       * to load only from it's local resources - however this will lead to errors
       * ( duplicated definition ) if findClass() is used.
       *
       * @param name
       * @return
       * @throws ClassNotFoundException
       */
      public Class findLocalClass(String name) throws ClassNotFoundException
      {
          Class clazz = findLoadedClass(name);
          if (clazz != null) {
              if (DEBUG)
                  log("findLocalClass() - FOUND " + name);
              return (clazz);
          }
          
          return findClass(name);
      }
  
  
  
      
      /**
       * Find the specified resource in our local repository, and return a
       * <code>URL</code> refering to it, or <code>null</code> if this resource
       * cannot be found.
       *
       * @param name Name of the resource to be found
       */
      public URL findResource(final String name) {
  
          URL url = null;
  
          url = super.findResource(name);
          
          if(url==null) {
              // try the repository
              // TODO
          }
          
          if (url==null && DEBUG) {
              if (DEBUGNF) log("findResource() NOTFOUND " + name );
              return null;
          }
  
          if (DEBUG) log("findResource() found " + name + " " + url );
          return (url);
      }
  
  
      /**
       * Return an enumeration of <code>URLs</code> representing all of the
       * resources with the given name.  If no resources with this name are
       * found, return an empty enumeration.
       *
       * @param name Name of the resources to be found
       *
       * @exception IOException if an input/output error occurs
       */
      public Enumeration findResources(String name) throws IOException {
          Vector result=new Vector();
          
          Enumeration myRes=super.findResources(name);
          if( myRes!=null ) {
              while( myRes.hasMoreElements() ) {
                  result.addElement(myRes.nextElement());
              }
          }
          // TODO: same in repository ( parent ? ) 
          
          return result.elements();
  
      }
  
      // Next methods implement the search alghoritm - parent, repo, delegation, etc 
  
      /** getResource() - modified to implement the search alghoritm 
       * 
       */
      public URL getResource(String name) {
  
          URL url = null;
  
          // (1) Delegate to parent if requested
          if (delegate) {
              url=getResourceParentDelegate(name);
              if(url!=null ) return url;
          }
  
          // (2) Search local repositories
          url = findResource(name);
          if (url != null) {
              // TODO: antijar locking - WebappClassLoader is making a copy ( is it ??)
              if (DEBUG)
                  log("getResource() found locally " + delegate + " " + name + " " + url);
              return (url);
          }
  
          // Finally, try the group loaders ( via super() in StandardClassLoader ).
          // not found using normal loading mechanism. load from one of the classes in the group
          if( repository!=null ) {
              url=repository.findResource(this, name);
              if(url!=null ) {
                  if( DEBUG )
                      log("getResource() FOUND from group " + repository.getName() + " " + name + " " + url);
                  return url;
              }
          }
  
          // (3) Delegate to parent unconditionally if not already attempted
          if( !delegate ) {
              url=getResourceParentDelegate(name);
              if(url!=null ) return url;
          }
  
          
          // (4) Resource was not found
          if (DEBUGNF)
              log("getResource() NOTFOUND  " + delegate + " " + name + " " + url);
          return (null);
  
      }
  
      // to avoid duplication
      private URL getResourceParentDelegate(String name) {
          URL url=null;
          ClassLoader loader = getParent();
          
          if (loader == null) {
              loader = getSystemClassLoader();
              if (url != null) {
                  if (DEBUG)
                      log("getResource() found by system " +  delegate + " " + name + " " + url);
                  return (url);
              }
          } else {
              url = loader.getResource(name);
              if (url != null) {
                  if (DEBUG)
                      log("getResource() found by parent " +  delegate + " " + name + " " + url);
                  return (url);
              }
          }
  
          return url;
      }
      
      /**
       * Load the class with the specified name, searching using the following
       * algorithm until it finds and returns the class.  If the class cannot
       * be found, returns <code>ClassNotFoundException</code>.
       * <ul>
       * <li>Call <code>findLoadedClass(String)</code> to check if the
       *     class has already been loaded.  If it has, the same
       *     <code>Class</code> object is returned.</li>
       * <li>If the <code>delegate</code> property is set to <code>true</code>,
       *     call the <code>loadClass()</code> method of the parent class
       *     loader, if any.</li>
       * <li>Call <code>findClass()</code> to find this class in our locally
       *     defined repositories.</li>
       * <li>Call the <code>loadClass()</code> method of our parent
       *     class loader, if any.</li>
       * </ul>
       * If the class was found using the above steps, and the
       * <code>resolve</code> flag is <code>true</code>, this method will then
       * call <code>resolveClass(Class)</code> on the resulting Class object.
       *
       * @param name Name of the class to be loaded
       * @param resolve If <code>true</code> then resolve the class
       *
       * @exception ClassNotFoundException if the class was not found
       */
      public Class loadClass(String name, boolean resolve)
          throws ClassNotFoundException
      {
  
          Class clazz = null;
  
          // Don't load classes if class loader is stopped
          if (!started) {
              //log("Not started " + this + " " + module);
              throw new ThreadDeath();
          }
  
          // (0) Check our previously loaded local class cache
          clazz = findLoadedClass(name);
          if (clazz != null) {
              if (DEBUG)
                  log("loadClass() FOUND findLoadedClass " + name + " , " + resolve);
              if (resolve) resolveClass(clazz);
              return (clazz);
          }
  
          // (0.2) Try loading the class with the system class loader, to prevent
          //       the webapp from overriding J2SE classes
          try {
              clazz = getSystemClassLoader().loadClass(name);
              if (clazz != null) {
                  // enabling this can result in ClassCircularityException
  //                if (DEBUG)
  //                    log("loadClass() FOUND system " + name + " , " + resolve);
                  if (resolve) resolveClass(clazz);
                  return (clazz);
              }
          } catch (ClassNotFoundException e) {
              // Ignore
          }
  
          // TODO: delegate based on filter
          boolean delegateLoad = delegate;// || filter(name);
  
          // (1) Delegate to our parent if requested
          if (delegateLoad) {
  
              ClassLoader loader = getParent();
              if( loader != null ) {
                  try {
                      clazz = loader.loadClass(name);
                      if (clazz != null) {
                          if (DEBUG)
                              log("loadClass() FOUND by parent " + delegate + " " + name + " , " + resolve);
                          if (resolve)
                              resolveClass(clazz);
                          return (clazz);
                      }
                  } catch (ClassNotFoundException e) {
                      ;
                  }
              }
          }
  
          // (2) Search local repositories
          try {
              clazz = findClass(name);
              if (clazz != null) {
  //                if (DEBUG)
  //                    log("loadClass - FOUND findClass " + delegate + " " + name + " , " + resolve);
                  if (resolve) resolveClass(clazz);
                  return (clazz);
              }
          } catch (ClassNotFoundException e) {
              ;
          }
  
          // Finally, try the group loaders ( via super() in StandardClassLoader ).
          // not found using normal loading mechanism. load from one of the classes in the group
          if( repository!=null ) {
              Class cls=repository.findClass(this, name);
              if(cls!=null ) {
                  if( DEBUG )
                      log("loadClass(): FOUND from group " + repository.getName() + " " + name);
                  if (resolve) resolveClass(clazz);
                  return cls;
              }
          }
  
          // (3) Delegate to parent unconditionally
          if (!delegateLoad) {
              ClassLoader loader = getParent();
              if( loader != null ) {
                  try {
                      clazz = loader.loadClass(name);
                      if (clazz != null) {
                          if (DEBUG)
                              log("loadClass() FOUND parent " + delegate + " " + name + " , " + resolve);
                          if (resolve) resolveClass(clazz);
                          return (clazz);
                      }
                  } catch (ClassNotFoundException e) {
                      ;
                  }
              }
          }
  
          if( DEBUGNF ) log("loadClass(): NOTFOUND " + name );
          throw new ClassNotFoundException(name);
      }
  
  
      // ------------------------------------------------------ Lifecycle Methods
  
  
  
      /**
       * Start the class loader.
       *
       * @exception LifecycleException if a lifecycle error occurs
       */
      void start()  {
  
          started = true;
  
      }
  
      /** Support for "disabled" state.
      *
      * @return
      */
      boolean isStarted() {
          return started;
      }
  
  
      /**
       * Stop the class loader.
       *
       * @exception LifecycleException if a lifecycle error occurs
       */
      void stop() {
  
          started = false;
  
      }
  
  
  
  
      /**
       * Validate a classname. As per SRV.9.7.2, we must restict loading of 
       * classes from J2SE (java.*) and classes of the servlet API 
       * (javax.servlet.*). That should enhance robustness and prevent a number
       * of user error (where an older version of servlet.jar would be present
       * in /WEB-INF/lib).
       * 
       * @param name class name
       * @return true if the name is valid
       */
      protected boolean validate(String name) {
  
          if (name == null)
              return false;
          if (name.startsWith("java."))
              return false;
  
          return true;
  
      }
  
  
      // ------------------ Local methods ------------------------
  
      private void log(String s ) {
          System.err.println("ModuleCL: " + s);
      }
      private void log(String s, Throwable t ) {
          System.err.println("ModuleCL: " + s);
          t.printStackTrace();
      }
      
      Object debugObj=new Object();
  
      /**
       * Render a String representation of this object.
       */
      public String toString() {
  
          StringBuffer sb = new StringBuffer("ModuleCL ");
          sb.append(debugObj).append(" delegate: ");
          sb.append(delegate);
          //sb.append("\r\n");
          sb.append(" cp: ");
          URL cp[]=super.getURLs();
          if (cp != null ) {
              for (int i = 0; i <cp.length; i++) {
                  sb.append("  ");
                  sb.append(cp[i].getFile());
              }
          }
          if (getParent() != null) {
              sb.append("\r\n----------> Parent: ");
              sb.append(getParent().toString());
              sb.append("\r\n");
          }
          return (sb.toString());
      }
  }
  
  
  
  
  1.1                  jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/ModuleListener.java
  
  Index: ModuleListener.java
  ===================================================================
  /*
   * Copyright 1999,2004 The Apache Software Foundation.
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *      http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  
  package org.apache.tomcat.util.loader;
  
  public interface ModuleListener {
      
      /** Called when a module group is created. This is only called when a new group
       * is added to a running engine - we may cache a the list of groups and reuse 
       * it on restarts. 
       * 
       * @param manager
       */
      public void repositoryAdd( Repository manager );
      
      /** Notification that a module has been added. You can get the group
       * with getGroup().
       * 
       * Adding a module doesn't imply that the module is started or the class loader
       * created - this happens only on start() ( TODO: or when a class is accessed ? ).
       * 
       * This callback is only called for new modules, deployed while the engine is 
       * running, or in some cases when the module engine is reseting the cache. For
       * old modules - you need to get a list from the ModuleGroup.
       * 
       * 
       * @param module
       */
      public void moduleAdd( Module module );
      
      /** Notification that a module has been removed.
       * 
       * @param module
       */
      public void moduleRemove( Module module );
      
      /** Module reload - whenever reload happens, a reload notification will be generated.
       * Sometimes a remove/add will do the same.
       * 
       * @param module
       */
      public void moduleReload( Module module );
      
      /** Called when a module is started. 
       * 
       * This is called after the class loader is created, to allow listeners to use classes
       * from the module to initialize.
       * 
       * I think it would be good to have the module 'started' on first class loaded
       * and 'stopped' explicitely.
       * 
       * @param module
       */
      public void moduleStart(Module module);
      
      /** 
       *  Called when a module is stopped. Stoping a module will stop the class
       * loader and remove all references to it. When a module is stopped, all attempts
       * to load classes will result in exceptions. 
       * 
       * The callback is called before the class loader is stopped - this allows listeners
       * to use classes from the module to deinitialize.
       * 
       * @param module
       */
      public void moduleStop(Module module);
      
      /** Pass a reference to the loader. 
       * 
       * This is the only supported way to get it - no static methods are 
       * provided. From loader you can control all repositories and modules.
       * 
       * Note that ModuleClassLoader does not provide a way to retrieve the Module -
       * you need to have a reference to the Loader to get the Module for a 
       * ClassLoader. 
       * @param main
       */
      public void setLoader(Loader main);
  
      /** Start the listener.
       * TODO: this is only used by Loader to pass control to the listener - 
       * instead of introspection for main()
       */
      public void start();
  
  }
  
  
  1.1                  jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/Repository.java
  
  Index: Repository.java
  ===================================================================
  /*
   * Copyright 1999,2004 The Apache Software Foundation.
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *      http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  
  package org.apache.tomcat.util.loader;
  
  
  import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.net.URL;
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.Vector;
  
  
  /**
   * A group of modules and libraries. 
   * 
   * Modules can have one or more jars and class dirs. Classloaders are created 
   * from modules when the module is started are be disposed when the module is stopped.
   * 
   * The module will delegate to the associated repository in addition to the 
   * normal delegation rules. The repository will search on all sibling modules.
   * This mechanism is defined in the MLetClassLoader and is also used by JBoss and
   * few other servers. 
   * 
   * TODO: explain more ( or point to the right jboss/mlet pages )
   * TODO: explain how this can be used for webapps to support better partitioning 
   *
   * @author Costin Manolache
   */
  public class Repository {
     
      private static final boolean DEBUG=true; //LoaderProperties.getProperty("loader.Repository.debug") != null;
      
      // Allows the (experimental) use of jar indexes
      // Right now ( for small set of jars, incomplete build ) it's a tiny 3.5 -> 3.4 sec dif.
      private static final boolean USE_IDX=true;// //LoaderProperties.getProperty("loader.Repository.useIdx") != null;
      
      private Vector loaders=new Vector();
      private String name;
      private Vector grpModules=new Vector();
      private transient Loader loader;
      
      private Repository parent;
  
      private transient ModuleClassLoader groupClassLoader;
      private Hashtable prefixes=new Hashtable();
  
      public Repository() {
      }
  
      public Repository(Loader loader) {
          if( loader== null ) throw new NullPointerException();
          this.loader=loader;
      }
  
      public Loader getLoader() {
          return loader;
      }
      
      public void addModule(  Module mod ) {
          mod.setRepository( this );
  
          grpModules.addElement(mod);
          if( loader.listener!=null ) {
              loader.listener.moduleAdd(mod);
          }
  
          if(! mod.isStarted()) {
              mod.start();
              //log("started " + mod);
          } else {
              //log("already started " + mod);
          }
          
          try {
              if( USE_IDX ) {
                  processJarIndex(mod);
                  writeCacheIdx();
              }
          } catch (Exception e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
          
      }
      
      public Enumeration getModules() {
          return grpModules.elements();
      }
      
      public Repository getParent() {
          return parent;
      }
  
      /**
       * 
       * @param parent The parent group
       */
      public void setParent(Repository parent) {
          this.parent = parent;
      }
  
      /** Add a class loder to the group.
       *
       *  If this is a StandardClassLoader instance, it will be able to delegate
       * to the group.
       *
       *  If it's a regular ClassLoader - it'll be searched for classes, but
       * it will not be able to delegate to peers.
       *
       * In future we may fine tune this by using manifests.
       */
      void addClassLoader(ClassLoader cl ) {
          if( ( cl instanceof ModuleClassLoader )) {
              ((ModuleClassLoader)cl).setRepository(this);
          }
          loaders.addElement(cl);
          //    log("Adding classloader " + cl);
      }
  
      public String getName() {
          return name;
      }
  
      public void removeClassLoader(ClassLoader cl) {
          int oldSize=loaders.size();
          loaders.removeElement(cl);
  
          if(DEBUG) log("removed " + loaders.size() + "/" + oldSize + ": "  + cl);
      }
  
      /** Return a class loader associated with the group.
       *  This will delegate to all modules in the group, then to parent.
       * 
       * @return
       */
      public ClassLoader getClassLoader() {
          if( groupClassLoader==null ) {
              if( parent == null ) {
                  groupClassLoader=new ModuleClassLoader(new URL[0]);
              } else {
                  groupClassLoader=new ModuleClassLoader(new URL[0], parent.getClassLoader());
              }
              groupClassLoader.start();
              groupClassLoader.setRepository(this);
          }
          return groupClassLoader;
      }
      /** 
       * Find a class in the group. It'll iterate over each loader
       * and try to find the class - using only the method that
       * search locally or on parent ( i.e. not in group, to avoid
       * recursivity ).
       *
       *
       * @param classN
       * @return
       */
      public Class findClass(ClassLoader caller, String classN ) {
          Class clazz=null;
          
          // do we have it in index ?
          if( USE_IDX ) {
          int lastIdx=classN.lastIndexOf(".");
          String prefix=(lastIdx>0) ? classN.substring(0, lastIdx) : classN;
          Object mO=prefixes.get(prefix.replace('.', '/'));
          if( mO!=null ) {
              if( mO instanceof Module ) {
                  Module m=(Module)mO;
                  try {
                      Class c=((ModuleClassLoader)m.getClassLoader()).findLocalClass(classN);
                      //log("Prefix: " +prefix + " " + classN  + " " + m);
                      return c;
                  } catch (Exception e) {
                      //log("Prefix err: " +prefix + " " + classN  + " " + m + " " + e);
                      //return null;
                  }
              } else {
                  Module mA[]=(Module[])mO;
                  for( int i=0; i<mA.length; i++ ) {
                      Module m=mA[i];
                      try {
                          Class c=((ModuleClassLoader)m.getClassLoader()).findLocalClass(classN);
                          //log("Prefix: " +prefix + " " + classN  + " " + m);
                          return c;
                      } catch (Exception e) {
                          //log("Prefix err: " +prefix + " " + classN  + " " + m + " " + e);
                          //return null;
                      }
                  }
              }
          }
          }
  
          // TODO: move the vector to a []
          for( int i=loaders.size()-1; i>=0; i-- ) {
              
              // TODO: for regular CL, just use loadClass, they'll not recurse
              // The behavior for non-SCL or not in the group loader is the same as for parent loader
              ModuleClassLoader cl=(ModuleClassLoader)loaders.elementAt(i);
              // TODO: move loaders with index in separate vector
              //if( cl.getModule().hasIndex ) continue;
              if( cl== caller ) continue;
              //if( classN.indexOf("SmtpCoyoteProtocolHandler") > 0 ) {
              //log("try " + cl.debugObj + " " + name + " " + classN + " " + loaders.size());
              //}
              try {
                  if( cl instanceof ModuleClassLoader ) {
                      clazz=((ModuleClassLoader)cl).findLocalClass(classN );
                  } else {
                      clazz=cl.findClass(classN);
                  }
  
                  //System.err.println("GRPLD: " + classN + " from " + info.get(cl));
                  return clazz;
              } catch (ClassNotFoundException e) {
                  //System.err.println("CNF: " + classN + " " + info.get(cl) );
                  //if( classN.indexOf("smtp") > 0 ) e.printStackTrace();
              }
          }
          return null;
      }
      
      /**
       * @param loader
       * @param name2
       * @return
       */
      public URL findResource(ModuleClassLoader caller, String classN) {
          URL url=null;
          
          for( int i=loaders.size()-1; i>=0; i-- ) {
              // TODO: for regular CL, just use loadClass, they'll not recurse
              // The behavior for non-SCL or not in the group loader is the same as for parent loader
              ModuleClassLoader cl=(ModuleClassLoader)loaders.elementAt(i);
              if( cl== caller ) continue;
              url=((ModuleClassLoader)cl).findResource(classN );
              if( url!=null )
                  return url;
          }
          return null;
      }
  
      private void log(String s) {
          System.err.println("Repository (" + name + "): " + s );
      }
  
      /**
       * @param name2
       */
      public void setName(String name2) {
          this.name=name2;
      }
  
      /*
       * Work in progress: 
       * 
       * -use the INDEX.LIST to get prefixes to avoid linear
       * search in repositories.
       * 
       * - serialize the state ( including timestamps ) to improve startup time
       * ( avoids the need to open all jars - if INDEX.LIST is ok)
       */
      
      /**
       * Read the index. The index contain packages and top level resources
       * 
       * @param cl
       * @throws Exception
       */
      private void processJarIndex(Module m) throws Exception {
          ModuleClassLoader cl=(ModuleClassLoader)m.createClassLoader();
          // only support index for modules with a single jar in CP
          String cp=m.getClasspathString();
          if( ! cp.endsWith(".jar")) return;
          URL urlIdx=cl.findResource("META-INF/INDEX.LIST");
          if( urlIdx == null ) {
              log("INDEX.LIST not found, run: jar -i " + m.getClasspathString());
              return;
          }
          try {
              InputStream is=urlIdx.openStream();
              if( is==null ) {
                  log("Can't read " + urlIdx + " " + m.getClasspathString());
                  return;
              }
              BufferedReader br=new BufferedReader( new InputStreamReader(is) );
              String line=br.readLine();
              if( line==null ) return;
              if( ! line.startsWith( "JarIndex-Version:") ||  
                      ! line.endsWith("1.0")) {
                  log("Invalid signature " + line + " " + m.getClasspathString());
              }
              br.readLine(); // ""
              
              while( readSection(br, m) ) {
              }
             
              m.hasIndex=true;
          } catch (IOException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
      }
      
      private boolean readSection( BufferedReader br, Module m) throws IOException {
          String jarName=br.readLine();
          if( jarName==null ) return false; // done
          if( "".equals( jarName )) {
              log("Invalid jarName " + jarName + " " + m.getClasspathString() );
              return false;
          }
          //log("Index " + jarName + " " + m.getClasspathString());
          String prefix=null;
          while( ((prefix=br.readLine()) != null ) && 
                  (! "".equals( prefix )) ) {
              //log("found " + prefix + " " + m);
              Object o=prefixes.get(prefix);
              if( o == null ) {
                  prefixes.put(prefix, m);
              } else {
                  Module mA[]=null;
                  if( o instanceof Module ) {
                      mA=new Module[2];
                      mA[0]=(Module)o;
                      mA[1]=m;
                  } else {
                      Object oldA[]=(Module[])o;
                      mA=new Module[oldA.length + 1];
                      System.arraycopy(oldA, 0, mA, 0, oldA.length);
                      mA[oldA.length]=m;
                  }
                  prefixes.put( prefix, mA);
                  //log("Multiple prefixes: " + prefix + " " + mA);
                  
              }
          }
          
          return prefix!=null;
      }
  
  
      /** Read loader.REPO.cache from work dir
       * 
       * This file will hold timestamps for each module/jar and cache the INDEX -
       * to avoid opening the jars/modules that are not used 
       * 
       * @throws IOException
       */
      private void readCachedIdx() throws IOException {
          
      }
      
      /** Check the index and verify that:
       * - all jars are older than timestamp and still exist
       * - there are no new jars 
       * 
       * @throws IOException
       */
      private void checkCacheIdx() throws IOException {
          
      }
      
      
      private void writeCacheIdx() throws IOException {
          
      }
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/package.html
  
  Index: package.html
  ===================================================================
  <html>
  <body>
  
  The goal of this package is to provide class loading functionality,
  similar in behavior with Jboss and MLET loaders. There 
  is no policy, just a mechanism - how it is used depends on the higher level. 
  
  This package only deals with class loading, so it has to minimize the dependencies.
  Currently there is no dependency except bare JDK1.3. Debug logging is minimal and done
  using System.err.
  
  There is no JMX depenency - the modules and loaders can be registered with JMX 
  by a module using the ModuleListener, after jmx class loader is created. 
  TODO: conditional loading of jmx.jar, parser based on jdk version. 
  
  Configuration uses a simple properties file describing the classpaths and the 
  classes to launch - i.e. all a class loader needs to know, and 
  similar with the old catalina.properties. 
  
  To implement a good module system on top of this we need lifecycle ( already present
  in tomcat ) and discipline in making sure there are no stale references to 
  objects in a module after its death. An OSGI-like system may seem to 
  deal with the second problem - but it doesn't solve anything, it just makes 
  the references more visible and requires major changes in how you code, as 
  well as rewriting of most apis and implementations - and in the end it still 
  doesn't solve the problem. 
  
  </body>
  </html>
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org