User: juhalindfors Date: 02/03/17 02:30:31 Added: src/main/org/jboss/mx/loading UnifiedClassLoader.java UnifiedLoaderRepository.java Log: Unified class loader integration. Maybe still a bit flaky, but can handle at least the basic test -- two MLet loaders loading the same class definition only once and avoid a conflicting class definition. Revision Changes Path 1.1 jmx/src/main/org/jboss/mx/loading/UnifiedClassLoader.java Index: UnifiedClassLoader.java =================================================================== /*************************************** * * * JBoss: The OpenSource J2EE WebOS * * * * Distributable under LGPL license. * * See terms of license at gnu.org. * * * ***************************************/ package org.jboss.mx.loading; import java.net.URL; import java.net.URLClassLoader; import java.io.InputStream; import java.util.Map; import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap; /** * A ClassLoader which loads classes from a single URL in conjunction with * the {@link UnifiedLoaderRepository}. Notice that this classloader does * not work independently without the repository. A repository reference * must be provided via the constructor or the classloader must be explicitly * registered to the repository before any attempt to load a class. * * @author <a href="[EMAIL PROTECTED]">Marc Fleury</a> * @author <a href="[EMAIL PROTECTED]">Christoph G. Jung</a> * @author <a href="[EMAIL PROTECTED]">Scott Stark</a> * @author <a href="[EMAIL PROTECTED]">Juha Lindfors</a> * @version <tt>$Revision: 1.1 $</tt> * * <p><b>20010830 marc fleury:</b> * <ul> * <li>Initial import * </ul> * * <p><b>20011009 cgj:</b> * <ul> * <li>fixed default resolution behaviour * </ul> * * <p><b>20020315 Juha Lindfors:</b> * <ul> * <li> Support for adding your own class definitions instead of just an URL.</li> * * <li> Removed static reference to repository. This is now set as the classloader * is registered to the repository via setRepository() call. * </li> */ public class UnifiedClassLoader extends URLClassLoader { // Attributes ---------------------------------------------------- /** Reference to the unified repository. */ private UnifiedLoaderRepository repository = null; /** One URL per ClassLoader in our case */ private URL url = null; /** Class definitions */ private Map classes = new ConcurrentReaderHashMap(); // Constructors -------------------------------------------------- /** * Construct a <tt>UnifiedClassLoader</tt> without registering it to the * classloader repository. * * @param url the single URL to load classes from. */ public UnifiedClassLoader(URL url) { super(new URL[] {url}, UnifiedClassLoader.class.getClassLoader()); // FIXME: log it System.out.println("New UCL with url " + url); this.url = url; } /** * Construct a <tt>UnifiedClassLoader</tt> and registers it to the given * repository. * * @param url The single URL to load classes from. * @param repository the repository this classloader delegates to */ public UnifiedClassLoader(URL url, UnifiedLoaderRepository repository) { this(url); // set the repository reference this.repository = repository; // register this loader to the given repository repository.addClassLoader(this); } /** * Constructs a <tt>UnifiedClassLoader</tt> with given class definition. * * @param names class name * @param codes class definition */ public UnifiedClassLoader(String name, byte[] code) { // FIXME: UCL cloader or ctx cl as parent?? super(new URL[] {}, UnifiedClassLoader.class.getClassLoader()); addClass(name, code); } // Public -------------------------------------------------------- public void addClass(String name, byte[] code) { classes.put(name, code); } public void setRepository(UnifiedLoaderRepository repository) { this.repository = repository; } /** */ public Class loadClassLocally(String name, boolean resolve) throws ClassNotFoundException { // check our own class definitions foist, then delegate to parent Object o = classes.get(name); if (o != null) { byte[] code = (byte[])o; return defineClass(name, code, 0, code.length); } else { return super.loadClass(name, resolve); } } /** * Provides the same functionality as {@link java.net.URLClassLoader#getResource}. */ public URL getResourceLocally(String name) { return super.getResource(name); } /** */ public URL getURL() { return url; } /** * This method simply invokes the super.getURLs() method to access the * list of URLs that make up the UnifiedClassLoader classpath. */ public URL[] getClasspath() { return super.getURLs(); } // URLClassLoader overrides -------------------------------------- /** * We intercept the load class to know exactly the dependencies * of the underlying jar. * * <p>Forwards request to {@link UnifiedLoaderRepository}. */ public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { return repository.loadClass(name, resolve, this); } /** * Attempts to load the resource from its URL and if not found * forwards to the request to {@link UnifiedLoaderRepository}. */ public URL getResource(String name) { URL resource = super.getResource(name); if (resource == null) { resource = repository.getResource(name, this); } // if (resource == null) // { // if (log.isTraceEnabled()) // log.trace("Did not find the UCL resource: " + name); // } return resource; } /** */ public int hashCode() { // FIXME: right now we don't care about not hitting the same slot in case // of our own class definitions (as it's only used for optimized // dispatchers and those are never shared). return (url == null) ? classes.hashCode() : url.hashCode(); } /** */ public boolean equals(Object other) { if (other instanceof UnifiedClassLoader) { // FIXME: currently ignores equals check for instances that contain // their own class definitions. We're using this only for // optimized dispatcher classes and they're never equal URL url2 = ((UnifiedClassLoader)other).getURL(); return (url2 == null) ? false : url2.equals(url); } return false; } /** * Return an empty URL array to force the RMI marshalling subsystem to * use the <tt>java.server.codebase</tt> property as the annotated codebase. * * <p>Do not remove this method without discussing it on the dev list. * * @return Empty URL[] */ public URL[] getURLs() { return EMPTY_URL_ARRAY; } /** The value returned by {@link getURLs}. */ private static final URL[] EMPTY_URL_ARRAY = {}; /** * Retruns a string representaion of this UCL. */ public String toString() { return super.toString() + "{ url=" + getURL() + " }"; } } 1.1 jmx/src/main/org/jboss/mx/loading/UnifiedLoaderRepository.java Index: UnifiedLoaderRepository.java =================================================================== /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.mx.loading; import java.util.Set; import java.util.HashSet; import java.util.Map; import java.util.HashMap; import java.util.Iterator; import java.net.URL; import java.net.URLClassLoader; import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap; /** * <description> * * @see <related> * * @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a> * @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a> * @author <a href="mailto:[EMAIL PROTECTED]">Juha Lindfors</a>. * @version $Revision: 1.1 $ * */ public class UnifiedLoaderRepository extends LoaderRepository { // Attributes ---------------------------------------------------- /** * The classloaders we use for loading classes here. */ private Set classLoaders = new HashSet(); /** * Maps class names of the classes loaded here to the classes. */ private Map classes = new HashMap(); /** * Maps class loaders to the set of classes they loaded here. */ private Map clToClassSetMap = new HashMap(); /** * The version number of the {@link #clToClassSetMap} map. * If a lookup of a class detects a change in this while calling * the classloaders with locks removed, the {@link #clToClassSetMap} * and {@link #classes} fields should <em>only</em> be modified * if the classloader used for loading the class is still in the * {@link #classLoaders} set. */ private long clToClassSetMapVersion = 0; /** * Maps resource names of resources looked up here to the URLs used to * load them. */ private Map resources = new HashMap(); /** * Maps class loaders to the set of resource names they looked up here. */ private Map clToResourceSetMap = new HashMap(); /** * The version number of the {@link #clToResourceSetMap} map. * If a lookup of a resource detects a change in this while * calling the classloaders with locks removed, the * {@link #clToResourceSetMap} and {@link #resources} fields should * <em>only</em> be modified if the classloader used for loading * the class is still in the {@link #classLoaders} set. */ private long clToResourceSetMapVersion = 0; // Public -------------------------------------------------------- /** * Add a class to this repository. Allows a class to be added in * byte code form stored inside this repository. /** * Load a class in this repository. * * @param name The name of the class * @param resolve If <code>true</code>, the class will be resolved * @param scl The asking class loader * @return The loaded class. * resource could not be found. * @throws ClassNotFoundException If the class could not be found. */ public Class loadClass(String name, boolean resolve, ClassLoader scl) throws ClassNotFoundException { Class foundClass; Set classLoaders2; long clToClassSetMapVersion2; synchronized (this) { // Try the local map already foundClass = (Class)classes.get(name); if (foundClass != null) return foundClass; // Not found, make copies of the classLoader reference to avoid // working on a later version of it. classLoaders2 = classLoaders; // Save the current version of the class map, so we // can detect if it has changed. clToClassSetMapVersion2 = clToClassSetMapVersion; } // If not start asking around to URL classloaders for it // who will find it? UnifiedClassLoader cl = null; if (scl instanceof UnifiedClassLoader) { // First ask the asking classloader chances are the dependent class is in there try { foundClass = ((UnifiedClassLoader)scl).loadClassLocally(name, resolve); //If we get here we know the scl is the right one cl = (UnifiedClassLoader)scl; } catch (ClassNotFoundException ignored) { } } if (foundClass == null) { Iterator allLoaders = classLoaders2.iterator(); while (allLoaders.hasNext() && (foundClass == null)) { // next! cl = (UnifiedClassLoader)allLoaders.next(); if (!scl.equals(cl)) { try { foundClass = cl.loadClassLocally(name, resolve); } catch (ClassNotFoundException ignored2) { //try next loader } } } //allLoaders } if (foundClass != null) { synchronized (this) { // Did the version change? if (clToClassSetMapVersion2 != clToClassSetMapVersion) { // Yes. Is the class loader we used still here? if (!classLoaders.contains(cl)) { // No, it was removed from under us. // Don't change the maps, simply return the class. return foundClass; } } // We can keep track classes.put(name, foundClass); // When we cycle the cl we also need to remove the classes it loaded Set set = (Set)clToClassSetMap.get(cl); if (set == null) { set = new HashSet(); clToClassSetMap.put(cl, set); } set.add(name); } return foundClass; } // If we reach here, all of the classloaders currently in the VM don't know about the class throw new ClassNotFoundException(name); } /** * Find a resource from this repository. * * @param name The name of the resource * @param scl The asking class loader * @return An URL for reading the resource, or <code>null</code> if the * resource could not be found. */ public URL getResource(String name, ClassLoader scl) { Set classLoaders2; long clToResourceSetMapVersion2; URL resource = null; // First ask for the class to the asking class loader if (scl instanceof UnifiedClassLoader) resource = ((UnifiedClassLoader)scl).getResourceLocally(name); if (resource != null) return resource; synchronized (this) { // Is it in the global map? if (resources.containsKey(name)) return (URL)resources.get(name); // No, make copies of the classLoader reference to avoid working on // a later version of it. classLoaders2 = classLoaders; // Save the current version of the resource map, so we // can detect if it has changed. clToResourceSetMapVersion2 = clToResourceSetMapVersion; } // If not start asking around to URL classloaders for it for (Iterator iter = classLoaders2.iterator(); iter.hasNext();) { UnifiedClassLoader cl = (UnifiedClassLoader)iter.next(); if (!cl.equals(scl)) { // already tried this one resource = cl.getResourceLocally(name); if (resource != null) { synchronized (this) { // Did the version change? if (clToResourceSetMapVersion2 != clToResourceSetMapVersion) { // Yes. Is the class loader we used still here? if (!classLoaders.contains(cl)) { // No, it was removed from under us. // Don't change the maps, simply return the resource. return resource; } } // We can keep track resources.put(name, resource); // When we cycle the cl we also need to remove the classes it loaded Set set = (Set)clToResourceSetMap.get(cl); if (set == null) { set = new HashSet(); clToResourceSetMap.put(cl, set); } set.add(name); return resource; } } // if we found it } } // for all ClassLoaders, If we reach here, all of the classloaders currently in the VM // don't know about the resource return resource; } /** Iterates through the current class loaders and tries to find the given class name. @return the Class object for name if found, null otherwise. */ public Class findClass(String name) { Class clazz = null; Set classLoaders2; synchronized (this) { classLoaders2 = classLoaders; } /* We have to find the class as a resource as we don't want to invoke loadClass(name) and cause the side-effect of loading new classes. */ String classRsrcName = name.replace('.', '/') + ".class"; for(Iterator iter = classLoaders2.iterator(); iter.hasNext();) { UnifiedClassLoader cl = (UnifiedClassLoader)iter.next(); URL classURL = cl.getResource(classRsrcName); if( classURL != null ) { try { // Since the class was found we can load it which should be a noop clazz = cl.loadClass(name); } catch(ClassNotFoundException e) { System.out.println("Failed to load class: "+name+ "\n" +e.toString()); //log.debug("Failed to load class: "+name, e); } } } return clazz; } /** Obtain a listing of the URL for all UnifiedClassLoaders associated with *the ServiceLibraries */ public URL[] getURLs() { HashSet classpath = new HashSet(); Set classLoaders2; synchronized (this) { classLoaders2 = classLoaders; } for (Iterator iter = classLoaders2.iterator(); iter.hasNext();) { UnifiedClassLoader cl = (UnifiedClassLoader)iter.next(); URL[] urls = cl.getClasspath(); int length = urls != null ? urls.length : 0; for(int u = 0; u < length; u ++) { URL path = urls[u]; classpath.add(path); } } // for all ClassLoaders URL[] urls = new URL[classpath.size()]; classpath.toArray(urls); return urls; } // LoaderRepository overrides ------------------------------------ public Class loadClass(String className) throws ClassNotFoundException { // if someone comes to us directly through LoaderRepository interface // notice that UCL loaders will skip this and invoke // loadClass(name, resolve, cl) directly return loadClass(className, false, Thread.currentThread().getContextClassLoader()); } public Class loadClassWithout(ClassLoader loader, String className) throws ClassNotFoundException { throw new Error("NYI"); } public void addClassLoader(ClassLoader loader) { // if you come to us as UCL we send you straight to the orbit if (loader instanceof UnifiedClassLoader) addUnifiedClassLoader((UnifiedClassLoader)loader); // if you come to us as URLCL we'll slice you up and // orbit UCL per URL else if (loader instanceof URLClassLoader) { URLClassLoader ucl = (URLClassLoader)loader; URL[] urls = ucl.getURLs(); for (int i = 0; i < urls.length; ++i) { addUnifiedClassLoader(new UnifiedClassLoader(urls[i], this)); } } else // addNonDelegatingClassLoader(loader); throw new RuntimeException("ULR only allows UCL to be added"); } private synchronized void addUnifiedClassLoader(UnifiedClassLoader cl) { cl.setRepository(this); // we should only keep the first classloader declared if (!classLoaders.contains(cl)) { // We create a new copy of the classLoaders set. classLoaders = new HashSet(classLoaders); classLoaders.add(cl); // FIXME: log it System.out.println("Libraries adding UnifiedClassLoader " + cl.hashCode() + " key URL " + cl.getURL()); } else { // FIXME: log it System.out.println("Libraries skipping duplicate UnifiedClassLoader for key URL " + cl.getURL()); } } public synchronized void removeClassLoader(ClassLoader cl) { if (!classLoaders.contains(cl)) return; // nothing to remove // We create a new copy of the classLoaders set. classLoaders = new HashSet(classLoaders); classLoaders.remove(cl); if (clToClassSetMap.containsKey(cl)) { // We have a new version of the map ++clToClassSetMapVersion; Set clClasses = (Set)clToClassSetMap.remove(cl); for (Iterator iter = clClasses.iterator(); iter.hasNext();) { Object o = iter.next(); Object o1 = classes.remove(o); //if( trace ) // log.trace("removing class " + o + ", removed: " + o1); } } // Same procedure for resources if (clToResourceSetMap.containsKey(cl)) { ++clToResourceSetMapVersion; Set clResources = (Set)clToResourceSetMap.remove(cl); for (Iterator iter = clResources.iterator(); iter.hasNext();) { Object o = iter.next(); Object o1 = resources.remove(o); //if( trace ) // log.trace("removing resource " + o + ", removed: " + o1); } } } }
_______________________________________________ Jboss-development mailing list [EMAIL PROTECTED] https://lists.sourceforge.net/lists/listinfo/jboss-development