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 2006/05/19 07:18:20 UTC

svn commit: r407715 [1/2] - in /tomcat/sandbox: ./ java/org/apache/coyote/adapters/ java/org/apache/coyote/http11/ java/org/apache/coyote/http11/apr/ java/org/apache/coyote/standalone/ java/org/apache/tomcat/util/loader/ java/org/apache/tomcat/util/net...

Author: costin
Date: Thu May 18 22:18:18 2006
New Revision: 407715

URL: http://svn.apache.org/viewvc?rev=407715&view=rev
Log:
Cleaning up, this time I hope in the right place.

- The APR stuff is not going anywhere, and probably doesn't matter, people using it probably won't care nothing
is shared with the other connectors ( and viceversa )
- The adapters were just examples - I'm keeping the 2 that may matter for coyote standalone use.
- adding loader from util, so the only dep will be tomcat6




Added:
    tomcat/sandbox/java/org/apache/tomcat/util/loader/
    tomcat/sandbox/java/org/apache/tomcat/util/loader/Loader.java   (with props)
    tomcat/sandbox/java/org/apache/tomcat/util/loader/Module.java   (with props)
    tomcat/sandbox/java/org/apache/tomcat/util/loader/ModuleClassLoader.java   (with props)
    tomcat/sandbox/java/org/apache/tomcat/util/loader/ModuleListener.java
    tomcat/sandbox/java/org/apache/tomcat/util/loader/Repository.java   (with props)
    tomcat/sandbox/java/org/apache/tomcat/util/loader/RepositoryClassLoader.java
    tomcat/sandbox/java/org/apache/tomcat/util/loader/package.html
Removed:
    tomcat/sandbox/java/org/apache/coyote/adapters/Counters.java
    tomcat/sandbox/java/org/apache/coyote/adapters/HelloWorldAdapter.java
    tomcat/sandbox/java/org/apache/coyote/adapters/JsAdapter.java
    tomcat/sandbox/java/org/apache/coyote/adapters/Mapper.java
    tomcat/sandbox/java/org/apache/coyote/http11/apr/Http11AprBaseProtocol.java
    tomcat/sandbox/java/org/apache/coyote/http11/apr/Http11AprProcessor.java
    tomcat/sandbox/java/org/apache/coyote/http11/apr/Http11AprProtocol.java
    tomcat/sandbox/java/org/apache/coyote/http11/apr/InternalAprInputBuffer.java
    tomcat/sandbox/java/org/apache/coyote/http11/apr/InternalAprOutputBuffer.java
    tomcat/sandbox/java/org/apache/tomcat/util/net/apr/AprByteChannel.java
    tomcat/sandbox/java/org/apache/tomcat/util/net/apr/AprEndpoint.java
    tomcat/sandbox/java/org/apache/tomcat/util/net/apr/AprFileChannel.java
    tomcat/sandbox/java/org/apache/tomcat/util/net/apr/AprSocket.java
Modified:
    tomcat/sandbox/.classpath
    tomcat/sandbox/java/org/apache/coyote/adapters/FileAdapter.java
    tomcat/sandbox/java/org/apache/coyote/adapters/MapperAdapter.java
    tomcat/sandbox/java/org/apache/coyote/http11/Http11Protocol.java
    tomcat/sandbox/java/org/apache/coyote/standalone/Main.java

Modified: tomcat/sandbox/.classpath
URL: http://svn.apache.org/viewvc/tomcat/sandbox/.classpath?rev=407715&r1=407714&r2=407715&view=diff
==============================================================================
--- tomcat/sandbox/.classpath (original)
+++ tomcat/sandbox/.classpath Thu May 18 22:18:18 2006
@@ -1,14 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="java"/>
-	<classpathentry kind="src" path="test"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/container"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/connectors"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="var" path="TOMCAT_LIBS_BASE/rhino1_6R2/js.jar"/>
-	<classpathentry kind="var" path="TOMCAT_LIBS_BASE/junit3.8.1/junit.jar"/>
-	<classpathentry kind="var" path="TOMCAT_LIBS_BASE/servlet-api-2.4/lib/servlet-api.jar"/>
-	<classpathentry kind="var" path="TOMCAT_LIBS_BASE/commons-modeler-1.1/commons-modeler.jar"/>
-	<classpathentry kind="var" path="TOMCAT_LIBS_BASE/commons-el-1.0/commons-el.jar"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/tomcat6"/>
 	<classpathentry kind="output" path="classes"/>
 </classpath>

Modified: tomcat/sandbox/java/org/apache/coyote/adapters/FileAdapter.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/java/org/apache/coyote/adapters/FileAdapter.java?rev=407715&r1=407714&r2=407715&view=diff
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/adapters/FileAdapter.java (original)
+++ tomcat/sandbox/java/org/apache/coyote/adapters/FileAdapter.java Thu May 18 22:18:18 2006
@@ -10,9 +10,7 @@
 import org.apache.coyote.Adapter;
 import org.apache.coyote.Request;
 import org.apache.coyote.Response;
-import org.apache.coyote.standalone.MessageWriter;
 import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.C2BConverter;
 
 /**
  * Serve a static file. This is the traditional method, a separate adapter could
@@ -122,6 +120,11 @@
     
     public String getContentType( String ext ) {
         return contentTypes.getProperty( ext, "text/plain" );
+    }
+
+    public boolean event(Request req, Response res, boolean error) throws Exception {
+        // TODO Auto-generated method stub
+        return false;
     }
     
 }

Modified: tomcat/sandbox/java/org/apache/coyote/adapters/MapperAdapter.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/java/org/apache/coyote/adapters/MapperAdapter.java?rev=407715&r1=407714&r2=407715&view=diff
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/adapters/MapperAdapter.java (original)
+++ tomcat/sandbox/java/org/apache/coyote/adapters/MapperAdapter.java Thu May 18 22:18:18 2006
@@ -1,16 +1,12 @@
 package org.apache.coyote.adapters;
 
-import java.util.Enumeration;
 import java.util.Hashtable;
 
 import org.apache.coyote.Adapter;
 import org.apache.coyote.Request;
 import org.apache.coyote.Response;
-import org.apache.coyote.http11.Http11BaseProtocol;
 import org.apache.coyote.standalone.MessageWriter;
 import org.apache.tomcat.util.http.mapper.Mapper;
-import org.apache.tomcat.util.loader.Loader;
-import org.apache.tomcat.util.loader.Repository;
 
 /**
  * 
@@ -76,6 +72,11 @@
 
     public Adapter getDefaultAdapter() {
         return defaultAdapter;
+    }
+
+    public boolean event(Request req, Response res, boolean error) throws Exception {
+        // TODO Auto-generated method stub
+        return false;
     }
 
 }

Modified: tomcat/sandbox/java/org/apache/coyote/http11/Http11Protocol.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/java/org/apache/coyote/http11/Http11Protocol.java?rev=407715&r1=407714&r2=407715&view=diff
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/http11/Http11Protocol.java (original)
+++ tomcat/sandbox/java/org/apache/coyote/http11/Http11Protocol.java Thu May 18 22:18:18 2006
@@ -16,35 +16,14 @@
 
 package org.apache.coyote.http11;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.URLEncoder;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Iterator;
-
 import javax.management.MBeanRegistration;
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
 
-import org.apache.commons.modeler.Registry;
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.ActionHook;
-import org.apache.coyote.Adapter;
-import org.apache.coyote.ProtocolHandler;
 import org.apache.coyote.RequestGroupInfo;
 import org.apache.coyote.RequestInfo;
+import org.apache.tomcat.util.modeler.Registry;
 import org.apache.tomcat.util.net.PoolTcpEndpoint;
-import org.apache.tomcat.util.net.TcpConnection;
-import org.apache.tomcat.util.net.TcpConnectionHandler;
-import org.apache.tomcat.util.net.javaio.SSLImplementation;
-import org.apache.tomcat.util.net.javaio.SSLSupport;
-import org.apache.tomcat.util.net.javaio.ServerSocketFactory;
-import org.apache.tomcat.util.res.StringManager;
-import org.apache.tomcat.util.threads.ThreadPool;
 import org.apache.tomcat.util.threads.ThreadWithAttributes;
 
 

Modified: tomcat/sandbox/java/org/apache/coyote/standalone/Main.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/java/org/apache/coyote/standalone/Main.java?rev=407715&r1=407714&r2=407715&view=diff
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/standalone/Main.java (original)
+++ tomcat/sandbox/java/org/apache/coyote/standalone/Main.java Thu May 18 22:18:18 2006
@@ -4,11 +4,8 @@
 import java.util.Hashtable;
 
 import org.apache.coyote.Adapter;
-import org.apache.coyote.adapters.Counters;
-import org.apache.coyote.adapters.HelloWorldAdapter;
 import org.apache.coyote.adapters.MapperAdapter;
 import org.apache.coyote.http11.Http11BaseProtocol;
-import org.apache.tomcat.util.http.mapper.Mapper;
 import org.apache.tomcat.util.loader.Loader;
 import org.apache.tomcat.util.loader.Repository;
 
@@ -40,13 +37,8 @@
         proto = new Http11BaseProtocol();
 
         mainAdapter = new MapperAdapter();        
-        mainAdapter.addAdapter("/hello", new HelloWorldAdapter());
 
-        Counters cnt=new Counters();
-        cnt.setNext( mainAdapter );
-
-        //proto.setAdapter(mainAdapter);
-        proto.setAdapter(cnt);
+        proto.setAdapter(mainAdapter);
     }
     
     public MapperAdapter getMapper() {

Added: tomcat/sandbox/java/org/apache/tomcat/util/loader/Loader.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/java/org/apache/tomcat/util/loader/Loader.java?rev=407715&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/loader/Loader.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/loader/Loader.java Thu May 18 22:18:18 2006
@@ -0,0 +1,881 @@
+/*
+ * 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.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 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
+ * 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.
+ *
+ *
+ * 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.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
+
+
+    private static final String CATALINA_HOME_TOKEN = "${catalina.home}";
+    private 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 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;
+    }
+
+
+
+    /** Initialize the loader, creating all repositories.
+     *  Will create common, server, shared. 
+     *  
+     *  TODO: create additional repos.
+     *
+     */
+    public void init() {
+        try {
+            commonRepository = initRepository("common", null, parentClassLoader);
+            catalinaRepository = initRepository("server", commonRepository,null);
+            catalinaLoader = catalinaRepository.getClassLoader();
+            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);
+
+        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, ClassLoader pcl)
+        throws Exception 
+    {
+        String value = getProperty(name + ".loader");
+
+        Repository lg=createRepository(name, parent );
+        if( pcl != null )
+            lg.setParentClassLoader( pcl );
+        if( DEBUG ) log( "Creating loading group " + name + " - " + value + " " + pcl);
+        
+        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 = getCatalinaHome()
+                    + repository.substring(CATALINA_HOME_TOKEN.length());
+            } else if (repository.startsWith(CATALINA_BASE_TOKEN)) {
+                repository = 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!=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;
+            setPropertiesFile(props);
+        } else {
+            loadProperties();
+        }
+    }
+    
+    /**
+     * Initialize:
+     *  - detect the home/base directories
+     *  - init the loaders / modules
+     *  - instantiate the "startup" class(es)
+     * 
+     */
+    public void main()
+        throws Exception
+    {
+        processCLI();
+        
+        // Set Catalina path
+        setCatalinaHome();
+        setCatalinaBase();
+
+        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=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 if ( startupInstance instanceof Runnable ) {
+                ((Runnable)startupInstance).run();
+            } 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);
+                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);
+    }
+    
+    private  static void securityPreload(ClassLoader loader)
+        throws Exception {
+
+        if( System.getSecurityManager() == null ){
+            return;
+        }
+
+        String value=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
+
+    /** Access to the command line arguments, when Loader is used to launc an app.
+     */
+    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();
+        }
+
+    }
+
+
+    /**
+     * 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 );
+    }
+
+    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"));
+        }
+
+    }
+
+
+    
+    /**
+     * 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
+     */
+    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;
+                }
+//                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(" : "+ url);
+                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(" [pak]="+ file.getCanonicalPath());
+//                    URL url = new URL("file", null,
+//                            file.getCanonicalPath());
+                    URL url=file.toURL();
+                    if (DEBUG)
+                        sb.append(" pk="+ url);
+
+                    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;
+    }
+
+    void notifyModuleStart(Module module) {
+        if(listener!=null) listener.moduleStart(module);
+    }
+
+    void notifyModuleStop(Module module) {
+        if( listener!=null ) listener.moduleStop(module);
+    }
+    
+    /** 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();
+    }
+
+}

Propchange: tomcat/sandbox/java/org/apache/tomcat/util/loader/Loader.java
------------------------------------------------------------------------------
    svn:executable = *

Added: tomcat/sandbox/java/org/apache/tomcat/util/loader/Module.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/java/org/apache/tomcat/util/loader/Module.java?rev=407715&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/loader/Module.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/loader/Module.java Thu May 18 22:18:18 2006
@@ -0,0 +1,370 @@
+/*
+ * 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.MalformedURLException;
+import java.net.URL;
+
+// Based on org.apache.catalina.Loader - removed most of the catalina-specific
+
+/**
+ * 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
+ */
+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;
+
+        log("start()");
+
+        // 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().notifyModuleStart(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().notifyModuleStop(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
+
+    /** 
+     * 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
+     * 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);
+        }
+        System.err.println("---- Created class loader " + classpath + " " + parentClassLoader + " repo=" + repository.getName() + " " + parent);
+        
+        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;
+    }
+    
+    /** 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();
+        }
+    }
+
+
+    /**
+     * @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();
+    }
+
+
+    public void setParentClassLoader(ClassLoader parentClassLoader2) {
+        this.parentClassLoader=parentClassLoader2;
+    }
+
+
+}

Propchange: tomcat/sandbox/java/org/apache/tomcat/util/loader/Module.java
------------------------------------------------------------------------------
    svn:executable = *

Added: tomcat/sandbox/java/org/apache/tomcat/util/loader/ModuleClassLoader.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/java/org/apache/tomcat/util/loader/ModuleClassLoader.java?rev=407715&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/loader/ModuleClassLoader.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/loader/ModuleClassLoader.java Thu May 18 22:18:18 2006
@@ -0,0 +1,692 @@
+/*
+ * 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.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();
+    }
+
+
+    // ----------------------------------------------------- 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;
+
+    protected long lastModified=0L;
+    
+    /**
+     * Has this component been started?
+     */
+    protected boolean started = false;
+
+    protected Module module;
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * 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 {
+            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); 
+            iae.initCause(e);
+            //jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+    }
+
+    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 ) {
+                log("Modified file: " + dir + " " + subd[i] + " " + lm + " " + lastModified);
+                return true;
+            }
+            if( subd[i].isDirectory() ) {
+                return  dirCheck(subd[i]);
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Have one or more classes or resources been modified so that a reload
+     * is appropriate?
+     * 
+     * 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 ) {
+                    log( "Modified file: " + f + " " + lm + " " + lastModified);
+                    return true;
+                }
+                // assume dirs are used only for debug and small
+                if( f.isDirectory() ) {
+                    return dirCheck(f);
+                }
+            }
+        }
+
+        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 {
+        return findClass2(name, true);
+    }
+    
+    public Class findClass2(String name, boolean del2repo) throws ClassNotFoundException {
+        if( del2repo ) {
+            return ((RepositoryClassLoader)repository.getClassLoader()).findClass(name);            
+        } // else 
+ 
+        Class clazz = null;
+            
+        try {
+            clazz = super.findClass(name);
+        } catch (RuntimeException e) {
+            if (DEBUG)
+                log("findClass() -->RuntimeException " + name, e);
+            throw e;
+        } catch( ClassNotFoundException ex ) {
+            URL cp[]=this.getURLs();
+            if (DEBUGNF)
+                log("findClass() NOTFOUND  " + name + " " + (( cp.length > 0 ) ? cp[0].toString() : "") );
+            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) {
+        return findResource2( name, true);
+    }
+        
+    public URL findResource2(final String name, boolean del2repo ) {
+        if( del2repo ) {
+            return ((RepositoryClassLoader)repository.getClassLoader()).findResource(name);
+        } // else:
+
+        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 {
+        return findResources2(name, true);
+    }
+    
+    Enumeration findResources2(String name, boolean del2repo) throws IOException {
+        if( del2repo ) {
+            return ((RepositoryClassLoader)repository.getClassLoader()).findResources(name);
+        } else {
+            return super.findResources(name);
+        }
+    }
+
+    // Next methods implement the search alghoritm - parent, repo, delegation, etc 
+
+    /** getResource() - modified to implement the search alghoritm 
+     * 
+     */
+    public URL getResource(String name) {
+        return getResource2( name, null, true);
+    }
+
+    /** getResource() - same thing, but don't delegate to repo if called 
+     * from repo 
+     * 
+     */
+    URL getResource2(String name, ClassLoader originator, boolean delegate2repo ) {
+
+        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( delegate2repo && 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 - get resource from parent, when delegating
+    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);
+            }
+        }
+        if( DEBUG ) log("getResource not found by parent " + loader);
+
+        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
+    {
+        return loadClass2( name, resolve, true );
+    }
+    
+    public Class loadClass2(String name, boolean resolve, boolean del2repo)
+        throws ClassNotFoundException
+    {
+
+        Class clazz = null;
+
+        // Don't load classes if class loader is stopped
+        if (!started) {
+            log("Not started " + this + " " + module);
+            //throw new ThreadDeath();
+            start();
+        }
+
+        // (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( del2repo && 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 + " xxx " + getParent() +  " " + repository.getName() );
+        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("ModuleClassLoader: " + s);
+    }
+    private void log(String s, Throwable t ) {
+        System.err.println("ModuleClassLoader: " + 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());
+    }
+}
+

Propchange: tomcat/sandbox/java/org/apache/tomcat/util/loader/ModuleClassLoader.java
------------------------------------------------------------------------------
    svn:executable = *

Added: tomcat/sandbox/java/org/apache/tomcat/util/loader/ModuleListener.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/java/org/apache/tomcat/util/loader/ModuleListener.java?rev=407715&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/loader/ModuleListener.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/loader/ModuleListener.java Thu May 18 22:18:18 2006
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+/**
+ * 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
+     * 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();
+
+}
\ No newline at end of file



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