Modified: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/util/loader/Repository.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/util/loader/Repository.java?view=diff&rev=534293&r1=534292&r2=534293 ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/util/loader/Repository.java (original) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/util/loader/Repository.java Tue May 1 19:22:45 2007 @@ -14,314 +14,220 @@ * limitations under the License. */ - package org.apache.tomcat.util.loader; - import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.net.MalformedURLException; import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; import java.util.Enumeration; -import java.util.HashMap; import java.util.Hashtable; +import java.util.Iterator; import java.util.Map; import java.util.Vector; - /** - * A group of modules. - * - * Modules can have one or more jars and classes dirs, and are reloaded as a - * unit. Each module has an internal class loader. - * - * The module will delegate to the associated repository in addition to the - * normal delegation rules. The repository will search on all sibling modules. + * Class loader similar with the MLetClassLoader and JBoss - it maintains a list + * of 'modules' ( similar with mbeans - a single jar or dir ), each of them can + * be reloaded independently, without reloading the whole repository. * - * This mechanism is defined in the MLetClassLoader and is also used by JBoss - * and few other servers. - * - * Use: - * - create a Repository in all situations where you would need a separate - * class loader - each webapp, common/, server/, etc. - * - set the parent hierarchy - * - add modules to each repo - a module can be a single jar/classes, or - * multiple jars. I think single jar modules may be better. - * - get the classloader from repository + * This works well as long as the interface between modules don't change - it is + * a bit better than hot-reloading of classes, which requires each class method + * signatures to be unchanged. * * @author Costin Manolache */ public class Repository { - - private static final boolean DEBUG=System.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=System.getProperty("loader.Repository.noIndex") == null; - - private Vector loaders=new Vector(); - private String name; - private Vector grpModules=new Vector(); - //private transient Loader loader; - - private transient RepositoryClassLoader groupClassLoader; - private Hashtable prefixes=new Hashtable(); - // For delegation - private ClassLoader parentClassLoader; - private Repository parent; + // Use jar indexes. Most loaders don't - it's a small optimization, + // but it's a good practice. + private static final boolean USE_IDX = true; + Vector loaders = new Vector(); - public Repository() { - } + private RepositoryClassLoader groupClassLoader; - public void addModule( Module mod ) { - mod.setRepository( this ); + private ClassLoader parent; - grpModules.addElement(mod); - - if( parentClassLoader != null ) - mod.setParentClassLoader( parentClassLoader ); + // From jar index: package -> Module or Module[] + private Hashtable prefixes = new Hashtable(); + + // Collects all jars without INDEX.LIST. + private ArrayList missingIndexList = new ArrayList(); - if(! mod.isStarted()) { - mod.start(); + /** + * Should this class loader delegate to the parent class loader + * <strong>before</strong> searching its own repositories (i.e. the usual + * Java2 delegation model)? + */ + private boolean delegate = true; + + public Repository() { + this(null, null, true); + } + + public Repository(URL[] cp, ClassLoader parent) { + this(cp, parent, true); + } + + public Repository(URL[] cp, ClassLoader parent, boolean delegate) { + if (parent == null) { + parent = Thread.currentThread().getContextClassLoader(); } - - 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(); + this.parent = parent; + if (cp != null) { + for (int i = 0; i < cp.length; i++) { + addURL(cp[i]); } - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); } } - - public void newModule( String path ) { - Module m=new Module(); - m.setPath( path ); - addModule( m ); - } - - public Enumeration getModules() { - return grpModules.elements(); + + public void setParentClassLoader(ClassLoader myL) { + this.parent = myL; } - /** Reload any module that is modified + /** + * Return a class loader associated with the group. This will delegate to + * all modules in the group, then to parent. */ - public void checkReload() { - try { - Enumeration mE=grpModules.elements(); - while( mE.hasMoreElements() ) { - Module m=(Module)mE.nextElement(); - boolean modif=m.modified(); - log("Modified " + m + " " + modif); - - if( modif ) { - m.stop(); - m.start(); + public URLClassLoader getClassLoader() { + if (groupClassLoader == null) { + if (parent == null) { + groupClassLoader = new RepositoryClassLoader(new URL[0], this); + } else { + groupClassLoader = new RepositoryClassLoader(new URL[0], parent, + this); } } - } catch( Throwable t ) { - t.printStackTrace(); - } + return groupClassLoader; } - /** Verify if any module is modified. This is a deep search, including dirs. - * Expensive operation. - * - * @return - */ - public boolean isModified() { - try { - Enumeration mE=grpModules.elements(); - while( mE.hasMoreElements() ) { - Module m=(Module)mE.nextElement(); - boolean modif=m.modified(); - log("Modified " + m + " " + modif); - if( modif ) return true; - } - } catch( Throwable t ) { - t.printStackTrace(); - } - return false; + public void addURL(URL url) { + ModuleClassLoader mod = new ModuleClassLoader(new URL[] { url }, + parent, delegate, this); + addModuleClassLoader(mod); } - Repository getParent() { - return parent; + public void addURL(URL[] url) { + for (int i = 0; i < url.length; i++) { + addURL(url[i]); + } } - public String toString() { - return "Repository " + name + "(" + getClasspathString() + ")"; + Enumeration getModules() { + return loaders.elements(); } - - public String getClasspathString() { - StringBuffer sb=new StringBuffer(); - Enumeration mE=grpModules.elements(); - while( mE.hasMoreElements() ) { - Module m=(Module)mE.nextElement(); - sb.append( m.getClasspathString() + ":"); + + ModuleClassLoader createClassLoader(URL[] classpath) throws Exception { + ModuleClassLoader classLoader = null; + if (parent == null) { + parent = Thread.currentThread().getContextClassLoader(); } - return sb.toString(); + classLoader = new ModuleClassLoader(classpath, parent, + delegate, this); + + loaders.addElement(classLoader); + + return classLoader; } /** + * Verify if any module is modified. This is a deep search, including dirs. * - * @param parent The parent group - */ - 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. - * - * If this is a StandardClassLoader instance, it will be able to delegate - * to the group. - * - * If it's a regular ClassLoader - it'll be searched for classes, but - * it will not be able to delegate to peers. - * - * In future we may fine tune this by using manifests. + * @return */ - void addClassLoader(ClassLoader cl ) { - if( ( cl instanceof ModuleClassLoader )) { - ((ModuleClassLoader)cl).setRepository(this); + public boolean isModified() { + try { + Enumeration mE = loaders.elements(); + while (mE.hasMoreElements()) { + ModuleClassLoader m = (ModuleClassLoader) mE.nextElement(); + boolean modif = m.modified(); + if (modif) + return true; + } + } catch (Throwable t) { + t.printStackTrace(); } - loaders.addElement(cl); - // log("Adding classloader " + cl); - } - - public String getName() { - return name; + return false; } - public void removeClassLoader(ClassLoader cl) { - int oldSize=loaders.size(); - loaders.removeElement(cl); - - if(DEBUG) log("removed " + loaders.size() + "/" + oldSize + ": " + cl); - // TODO: remove from index + public String toString() { + return "Repository " + "(" + getClasspathString() + ")"; } - /** Return a class loader associated with the group. - * This will delegate to all modules in the group, then to parent. - * - * @return - */ - public ClassLoader getClassLoader() { - if( groupClassLoader==null ) { - - 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 RepositoryClassLoader(new URL[0], this); - } else { - groupClassLoader=new RepositoryClassLoader(new URL[0], pcl, this); + public String getClasspathString() { + StringBuffer sb = new StringBuffer(); + Enumeration mE = loaders.elements(); + while (mE.hasMoreElements()) { + ModuleClassLoader m = (ModuleClassLoader) mE.nextElement(); + URL[] urls = m.getURLs(); + for (int i = 0; i < urls.length; i++) { + sb.append(urls[i].getFile()).append(":"); } - if( DEBUG ) log("---------- Created repository loader " + pcl ); } - return groupClassLoader; + return sb.toString(); } - public void addDir(File directory) { - try { - URL url=directory.toURL(); - Module mod = new Module(); - mod.setClasspath(new URL[] {url}); - addModule(mod); - } catch (MalformedURLException e) { - e.printStackTrace(); - } - } - - public void addLibs(File directory) { - if (!directory.isDirectory() || !directory.exists() || - !directory.canRead()) { - if (DEBUG) - log(" Not found: "+ directory.getAbsolutePath()); - return; - } - 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]); - try { - URL url=file.toURL(); - Module mod = new Module(); - mod.setClasspath(new URL[] {url}); - addModule(mod); - } catch (MalformedURLException e) { - e.printStackTrace(); + + void addModuleClassLoader(ModuleClassLoader mod) { + loaders.addElement(mod); + try { + if (USE_IDX) { + processJarIndex(mod); } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); } } - - /** - * Find a class in the group. It'll iterate over each loader - * and try to find the class - using only the method that - * search locally or on parent ( i.e. not in group, to avoid - * recursivity ). - * - * + + /** + * Find a class in the group. It'll iterate over each loader and try to find + * the class - using only the method that search locally or on parent ( i.e. + * not in group, to avoid recursivity ). + * + * * @param classN * @return */ - Class findClass(ClassLoader caller, String classN ) { - Class clazz=null; - + 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; + 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 ModuleClassLoader) { + ModuleClassLoader m = (ModuleClassLoader) mO; try { - Class c=((ModuleClassLoader)m.getClassLoader()).findLocalClass(classN); - //log("Prefix: " +prefix + " " + classN + " " + m); + Class c = m.findLocalClass(classN); + // log("Prefix: " +prefix + " " + classN + " " + m); return c; } catch (Exception e) { - //log("Prefix err: " +prefix + " " + classN + " " + m + " " + e); - //return null; + // 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]; + ModuleClassLoader mA[] = (ModuleClassLoader[]) mO; + for (int i = 0; i < mA.length; i++) { + ModuleClassLoader m = mA[i]; try { - Class c=((ModuleClassLoader)m.getClassLoader()).findLocalClass(classN); - //log("Prefix: " +prefix + " " + classN + " " + m); + Class c = m.findLocalClass(classN); + // log("Prefix: " +prefix + " " + classN + " " + m); return c; } catch (Exception e) { - //log("Prefix err: " +prefix + " " + classN + " " + m + " " + e); - //return null; + // log("Prefix err: " +prefix + " " + classN + " " + + // m + " " + e); + // return null; } } } @@ -329,185 +235,592 @@ } // TODO: move the vector to a [] - for( int i=loaders.size()-1; i>=0; i-- ) { - + for (int i = loaders.size() - 1; i >= 0; i--) { + // TODO: for regular CL, just use loadClass, they'll not recurse - // The behavior for non-SCL or not in the group loader is the same as for parent loader - ModuleClassLoader cl=(ModuleClassLoader)loaders.elementAt(i); + // The behavior for non-SCL or not in the group loader is the same + // as for + // parent loader + ModuleClassLoader cl = (ModuleClassLoader) loaders.elementAt(i); // TODO: move loaders with index in separate vector - //if( cl.getModule().hasIndex ) continue; - if( cl== caller ) continue; - //if( classN.indexOf("SmtpCoyoteProtocolHandler") > 0 ) { - //log("try " + cl.debugObj + " " + name + " " + classN + " " + loaders.size()); - //} + // if( cl.getModule().hasIndex ) continue; + if (cl == caller) + continue; + // if( classN.indexOf("SmtpCoyoteProtocolHandler") > 0 ) { + // log("try " + cl.debugObj + " " + name + " " + classN + " " + + // loaders.size()); + // } try { - if( cl instanceof ModuleClassLoader ) { - clazz=((ModuleClassLoader)cl).findLocalClass(classN ); + if (cl instanceof ModuleClassLoader) { + clazz = ((ModuleClassLoader) cl).findLocalClass(classN); } else { - clazz=cl.findClass(classN); + clazz = cl.findClass(classN); } - //System.err.println("GRPLD: " + classN + " from " + info.get(cl)); + // System.err.println("GRPLD: " + classN + " from " + + // info.get(cl)); return clazz; } catch (ClassNotFoundException e) { - //System.err.println("CNF: " + classN + " " + info.get(cl) ); - //if( classN.indexOf("smtp") > 0 ) e.printStackTrace(); + // System.err.println("CNF: " + classN + " " + info.get(cl) ); + // if( classN.indexOf("smtp") > 0 ) e.printStackTrace(); } } return null; } - + /** * @param loader * @param name2 * @return */ 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-- ) { + URL url = null; + for (int i = loaders.size() - 1; i >= 0; i--) { // TODO: for regular CL, just use loadClass, they'll not recurse - // The behavior for non-SCL or not in the group loader is the same as for parent loader - ModuleClassLoader cl=(ModuleClassLoader)loaders.elementAt(i); - if( cl== caller ) continue; - url=((ModuleClassLoader)cl).findResource(classN ); - if( url!=null ) + // The behavior for non-SCL or not in the group loader is the same + // as for + // parent loader + ModuleClassLoader cl = (ModuleClassLoader) loaders.elementAt(i); + if (cl == caller) + continue; + url = ((ModuleClassLoader) cl).findResource(classN); + if (url != null) return url; } return null; } - private void log(String s) { - System.err.println("Repository (" + name + "): " + s ); + public Class findClass(String name) throws ClassNotFoundException { + Enumeration modulesE = getModules(); + while (modulesE.hasMoreElements()) { + try { + ModuleClassLoader m = (ModuleClassLoader) modulesE + .nextElement(); + return m.findClass2(name, false); + } catch (ClassNotFoundException ex) { + // ignore + } + } + throw new ClassNotFoundException(name); + } + + public URL findResource(final String name) { + URL url = null; + Enumeration modulesE = getModules(); + while (modulesE.hasMoreElements()) { + ModuleClassLoader m = (ModuleClassLoader) modulesE.nextElement(); + url = m.findResource2(name, false); + if (url != null) { + return url; + } + } + return null; + } + + public Enumeration findResources(String name) throws IOException { + Vector result = new Vector(); + + Enumeration modulesE = getModules(); + while (modulesE.hasMoreElements()) { + ModuleClassLoader m = (ModuleClassLoader) modulesE.nextElement(); + Enumeration myRes = m.findResources2(name, false); + if (myRes != null) { + while (myRes.hasMoreElements()) { + result.addElement(myRes.nextElement()); + } + } + } + + return result.elements(); + } + + public URL getResource(String name) { + + URL url = null; + Enumeration modulesE = getModules(); + while (modulesE.hasMoreElements()) { + ModuleClassLoader m = (ModuleClassLoader) modulesE.nextElement(); + url = m.getResource2(name, null, false); + if (url != null) { + return url; + } + } + + return null; + } + + public Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + + Enumeration modulesE = getModules(); + while (modulesE.hasMoreElements()) { + try { + ModuleClassLoader m = (ModuleClassLoader) modulesE + .nextElement(); + return m.loadClass2(name, resolve, false); + } catch (ClassNotFoundException ex) { + // ignore + } + } + + throw new ClassNotFoundException(name); } + /** - * @param name2 + * Reload any module that is modified */ - public void setName(String name2) { - this.name=name2; + public void checkReload() { + try { + Enumeration mE = loaders.elements(); + ArrayList toReload = null; + while (mE.hasMoreElements()) { + ModuleClassLoader m = (ModuleClassLoader) mE.nextElement(); + boolean modif = m.modified(); + if (modif) { + toReload = new ArrayList(); + toReload.add(m); + } + } + Iterator reloadIt = toReload.iterator(); + while (reloadIt.hasNext()) { + ModuleClassLoader m = (ModuleClassLoader) reloadIt.next(); + URL[] cp = m.getURLs(); + loaders.removeElement(m); + missingIndexList.remove(m); + if (prefixes.containsValue(m)) { + ArrayList toRemove = new ArrayList(); + Iterator prefI = prefixes.entrySet().iterator(); + while (prefI.hasNext()) { + Map.Entry entry = (Map.Entry) prefI.next(); + if (entry.getValue() == m) { + toRemove.add(entry.getKey()); + } + } + prefI = toRemove.iterator(); + while (prefI.hasNext()) { + prefixes.remove(prefI.next()); + } + } + + m = createClassLoader(cp); + loaders.addElement(m); + } + + } catch (Throwable t) { + t.printStackTrace(); + } } - // Collects all jars without INDEX.LIST. Can be used for debugging. - public static Map missingIndexList = new HashMap(); - - /* - * Work in progress: - * - * -use the INDEX.LIST to get prefixes to avoid linear - * search in repositories. - * - * - serialize the state ( including timestamps ) to improve startup time - * ( avoids the need to open all jars - if INDEX.LIST is ok) - */ - + /** * Read the index. The index contain packages and top level resources * * @param cl * @throws Exception */ - private void processJarIndex(Module m) throws Exception { - ModuleClassLoader cl=(ModuleClassLoader)m.createClassLoader(); - // only support index for modules with a single jar in CP - String cp=m.getClasspathString(); - if( ! cp.endsWith(".jar")) return; - URL urlIdx=cl.findResource("META-INF/INDEX.LIST"); - if( urlIdx == null ) { - missingIndexList.put(cp, "1"); + private void processJarIndex(ModuleClassLoader cl) throws Exception { + String cp = cl.getURLs()[0].toString(); + if (!cp.endsWith(".jar")) { + missingIndexList.add(cl); + return; + } + + // ModuleClassLoader cl = (ModuleClassLoader) m.createClassLoader(); + URL urlIdx = cl.findResource("META-INF/INDEX.LIST"); + if (urlIdx == null) { + missingIndexList.add(cl); return; } + try { - InputStream is=urlIdx.openStream(); - if( is==null ) { - log("Can't read " + urlIdx + " " + m.getClasspathString()); + InputStream is = urlIdx.openStream(); + if (is == null) { + missingIndexList.add(cl); + return; + } + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String line = br.readLine(); + if (line == null) { + missingIndexList.add(cl); return; } - BufferedReader br=new BufferedReader( new InputStreamReader(is) ); - String line=br.readLine(); - if( line==null ) return; - if( ! line.startsWith( "JarIndex-Version:") || - ! line.endsWith("1.0")) { - log("Invalid signature " + line + " " + m.getClasspathString()); + if (!line.startsWith("JarIndex-Version:") || !line.endsWith("1.0")) { + missingIndexList.add(cl); + return; } br.readLine(); // "" - - while( readSection(br, m) ) { + while (readSection(br, cl)) { } - - m.hasIndex=true; } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + missingIndexList.add(cl); } } - - private boolean readSection( BufferedReader br, Module m) throws IOException { - String jarName=br.readLine(); - if( jarName==null ) return false; // done - if( "".equals( jarName )) { - log("Invalid jarName " + jarName + " " + m.getClasspathString() ); - return false; - } - //log("Index " + jarName + " " + m.getClasspathString()); - String prefix=null; - while( ((prefix=br.readLine()) != null ) && - (! "".equals( prefix )) ) { - //log("found " + prefix + " " + m); - Object o=prefixes.get(prefix); - if( o == null ) { + + private boolean readSection(BufferedReader br, ModuleClassLoader m) + throws IOException { + String jarName = br.readLine(); + if (jarName == null) + return false; // done + if ("".equals(jarName)) { + throw new IOException("Invalid jarName " + jarName); + } + + String prefix = null; + while (((prefix = br.readLine()) != null) && (!"".equals(prefix))) { + Object o = prefixes.get(prefix); + if (o == null) { + // Common case prefixes.put(prefix, m); } else { - Module mA[]=null; - if( o instanceof Module ) { - mA=new Module[2]; - mA[0]=(Module)o; - mA[1]=m; + ModuleClassLoader mA[] = null; + if (o instanceof ModuleClassLoader) { + mA = new ModuleClassLoader[2]; + mA[0] = (ModuleClassLoader) o; + mA[1] = m; } else { - Object oldA[]=(Module[])o; - mA=new Module[oldA.length + 1]; + Object oldA[] = (ModuleClassLoader[]) o; + mA = new ModuleClassLoader[oldA.length + 1]; System.arraycopy(oldA, 0, mA, 0, oldA.length); - mA[oldA.length]=m; + mA[oldA.length] = m; } - prefixes.put( prefix, mA); - //log("Multiple prefixes: " + prefix + " " + mA); - + prefixes.put(prefix, mA); } } - - return prefix!=null; + return prefix != null; } - - /** Read loader.REPO.cache from work dir - * - * This file will hold timestamps for each module/jar and cache the INDEX - - * to avoid opening the jars/modules that are not used - * - * @throws IOException - */ - private void readCachedIdx() throws IOException { - - } - - /** Check the index and verify that: - * - all jars are older than timestamp and still exist - * - there are no new jars + /** + * Class loader associated with a repository. * - * @throws IOException + * This class loader will never load any class by itself it will just + * delegate to modules. */ - private void checkCacheIdx() throws IOException { - - } - - - 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 + static class RepositoryClassLoader extends URLClassLoader { - // we'll use the cache - + private Repository repository; + + public RepositoryClassLoader(URL repositories[], ClassLoader parent, + Repository lg) { + super(repositories, parent); + this.repository = lg; + } + + public RepositoryClassLoader(URL repositories[], Repository lg) { + super(repositories); + this.repository = lg; + } + public void addURL(URL url) { + repository.addURL(url); + } + + public Class findClass(String name) throws ClassNotFoundException { + return repository.findClass(name); + } + + public URL findResource(final String name) { + return repository.findResource(name); + } + + public Enumeration findResources(String name) throws IOException { + return repository.findResources(name); + } + + public URL getResource(String name) { + return repository.getResource(name); + } + + public Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + return repository.loadClass(name, resolve); + } } + static class ModuleClassLoader extends URLClassLoader { + public ModuleClassLoader(URL repositories[], ClassLoader parent, + boolean delegate, Repository lg) { + super(repositories, parent); + this.delegate = delegate; + this.repository = lg; + lastModified = System.currentTimeMillis(); + } + + protected long lastModified = 0L; + + protected Repository repository; + + protected boolean delegate = false; + + // ---------------------------------------------------- ClassLoader + // Methods + + public Class findClass(String name) throws ClassNotFoundException { + return findClass2(name, true); + } + + public Class findClass2(String name, boolean del2repo) + throws ClassNotFoundException { + if (del2repo) { + return repository.findClass(name); + } + Class clazz = super.findClass(name); + // if( clazz.getClassLoader() != this ) + // log("findClass() FOUND " + clazz + " Loaded by " + + // clazz.getClassLoader()); + 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. + */ + public Class findLocalClass(String name) throws ClassNotFoundException { + Class clazz = findLoadedClass(name); + if (clazz != null) { + return (clazz); + } + return findClass(name); + } + + public URL findResource(final String name) { + return findResource2(name, true); + } + + public URL findResource2(final String name, boolean del2repo) { + if (del2repo) { + return repository.findResource(name); + } + return super.findResource(name); + } + + public Enumeration findResources(String name) throws IOException { + return findResources2(name, true); + } + + Enumeration findResources2(String name, boolean del2repo) + throws IOException { + if (del2repo) { + return repository.findResources(name); + } + return super.findResources(name); + } + + 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 ??) + // log("getResource() found locally " + delegate + " " + + // name + " " + url); + return (url); + } + + // (3) load from one of the classes in the group + if (delegate2repo && repository != null) { + url = repository.findResource(this, name); + if (url != null) { + // log("getResource() FOUND from group " + + // repository.getName() + // + " " + name + " " + url); + return url; + } + } + + // (4) Delegate to parent unconditionally if not already attempted + if (!delegate) { + url = getResourceParentDelegate(name); + if (url != null) + return url; + } + + // (5) Resource was not found + return (null); + + } + + // TODO: repository should handle this ! + // 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(); + } + url = loader.getResource(name); + if (url != null) { + // log("getResource() found by system " + delegate + " " + + // name + " " + url); + return (url); + } + return url; + } + + 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; + + // (0) Check our previously loaded local class cache + clazz = findLoadedClass(name); + if (clazz != null) { + if (resolve) + resolveClass(clazz); + return (clazz); + } + + // (1) 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) { + if (resolve) + resolveClass(clazz); + return (clazz); + } + } catch (ClassNotFoundException e) { + } + + // 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) { + // 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) { + // log("loadClass - FOUND findClass " + delegate + " " + + // name + " , " + resolve); + if (resolve) + resolveClass(clazz); + return (clazz); + } + } catch (ClassNotFoundException e) { + } + + // (3) Other peer loaders + if (del2repo && repository != null) { + Class cls = repository.findClass(this, name); + if (cls != null) { + // log("loadClass(): FOUND from group " + + // repository.getName() + // + " " + name); + if (resolve) + resolveClass(clazz); + return cls; + } + } + + // (4) Delegate to parent unconditionally + if (!delegateLoad) { + ClassLoader loader = getParent(); + if (loader != null) { + try { + clazz = loader.loadClass(name); + if (clazz != null) { + // log("loadClass() FOUND parent " + delegate + " " + // + + // name + " , " + resolve); + if (resolve) + resolveClass(clazz); + return (clazz); + } + } catch (ClassNotFoundException e) { + ; + } + } + } + throw new ClassNotFoundException(name); + } + + /** + * Have one or more classes or resources been modified so that a reload + * is appropriate? + */ + boolean modified() { + URL cp[] = getURLs(); + if (cp != null) { + for (int i = 0; i < cp.length; i++) { + // assume dirs are used only for debug and small + File f = new File(cp[i].getFile()); + if (f.isDirectory()) { + return null != dirCheck(f, 10); + } else { + long lm = f.lastModified(); + if (lm > lastModified) { + return true; + } + } + } + } + + return (false); + } + + private File dirCheck(File dir, int level) { + File subd[] = dir.listFiles(); + for (int i = 0; i < subd.length; i++) { + if (subd[i].isDirectory() && level > 0) { + return dirCheck(subd[i], level - 1); + } else { + long lm = subd[i].lastModified(); + if (lm > lastModified) { + return subd[i]; + } + } + } + return null; + } + } }
Added: tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/util/res/StringManager.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/util/res/StringManager.java?view=auto&rev=534293 ============================================================================== --- tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/util/res/StringManager.java (added) +++ tomcat/sandbox/tomcat-lite/java/org/apache/tomcat/util/res/StringManager.java Tue May 1 19:22:45 2007 @@ -0,0 +1,283 @@ +/* + * 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.res; + +import java.text.MessageFormat; +import java.util.Hashtable; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * An internationalization / localization helper class which reduces + * the bother of handling ResourceBundles and takes care of the + * common cases of message formating which otherwise require the + * creation of Object arrays and such. + * + * <p>The StringManager operates on a package basis. One StringManager + * per package can be created and accessed via the getManager method + * call. + * + * <p>The StringManager will look for a ResourceBundle named by + * the package name given plus the suffix of "LocalStrings". In + * practice, this means that the localized information will be contained + * in a LocalStrings.properties file located in the package + * directory of the classpath. + * + * <p>Please see the documentation for java.util.ResourceBundle for + * more information. + * + * @author James Duncan Davidson [EMAIL PROTECTED] + * @author James Todd [EMAIL PROTECTED] + * @author Mel Martinez [EMAIL PROTECTED] + * @see java.util.ResourceBundle + */ + +public class StringManager { + + /** + * The ResourceBundle for this StringManager. + */ + + private ResourceBundle bundle; + private Locale locale; + + /** + * Creates a new StringManager for a given package. This is a + * private method and all access to it is arbitrated by the + * static getManager method call so that only one StringManager + * per package will be created. + * + * @param packageName Name of package to create StringManager for. + */ + + private StringManager(String packageName) { + this( packageName, Locale.getDefault() ); + } + + private StringManager(String packageName, Locale loc) { + String bundleName = packageName + ".LocalStrings"; + bundle = ResourceBundle.getBundle(bundleName, loc); + // Get the actual locale, which may be different from the requested one + locale = bundle.getLocale(); + } + + private StringManager(ResourceBundle bundle ) + { + this.bundle=bundle; + locale = bundle.getLocale(); + } + + /** + Get a string from the underlying resource bundle or return + null if the String is not found. + + @param key to desired resource String + @return resource String matching <i>key</i> from underlying + bundle or null if not found. + @throws IllegalArgumentException if <i>key</i> is null. + */ + + public String getString(String key) { + if(key == null){ + String msg = "key may not have a null value"; + + throw new IllegalArgumentException(msg); + } + + String str = null; + + try{ + str = bundle.getString(key); + }catch(MissingResourceException mre){ + //bad: shouldn't mask an exception the following way: + // str = "[cannot find message associated with key '" + key + "' due to " + mre + "]"; + // because it hides the fact that the String was missing + // from the calling code. + //good: could just throw the exception (or wrap it in another) + // but that would probably cause much havoc on existing + // code. + //better: consistent with container pattern to + // simply return null. Calling code can then do + // a null check. + str = null; + } + + return str; + } + + /** + * Get a string from the underlying resource bundle and format + * it with the given set of arguments. + * + * @param key + * @param args + */ + + public String getString(String key, Object[] args) { + String iString = null; + String value = getString(key); + + // this check for the runtime exception is some pre 1.1.6 + // VM's don't do an automatic toString() on the passed in + // objects and barf out + + try { + // ensure the arguments are not null so pre 1.2 VM's don't barf + if(args==null){ + args = new Object[1]; + } + + Object[] nonNullArgs = args; + for (int i=0; i<args.length; i++) { + if (args[i] == null) { + if (nonNullArgs==args){ + nonNullArgs=(Object[])args.clone(); + } + nonNullArgs[i] = "null"; + } + } + if( value==null ) value=key; + MessageFormat mf = new MessageFormat(value); + mf.setLocale(locale); + iString = mf.format(nonNullArgs, new StringBuffer(), null).toString(); + } catch (IllegalArgumentException iae) { + StringBuffer buf = new StringBuffer(); + buf.append(value); + for (int i = 0; i < args.length; i++) { + buf.append(" arg[" + i + "]=" + args[i]); + } + iString = buf.toString(); + } + return iString; + } + + /** + * Get a string from the underlying resource bundle and format it + * with the given object argument. This argument can of course be + * a String object. + * + * @param key + * @param arg + */ + + public String getString(String key, Object arg) { + Object[] args = new Object[] {arg}; + return getString(key, args); + } + + /** + * Get a string from the underlying resource bundle and format it + * with the given object arguments. These arguments can of course + * be String objects. + * + * @param key + * @param arg1 + * @param arg2 + */ + + public String getString(String key, Object arg1, Object arg2) { + Object[] args = new Object[] {arg1, arg2}; + return getString(key, args); + } + + /** + * Get a string from the underlying resource bundle and format it + * with the given object arguments. These arguments can of course + * be String objects. + * + * @param key + * @param arg1 + * @param arg2 + * @param arg3 + */ + + public String getString(String key, Object arg1, Object arg2, + Object arg3) { + Object[] args = new Object[] {arg1, arg2, arg3}; + return getString(key, args); + } + + /** + * Get a string from the underlying resource bundle and format it + * with the given object arguments. These arguments can of course + * be String objects. + * + * @param key + * @param arg1 + * @param arg2 + * @param arg3 + * @param arg4 + */ + + public String getString(String key, Object arg1, Object arg2, + Object arg3, Object arg4) { + Object[] args = new Object[] {arg1, arg2, arg3, arg4}; + return getString(key, args); + } + // -------------------------------------------------------------- + // STATIC SUPPORT METHODS + // -------------------------------------------------------------- + + private static Hashtable managers = new Hashtable(); + + /** + * Get the StringManager for a particular package. If a manager for + * a package already exists, it will be reused, else a new + * StringManager will be created and returned. + * + * @param packageName The package name + */ + public synchronized static StringManager getManager(String packageName) { + StringManager mgr = (StringManager)managers.get(packageName); + if (mgr == null) { + mgr = new StringManager(packageName); + managers.put(packageName, mgr); + } + return mgr; + } + + /** + * Get the StringManager for a particular package. If a manager for + * a package already exists, it will be reused, else a new + * StringManager will be created and returned. + * + * @param bundle The resource bundle + */ + public synchronized static StringManager getManager(ResourceBundle bundle) { + return new StringManager( bundle ); + } + + /** + * Get the StringManager for a particular package and Locale. If a manager for + * a package already exists, it will be reused, else a new + * StringManager will be created for that Locale and returned. + * + * @param packageName The package name + * @param loc The locale + */ + + public synchronized static StringManager getManager(String packageName,Locale loc) { + StringManager mgr = (StringManager)managers.get(packageName+"_"+loc.toString()); + if (mgr == null) { + mgr = new StringManager(packageName,loc); + managers.put(packageName+"_"+loc.toString(), mgr); + } + return mgr; + } + +} --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]