costin      2005/09/28 23:35:48

  Modified:    util/loader/org/apache/tomcat/util/loader Loader.java
                        Module.java ModuleClassLoader.java
                        ModuleListener.java Repository.java package.html
  Removed:     util/loader/org/apache/tomcat/util/loader
                        LoaderProperties.java
  Log:
  AFAIK this is not currently used, it's just an experimental refactoring of 
the loading I started long time ago. Besides beeing
  useable standalone and more generic, it supports few extra features, like 
reloading of .jar files and using a 'flat' loader, similar with jboss and jmx.
  
  - refactoring to clean up the interfaces ( code is based on catalina loader )
  - add support for delegating to a parent classloader, needed for the case 
this is embeded in another app
  - fix various things.
  
  Revision  Changes    Path
  1.3       +396 -36   
jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/Loader.java
  
  Index: Loader.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/Loader.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Loader.java       6 Oct 2004 15:41:44 -0000       1.2
  +++ Loader.java       29 Sep 2005 06:35:47 -0000      1.3
  @@ -19,18 +19,24 @@
   
   
   import java.io.File;
  +import java.io.FileInputStream;
  +import java.io.InputStream;
  +import java.lang.reflect.InvocationTargetException;
   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.Properties;
   import java.util.StringTokenizer;
   import java.util.Vector;
   
   
   /**
  - * Boostrap loader for Catalina.  This application constructs a class loader
  + * Boostrap loader for Catalina or other java apps. 
  + * 
  + * 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
  @@ -38,20 +44,44 @@
    * other classes they depend on, such as an XML parser) out of the system
    * class path and therefore not visible to application level classes.
    *
  + *
  + * Merged with CatalinaProperties:
  + * Load a properties file describing the modules and startup sequence.
  + * This is responsible for configuration of the loader. 
  + * TODO: support jmx-style configuration, including persistence.
  + * TODO: better separate legacy config and the new style
  + * 
  + * 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 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 DEBUG=true; 
//LoaderProperties.getProperty("loader.debug.Loader") != null;
  +    // If flat, only one loader is created. If false - one loader per jar/dir
       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}";
  +    private static final String CATALINA_HOME_TOKEN = "${catalina.home}";
  +    private static final String CATALINA_BASE_TOKEN = "${catalina.base}";
   
   
       // ------------------------------------------------------- Static 
Variables
  @@ -75,24 +105,53 @@
       protected ClassLoader catalinaLoader = null;
       private String[] args;
       private Hashtable repositories=new Hashtable();
  +    private ClassLoader parentClassLoader;
       
  +    private static Properties properties = null;
  +
   
  +    private static String propFile;
   
       // -------------------------------------------------------- Private 
Methods
  +    
  +    /** Set the parent class loader - can be used instead of setParent, 
  +     * in case this is the top loader and needs to delagate to embedding app.
  +     * The common loader will delegate to this loader
  +     * 
  +     * @param myL
  +     */
  +    public void setParentClassLoader(ClassLoader myL) {
  +        this.parentClassLoader=myL;
  +    }
   
   
  -    private void initClassLoaders() {
  +
  +    /** Initialize the loader, creating all repositories.
  +     *  Will create common, server, shared. 
  +     *  
  +     *  TODO: create additional repos.
  +     *
  +     */
  +    public void init() {
           try {
  -            commonRepository = initRepository("common", null);
  -            catalinaRepository = initRepository("server", commonRepository);
  +            commonRepository = initRepository("common", null, 
parentClassLoader);
  +            catalinaRepository = initRepository("server", 
commonRepository,null);
               catalinaLoader = catalinaRepository.getClassLoader();
  -            sharedRepository = initRepository("shared", commonRepository);
  +            sharedRepository = initRepository("shared", 
commonRepository,null);
           } catch (Throwable t) {
               log("Class loader creation threw exception", t);
               System.exit(1);
           }
       }
   
  +    /** Create a new repository. 
  +     *  No Module is added ( currently )
  +     *  TODO: use props to prepopulate, if any is present.
  +     * 
  +     * @param name
  +     * @param parent
  +     * @return
  +     */
       public Repository createRepository(String name, Repository parent) {
           Repository lg=new Repository(this);
   
  @@ -120,13 +179,15 @@
        * @return
        * @throws Exception
        */
  -    private Repository initRepository(String name, Repository parent)
  +    private Repository initRepository(String name, Repository parent, 
ClassLoader pcl)
           throws Exception 
       {
  -        String value = LoaderProperties.getProperty(name + ".loader");
  +        String value = getProperty(name + ".loader");
   
           Repository lg=createRepository(name, parent );
  -        if( DEBUG ) log( "Creating loading group " + name + " - " + value);
  +        if( pcl != null )
  +            lg.setParentClassLoader( pcl );
  +        if( DEBUG ) log( "Creating loading group " + name + " - " + value + 
" " + pcl);
           
           if ((value == null) || (value.equals("")))
               return lg;
  @@ -142,11 +203,12 @@
   
               // Local repository
               boolean packed = false;
  +            
               if (repository.startsWith(CATALINA_HOME_TOKEN)) {
  -                repository = LoaderProperties.getCatalinaHome()
  +                repository = getCatalinaHome()
                       + repository.substring(CATALINA_HOME_TOKEN.length());
               } else if (repository.startsWith(CATALINA_BASE_TOKEN)) {
  -                repository = LoaderProperties.getCatalinaBase()
  +                repository = getCatalinaBase()
                       + repository.substring(CATALINA_BASE_TOKEN.length());
               }
   
  @@ -198,14 +260,17 @@
        * file and removed from args[].  
        */
       private void processCLI() {
  -        if( args.length > 0 &&
  +        if( args!=null && args.length > 0 &&
                   (args[0].toLowerCase().endsWith(".tomcat") ||
  +                        args[0].toLowerCase().endsWith(".loader") ||
                           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);
  +            setPropertiesFile(props);
  +        } else {
  +            loadProperties();
           }
       }
       
  @@ -222,22 +287,26 @@
           processCLI();
           
           // Set Catalina path
  -        LoaderProperties.setCatalinaHome();
  -        LoaderProperties.setCatalinaBase();
  +        setCatalinaHome();
  +        setCatalinaBase();
   
  -        initClassLoaders();
  +        init();
           
           Thread.currentThread().setContextClassLoader(catalinaLoader);
   
           securityPreload(catalinaLoader);
   
  +        autostart();
  +    }
  +
  +    private void autostart() throws ClassNotFoundException, 
InstantiationException, IllegalAccessException, NoSuchMethodException, 
InvocationTargetException {
           // 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",
  +        String startupClasses=getProperty("loader.auto-startup",
                   "org.apache.catalina.startup.CatalinaModuleListener");
           Vector v=split( startupClasses ); 
           
  @@ -261,6 +330,8 @@
                   // all arg processing moved there. Maybe we can make it 
consistent
                   // for all startup schemes
                   listener.start();
  +            } else if ( startupInstance instanceof Runnable ) {
  +                ((Runnable)startupInstance).run();
               } else {
                   Class paramTypes[] = new Class[0];
                   Object paramValues[] = new Object[0];
  @@ -268,12 +339,21 @@
                       startupInstance.getClass().getMethod("execute", 
paramTypes);
                   if( method==null ) 
                       method = startupInstance.getClass().getMethod("start", 
paramTypes);
  -                method.invoke(startupInstance, paramValues);
  +                if( method!=null )
  +                    method.invoke(startupInstance, paramValues);
               }
   
           }
       }
   
  +    /** Returns one of the repositories. 
  +     * 
  +     *  Typically at startup we create at least: "common", "shared" and 
"server", with 
  +     *  same meaning as in tomcat. 
  +     * 
  +     * @param name
  +     * @return
  +     */
       public Repository getRepository( String name ) {
           return (Repository)repositories.get(name);
       }
  @@ -285,7 +365,7 @@
               return;
           }
   
  -        String value=LoaderProperties.getProperty("security.preload");
  +        String value=getProperty("security.preload");
           Vector repo=split( value );
           Enumeration elems=repo.elements();
           while (elems.hasMoreElements()) {
  @@ -298,8 +378,11 @@
           }
       }
   
  +
       // ----------------------------------------------------------- Main 
Program
   
  +    /** Access to the command line arguments, when Loader is used to launc 
an app.
  +     */
       public String[] getArgs() {
           return args;
       }
  @@ -329,16 +412,287 @@
   
       }
   
  -    public void setCatalinaHome(String s) {
  +
  +    /**
  +     * Initialize the loader properties explicitely. 
  +     * 
  +     * TODO: add setPropertiesRes
  +     * 
  +     * @param props
  +     */
  +    public void setPropertiesFile(String props) {
  +        propFile=props;
  +        loadProperties();
  +    }
  +
  +    /**
  +     * Return specified property value.
  +     */
  +    static String getProperty(String name) {
  +        if( properties==null ) loadProperties();
  +        return properties.getProperty(name);
  +    }
  +
  +
  +    /**
  +     * Return specified property value.
  +     */
  +    static String getProperty(String name, String defaultValue) {
  +        if( properties==null ) loadProperties();
  +        return properties.getProperty(name, defaultValue);
  +    }
  +
  +    /**
  +     * 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. 
  +     */
  +    static void loadProperties() {
  +        properties = new Properties();
  +        
  +        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 {
  +                // "loader.config" system property
  +                String configUrl = System.getProperty("loader.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 {
  +                setCatalinaBase(); // use system properties, then user.dir
  +                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 propertiesF=null;
  +                if(  conf.exists() )  
  +                     propertiesF= new File(conf, "loader.properties");
  +                if( ! propertiesF.exists() ) {
  +                    propertiesF= new File( home, "loader.properties");
  +                }
  +                if( propertiesF.exists() )
  +                    is = new FileInputStream(propertiesF);
  +                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 = Loader.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 = Loader.class.getResourceAsStream
  +                    ("loader.properties");
  +                if( is!=null && DEBUG ) {
  +                    log("Loaded from res loader.properties " );
  +                }
  +            } catch (Throwable t) {
  +                // Ignore
  +            }
  +        }
  +        
  +        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);
  +                }
  +            }
  +        }
  +
  +    }
  +
  +    static void setCatalinaHome(String s) {
           System.setProperty( "catalina.home", s );
       }
   
  -    public void setCatalinaBase(String s) {
  +    static void setCatalinaBase(String s) {
           System.setProperty( "catalina.base", s );
       }
   
  +
  +    /**
  +     * Get the value of the catalina.home environment variable.
  +     * 
  +     * @deprecated
  +     */
  +    static String getCatalinaHome() {
  +        if( properties==null ) loadProperties();
  +        return System.getProperty("catalina.home",
  +                                  System.getProperty("user.dir"));
  +    }
  +    
  +    
  +    /**
  +     * Get the value of the catalina.base environment variable.
  +     * 
  +     * @deprecated
  +     */
  +    static String getCatalinaBase() {
  +        if( properties==null ) loadProperties();
  +        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( properties==null ) loadProperties();
  +
  +        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"));
  +        }
  +
  +    }
  +
  +
  +    
       /**
  -     * Public as ModuleClassLoader. 
  +     * Get the module from the classloader. Works only for classloaders 
created by
  +     * this package - or extending ModuleClassLoader.
  +     * 
  +     * This shold be the only public method that allows this - Loader acts 
as a 
  +     * guard, only if you have the loader instance you can access the 
internals.
  +     * 
        * 
        * @param cl
        * @return
  @@ -387,16 +741,18 @@
                           log("  Not found:  "+ file.getAbsolutePath());
                       continue;
                   }
  +//                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);
  +//                }
  +                URL url=file.toURL();
                   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);
  -                }
  +                    sb.append(" : "+ url);
                   if( ! FLAT ) {
                       addLoader(lg, parent, new URL[] { url });
                   } else {
  @@ -421,10 +777,14 @@
                       if (!filename.endsWith(".jar"))
                           continue;
                       File file = new File(directory, filenames[j]);
  +//                    if (DEBUG)
  +//                        sb.append(" [pak]="+ file.getCanonicalPath());
  +//                    URL url = new URL("file", null,
  +//                            file.getCanonicalPath());
  +                    URL url=file.toURL();
                       if (DEBUG)
  -                        sb.append(" "+ file.getAbsolutePath());
  -                    URL url = new URL("file", null,
  -                            file.getCanonicalPath());
  +                        sb.append(" pk="+ url);
  +
                       if( ! FLAT ) {
                           addLoader(lg, parent, new URL[] { url });
                       } else {
  
  
  
  1.3       +35 -4     
jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/Module.java
  
  Index: Module.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/Module.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Module.java       6 Oct 2004 15:41:44 -0000       1.2
  +++ Module.java       29 Sep 2005 06:35:47 -0000      1.3
  @@ -21,17 +21,16 @@
   import java.io.File;
   import java.io.Serializable;
   import java.lang.reflect.Constructor;
  +import java.net.MalformedURLException;
   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.   
  - * 
  + * Represent one unit of code - jar, webapp, etc. Modules can be reloaded 
independently,
  + * and may be part of a flat structure or a hierarchy.
    * 
    * @author Costin Manolache
  - * 
    * @author Craig R. McClanahan
    * @author Remy Maucherat
    */
  @@ -220,6 +219,16 @@
   
       // ------------------------------------------------------- Private 
Methods
   
  +    /** 
  +     * Experiment for basic lifecycle driven by higher layer.
  +     * start() and stop() methods will be called on the class when the
  +     * module is stopped and started.
  +     * 
  +     */
  +    //public void addModuleClass(String s) {
  +        
  +    //}
  +    
       /** Set the class used to construct the class loader.
        * 
        * The alternative is to set the context class loader to allow 
loaderClass
  @@ -262,6 +271,7 @@
           } else {
               classLoader=new ModuleClassLoader( classpath, parentClassLoader);
           }
  +        System.err.println("---- Created class loader " + classpath + " " + 
parentClassLoader + " repo=" + repository.getName() + " " + parent);
           
           classLoader.setModule(this);
           classLoader.setDelegate( delegate );
  @@ -286,6 +296,22 @@
       public void setClasspath(URL[] array) {
           this.classpath=array;
       }
  +    
  +    /** Set the path to the module.
  +     * In normal use, each module will be associated with one jar or 
  +     * classpath dir.
  +     * 
  +     * @param name
  +     */
  +    public void setPath(String name) {
  +        this.classpath=new URL[1];
  +        try {
  +            classpath[0]=new URL(name);
  +        } catch (MalformedURLException e) {
  +            // TODO Auto-generated catch block
  +            e.printStackTrace();
  +        }
  +    }
   
   
       /**
  @@ -334,4 +360,9 @@
       }
   
   
  +    public void setParentClassLoader(ClassLoader parentClassLoader2) {
  +        this.parentClassLoader=parentClassLoader2;
  +    }
  +
  +
   }
  
  
  
  1.2       +59 -20    
jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/ModuleClassLoader.java
  
  Index: ModuleClassLoader.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/ModuleClassLoader.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ModuleClassLoader.java    6 Oct 2004 05:00:04 -0000       1.1
  +++ ModuleClassLoader.java    29 Sep 2005 06:35:47 -0000      1.2
  @@ -68,19 +68,23 @@
       // 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;
  +    private static final boolean DEBUG=false; 
//LoaderProperties.getProperty("loader.debug.ModuleClassLoader") != null;
  +    private static final boolean 
DEBUGNF=false;//LoaderProperties.getProperty("loader.debug.ModuleClassLoaderNF")
 != null;
       
       // ----------------------------------------------------------- 
Constructors
   
   
       public ModuleClassLoader(URL repositories[], ClassLoader parent) {
           super(repositories, parent);
  +        if(DEBUG) log( "NEW ModuleClassLoader  " + parent + " " + 
repositories.length);
  +        updateStamp();
       }
       
   
       public ModuleClassLoader(URL repositories[]) {
           super(repositories);
  +        if(DEBUG) log( "NEW ModuleClassLoader  -null-"+ " " + 
repositories.length);
  +        updateStamp();
       }
   
   
  @@ -104,6 +108,8 @@
        */
       protected long lastJarAccessed = 0L;
   
  +    protected long lastModified=0L;
  +    
       /**
        * Has this component been started?
        */
  @@ -113,17 +119,6 @@
   
       // ------------------------------------------------------------- 
Properties
   
  -
  -    /**
  -     * Return the "delegate first" flag for this class loader.
  -     */
  -//    boolean getDelegate() {
  -//
  -//        return (this.delegate);
  -//
  -//    }
  -
  -
       /**
        * Set the "delegate first" flag for this class loader.
        *
  @@ -168,8 +163,13 @@
       void addRepository(String repository) {
           // Add this repository to our underlying class loader
           try {
  +            boolean mod=modified();
               URL url = new URL(repository);
               super.addURL(url);
  +            if( ! mod ) {
  +                // don't check if it is modified, so it works
  +                updateStamp();
  +            }
           } catch (MalformedURLException e) {
               IllegalArgumentException iae = new IllegalArgumentException
                   ("Invalid repository: " + repository); 
  @@ -179,6 +179,30 @@
           }
       }
   
  +    void updateStamp() {
  +        URL cp[]=super.getURLs();
  +        if (cp != null ) {
  +            for (int i = 0; i <cp.length; i++) {
  +                File f=new File(cp[i].getFile());
  +                long lm=f.lastModified();
  +                if( lm > lastModified ) lastModified=lm;
  +            }
  +        }
  +    }
  +    
  +    private boolean dirCheck(File dir ) {
  +        log("Checking " + dir );
  +        File subd[]=dir.listFiles();
  +        for( int i=0; i< subd.length; i++ ) {
  +            long lm=subd[i].lastModified();
  +            if( lm > lastModified ) return true;
  +            if( subd[i].isDirectory() ) {
  +                if( dirCheck(subd[i]) ) return true;
  +            }
  +        }
  +        return false;
  +    }
  +    
       /**
        * Have one or more classes or resources been modified so that a reload
        * is appropriate?
  @@ -186,6 +210,19 @@
        * Not public - call it via Module
        */
       boolean modified() {
  +        URL cp[]=super.getURLs();
  +        if (cp != null ) {
  +            for (int i = 0; i <cp.length; i++) {
  +                File f=new File(cp[i].getFile());
  +                long lm=f.lastModified();
  +                if( lm > lastModified ) return true;
  +                // assume dirs are used only for debug and small
  +                if( f.isDirectory() ) {
  +                    if( dirCheck(f) ) return true;
  +                }
  +            }
  +        }
  +
           if (DEBUG)
               log("modified() false");
   
  @@ -215,8 +252,9 @@
                   log("findClass() -->RuntimeException " + name, e);
               throw e;
           } catch( ClassNotFoundException ex ) {
  +            URL cp[]=this.getURLs();
               if (DEBUGNF)
  -                log("findClass() NOTFOUND  " + name);
  +                log("findClass() NOTFOUND  " + name + " " + (( cp.length > 0 
) ? cp[0].toString() : "") );
               throw ex;
           }
               
  @@ -364,7 +402,7 @@
   
       }
   
  -    // to avoid duplication
  +    // to avoid duplication - get resource from parent, when delegating
       private URL getResourceParentDelegate(String name) {
           URL url=null;
           ClassLoader loader = getParent();
  @@ -384,6 +422,7 @@
                   return (url);
               }
           }
  +        if( DEBUG ) log("getResource not found by parent " + loader);
   
           return url;
       }
  @@ -476,8 +515,8 @@
           try {
               clazz = findClass(name);
               if (clazz != null) {
  -//                if (DEBUG)
  -//                    log("loadClass - FOUND findClass " + delegate + " " + 
name + " , " + resolve);
  +                if (DEBUG)
  +                    log("loadClass - FOUND findClass " + delegate + " " + 
name + " , " + resolve);
                   if (resolve) resolveClass(clazz);
                   return (clazz);
               }
  @@ -515,7 +554,7 @@
               }
           }
   
  -        if( DEBUGNF ) log("loadClass(): NOTFOUND " + name );
  +        if( DEBUGNF ) log("loadClass(): NOTFOUND " + name + " xxx " + 
getParent() +  " " + repository.getName() );
           throw new ClassNotFoundException(name);
       }
   
  @@ -583,10 +622,10 @@
       // ------------------ Local methods ------------------------
   
       private void log(String s ) {
  -        System.err.println("ModuleCL: " + s);
  +        System.err.println("ModuleClassLoader: " + s);
       }
       private void log(String s, Throwable t ) {
  -        System.err.println("ModuleCL: " + s);
  +        System.err.println("ModuleClassLoader: " + s);
           t.printStackTrace();
       }
       
  
  
  
  1.2       +5 -0      
jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/ModuleListener.java
  
  Index: ModuleListener.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/ModuleListener.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ModuleListener.java       6 Oct 2004 05:00:04 -0000       1.1
  +++ ModuleListener.java       29 Sep 2005 06:35:47 -0000      1.2
  @@ -17,6 +17,11 @@
   
   package org.apache.tomcat.util.loader;
   
  +/**
  + * Interface providing notifications on Module events.
  + * 
  + * @author Costin Manolache
  + */
   public interface ModuleListener {
       
       /** Called when a module group is created. This is only called when a 
new group
  
  
  
  1.2       +90 -31    
jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/Repository.java
  
  Index: Repository.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/Repository.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Repository.java   6 Oct 2004 05:00:04 -0000       1.1
  +++ Repository.java   29 Sep 2005 06:35:47 -0000      1.2
  @@ -46,23 +46,26 @@
    */
   public class Repository {
      
  -    private static final boolean DEBUG=true; 
//LoaderProperties.getProperty("loader.Repository.debug") != null;
  +    private static final boolean 
DEBUG=Loader.getProperty("loader.debug.Repository") != 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 static final boolean 
USE_IDX=Loader.getProperty("loader.Repository.noIndex") == 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() {
  +    // For delegation
  +    private ClassLoader parentClassLoader;
  +    private Repository parent;
  +
  +
  +    private Repository() {
       }
   
       public Repository(Loader loader) {
  @@ -74,13 +77,16 @@
           return loader;
       }
       
  -    public void addModule(  Module mod ) {
  +    void addModule(  Module mod ) {
           mod.setRepository( this );
   
           grpModules.addElement(mod);
           if( loader.listener!=null ) {
               loader.listener.moduleAdd(mod);
           }
  +        
  +        if( parentClassLoader != null ) 
  +            mod.setParentClassLoader( parentClassLoader );
   
           if(! mod.isStarted()) {
               mod.start();
  @@ -92,6 +98,8 @@
           try {
               if( USE_IDX ) {
                   processJarIndex(mod);
  +                // TODO: if we are in the initial starting, write cache only 
once
  +                // TODO: write it only if there is a change in the timestamp
                   writeCacheIdx();
               }
           } catch (Exception e) {
  @@ -101,11 +109,34 @@
           
       }
       
  +    public void newModule( String path ) {
  +        Module m=new Module();
  +        m.setPath( path );
  +        addModule( m );
  +    }
  +    
       public Enumeration getModules() {
           return grpModules.elements();
       }
       
  -    public Repository getParent() {
  +    public void checkReload() {
  +        try {
  +        Enumeration mE=grpModules.elements();
  +        while( mE.hasMoreElements() ) {
  +            Module m=(Module)mE.nextElement();
  +            log("Modified " + m + " " + m.modified());
  +            
  +            if( m.modified() ) {
  +                m.stop();
  +                m.start();
  +            }
  +        }
  +        } catch( Throwable t ) {
  +            t.printStackTrace();
  +        }
  +    }
  +    
  +    Repository getParent() {
           return parent;
       }
   
  @@ -116,6 +147,16 @@
       public void setParent(Repository parent) {
           this.parent = parent;
       }
  +    
  +    /** Set the parent class loader - can be used instead of setParent, 
  +     * in case this is the top loader and needs to delagate to embedding app
  +     * 
  +     * @param myL
  +     */
  +    public void setParentClassLoader(ClassLoader myL) {
  +        this.parentClassLoader=myL;
  +    }
  +
   
       /** Add a class loder to the group.
        *
  @@ -144,6 +185,7 @@
           loaders.removeElement(cl);
   
           if(DEBUG) log("removed " + loaders.size() + "/" + oldSize + ": "  + 
cl);
  +        // TODO: remove from index
       }
   
       /** Return a class loader associated with the group.
  @@ -153,16 +195,27 @@
        */
       public ClassLoader getClassLoader() {
           if( groupClassLoader==null ) {
  -            if( parent == null ) {
  +            ClassLoader pcl=parentClassLoader;
  +            if( pcl==null && parent!=null ) {
  +                pcl=parent.getClassLoader();
  +            } 
  +            if( pcl==null ) {
  +                pcl=Thread.currentThread().getContextClassLoader();
  +             }
  +
  +            if( pcl == null ) {
  +                // allow delegation to embedding app
                   groupClassLoader=new ModuleClassLoader(new URL[0]);
               } else {
  -                groupClassLoader=new ModuleClassLoader(new URL[0], 
parent.getClassLoader());
  +                groupClassLoader=new ModuleClassLoader(new URL[0], pcl);
               }
  +            if( DEBUG ) log("---------- Created repository loader " + pcl );
               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
  @@ -173,29 +226,17 @@
        * @param classN
        * @return
        */
  -    public Class findClass(ClassLoader caller, String classN ) {
  +    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];
  +            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);
  @@ -204,10 +245,22 @@
                           //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-- ) {
  @@ -243,9 +296,9 @@
        * @param name2
        * @return
        */
  -    public URL findResource(ModuleClassLoader caller, String classN) {
  +    URL findResource(ModuleClassLoader caller, String classN) {
           URL url=null;
  -        
  +        if( DEBUG ) log("Repository.findResource " + classN + " " + caller );
           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
  @@ -380,6 +433,12 @@
       
       
       private void writeCacheIdx() throws IOException {
  +        // For each module we write the timestamp, filename then the index
  +        // The idea is to load this single file to avoid scanning many jars
  +
  +        // we'll use the cache 
  +        
           
       }
  +
   }
  
  
  
  1.2       +26 -23    
jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/package.html
  
  Index: package.html
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader/package.html,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- package.html      6 Oct 2004 05:00:04 -0000       1.1
  +++ package.html      29 Sep 2005 06:35:47 -0000      1.2
  @@ -1,29 +1,32 @@
   <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. 
  +The goal of this package is to provide class loading functionality, similar 
in behavior with Jboss and MLET loaders. There 
  +is no specific policy, just a mechanism - how it is used depends on the 
application. It is based on the tomcat5.x class
  +loader, with additional support for the 'repository' delegation.
  +
  +The main class is Loader - it controls a hierarchy of Repositories, each 
consisting of one or more Modules. Each Module corresponds to one jar file 
  +or directory - and will have a ModuleClassLoader that answers only for that 
file. The Repository is associated with a ModuleClassLoader that delegates to 
  +each Module. It is possible to add/remove/replace Modules at runtime - just 
like in JMX and JBoss. In normal tomcat, only webapps can be reloaded - this 
also allow connectors, valves, and any internal server jar to be reloaded.
  +
  +The package only deals with class loading, with minimal the dependencies. 
Currently there is no dependency except bare JDK1.3. 
  +
  +The modules and loaders can be registered with JMX by a module using the 
ModuleListener, after jmx class loader is created. Note that JMX is not a 
dependency and doesn't have to be in the classpath - it can be loaded in a 
Repository, and then something like Modeler will do the mapping. 
  +
  +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. JBoss and JMX are actually on the right track in 
this, as oposed to OSGI. 
  +
  +The loader is also trying to stick to the minimal classloading-related 
functionality - unlike OSGI wich is reinventing all weels. I started working on 
the loader after trying to see how OSGI would fit, and realizing that it is a 
wrong design.
  +
  +
  +<h2>Using loader for launching</h2>
  +
  +Loader has a main(), and will look up the loader.properties file, create the 
class loaders, and then launch any 'auto-startup' classes. The must important 
part of launching an app is setting the classpath, and using Loader allows the 
app to use more advanced features than using simple CLASSPATH.
   
   </body>
   </html>
  \ No newline at end of file
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to