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 2005/09/29 08:35:48 UTC
cvs commit: jakarta-tomcat-connectors/util/loader/org/apache/tomcat/util/loader Loader.java Module.java ModuleClassLoader.java ModuleListener.java Repository.java package.html LoaderProperties.java
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: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org