You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ho...@apache.org on 2007/05/08 08:42:31 UTC

svn commit: r536097 - /geronimo/server/trunk/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java

Author: hogstrom
Date: Mon May  7 23:42:30 2007
New Revision: 536097

URL: http://svn.apache.org/viewvc?view=rev&rev=536097
Log:
Modified ClassLoader behaviour to optimize search process for requested classes.  This patch ensures that a ClassLoader in the multi-parent hierarchy is searched only once per load request.

Modified:
    geronimo/server/trunk/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java

Modified: geronimo/server/trunk/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java?view=diff&rev=536097&r1=536096&r2=536097
==============================================================================
--- geronimo/server/trunk/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java (original)
+++ geronimo/server/trunk/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java Mon May  7 23:42:30 2007
@@ -30,10 +30,12 @@
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.geronimo.kernel.classloader.UnionEnumeration;
 import org.apache.geronimo.kernel.repository.Artifact;
@@ -49,6 +51,7 @@
  * @version $Rev$ $Date$
  */
 public class MultiParentClassLoader extends URLClassLoader {
+    private static final Log log = LogFactory.getLog(MultiParentClassLoader.class);
     private final Artifact id;
     private final ClassLoader[] parents;
     private final boolean inverseClassLoading;
@@ -58,6 +61,32 @@
     private final String[] nonOverridableResources;
     private boolean destroyed = false;
 
+    // I used this pattern as its temporary and with the static final we get compile time 
+    // optimizations.
+    private final static int classLoaderSearchMode;
+    private final static int ORIGINAL_SEARCH = 1;
+    private final static int OPTIMIZED_SEARCH = 2;
+     
+    static {
+    	// Extract the classLoaderSearchMode if specified.  If not, default to "safe".
+    	String mode = System.getProperty("Xorg.apache.geronimo.kernel.config.MPCLSearchOption");
+    	int runtimeMode = OPTIMIZED_SEARCH;  // Default to optimized
+    	String runtimeModeMessage = "Original Classloading";
+    	if (mode != null) { 
+    		if (mode.equals("safe")) {
+                runtimeMode = ORIGINAL_SEARCH;
+                runtimeModeMessage = "Safe ClassLoading";
+    		} else if (mode.equals("optimized"))
+    			runtimeMode = OPTIMIZED_SEARCH;
+    	}
+    	
+		classLoaderSearchMode = runtimeMode;
+		log.info("ClassLoading behaviour has changed.  The "+runtimeModeMessage+" mode is in effect.  If you are experiencing a problem\n"+
+				 "you can change the behaviour by specifying -DXorg.apache.geronimo.kernel.config.MPCLSearchOption= property.  Specify \n"+
+				 "=\"safe\" to revert to the original behaviour.  This is a temporary change until we decide whether or not to make it\n"+
+				 "permanent for the 2.0 release");
+    }
+
     /**
      * Creates a named class loader with no parents.
      *
@@ -238,7 +267,26 @@
         super.addURL(url);
     }
 
-    protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+    /**
+     * TODO This method should be removed and replaced with the best classLoading option.  Its intent is to 
+     * provide a way for folks to switch back to the old classLoader if this fix breaks something.
+     */
+    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+    	if (classLoaderSearchMode == ORIGINAL_SEARCH) 
+    		return loadSafeClass(name, resolve);
+    	else
+    		return loadOptimizedClass(name, resolve);
+    }
+    
+    /**
+     * This method executes the old class loading behaviour before optimization.
+     * 
+     * @param name
+     * @param resolve
+     * @return
+     * @throws ClassNotFoundException
+     */
+    protected synchronized Class<?> loadSafeClass(String name, boolean resolve) throws ClassNotFoundException {
         //
         // Check if class is in the loaded classes cache
         //
@@ -303,6 +351,162 @@
         }
 
         throw new ClassNotFoundException(name + " in classloader " + id);
+    }    
+    
+    /**
+     * 
+     * Optimized classloading.
+     * 
+     * This method is the normal way to resolve class loads.  This method recursively calls its parents to resolve 
+     * classloading requests.  Here is the sequence of operations:
+     * 
+     *   1. Call findClass to see if we already have this class loaded.
+     *   2. If not, call the SystemClassLoader which needs to be called anyway.
+     *   3. Check if inverse loading, if so look in our class loader.
+     *   4. Search our parents, recursively.  Keeping track of which parents have already been called.
+     *      Since MultiParentClassLoaders can appear more than once we do not search an already searched classloader.
+     *   5. Search our classloader.  
+     * 
+     */
+    protected synchronized Class<?> loadOptimizedClass(String name, boolean resolve) throws ClassNotFoundException {
+    	// System.err.println("Started load for class "+name+" in classloader "+this);
+        //
+        // Check if class is in the loaded classes cache
+        //
+        Class cachedClass = findLoadedClass(name);
+        if (cachedClass != null) {
+            return resolveClass(cachedClass, resolve);
+        }
+
+        //
+        // No dice, let's offer the primordial loader a shot...
+        //
+        try {
+        	return resolveClass(findSystemClass(name), resolve);
+        } catch (ClassNotFoundException cnfe) {
+        	// ignore...just being a good citizen.
+        }
+
+        //
+        // if we are using inverse class loading, check local urls first
+        //
+        if (inverseClassLoading && !isDestroyed() && !isNonOverridableClass(name)) {
+            try {
+                Class clazz = findClass(name);
+                return resolveClass(clazz, resolve);
+            } catch (ClassNotFoundException ignored) {
+            }
+        }
+        
+        //
+        // Check parent class loaders
+        //
+        if (!isHiddenClass(name)) {
+       		try {
+       			LinkedList<ClassLoader> visitedClassLoaders = new LinkedList<ClassLoader>();
+                Class clazz = checkParents(name, resolve, visitedClassLoaders);
+                if (clazz != null) return resolveClass(clazz, resolve);
+      		} catch (ClassNotFoundException cnfe) {
+        			// ignore
+       		}
+        }
+
+        //
+        // if we are not using inverse class loading, check local urls now
+        //
+        // don't worry about excluding non-overridable classes here... we
+        // have alredy checked he parent and the parent didn't have the
+        // class, so we can override now
+        if (!isDestroyed()) {
+            try {
+                Class clazz = findClass(name);
+                return resolveClass(clazz, resolve);
+            } catch (ClassNotFoundException ignored) {
+            }
+        }
+
+        throw new ClassNotFoundException(name + " in classloader " + id);
+    }
+    
+    /**
+     * This method is an internal hook that allows us to be performant on Class lookups when multiparent
+     * classloaders are involved.  We can bypass certain lookups that have already occurred in the initiating
+     * classloader.  Also, we track the classLoaders that are visited by adding them to an already vistied list.
+     * In this way, we can bypass redundant checks for the same class.
+     * 
+     * @param name
+     * @param visitedClassLoaders
+     * @return
+     * @throws ClassNotFoundException
+     */
+    protected synchronized Class<?> loadClassInternal(String name, boolean resolve, LinkedList<ClassLoader> visitedClassLoaders) throws ClassNotFoundException {
+        //
+        // Check if class is in the loaded classes cache
+        //
+        Class cachedClass = findLoadedClass(name);
+        if (cachedClass != null) {
+            return resolveClass(cachedClass, resolve);
+        }
+
+        //
+        // Check parent class loaders
+        //
+        if (!isHiddenClass(name)) {
+            try {
+        	    Class clazz = checkParents(name, resolve, visitedClassLoaders);
+        	    if (clazz != null) return resolveClass(clazz,resolve);
+            } catch (ClassNotFoundException cnfe) {
+        	    // ignore
+            }
+        }
+        
+        //
+        // if we are not using inverse class loading, check local urls now
+        //
+        // don't worry about excluding non-overridable classes here... we
+        // have alredy checked he parent and the parent didn't have the
+        // class, so we can override now
+        if (!isDestroyed()) {
+        	Class clazz = findClass(name);
+            return resolveClass(clazz, resolve);
+        }
+
+        return null;  // Caler is expecting a class.  Null indicates CNFE and will save some time.
+    }
+
+    /**
+     * In order to optimize the classLoading process and visit a directed set of 
+     * classloaders this internal method for Geronimo MultiParentClassLoaders 
+     * is used.  Effectively, as each classloader is visited it is passed a linked
+     * list of classloaders that have already been visited and can safely be skipped.
+     * This method assumes the context of an MPCL and is not for use external to this class.
+     * 
+     * @param name
+     * @param visitedClassLoaders
+     * @return
+     * @throws ClassNotFoundException
+     */
+    private synchronized Class<?> checkParents(String name, boolean resolve, LinkedList<ClassLoader> visitedClassLoaders) throws ClassNotFoundException {
+     	for (ClassLoader parent : parents) {
+    		//  When we've encountered the primordial loader we are done.  Since we've already looked there we do not need
+     		// to repeat the check.  Simply return null and all things will be handled nicely.
+    		if (parent == ClassLoader.getSystemClassLoader()) return null;
+    		if (!visitedClassLoaders.contains(parent)) {
+    	        visitedClassLoaders.add(parent);  // Track that we've been here before
+    		    try {
+        	        if (parent instanceof MultiParentClassLoader) {
+        	        	Class clazz = ((MultiParentClassLoader) parent).loadClassInternal(name, resolve, visitedClassLoaders);
+        	        	if (clazz != null) return resolveClass(clazz, resolve);
+        	        } else {
+        	        	return parent.loadClass(name);
+        	        }
+    	    	} catch (ClassNotFoundException cnfe) {
+        	    	// ignore
+        	    }
+    		}
+    	} 
+     	// To avoid yet another CNFE we'll simply return null and let the caller handle appropriately.
+    	return null;
     }
 
     private boolean isNonOverridableClass(String name) {
@@ -531,5 +735,5 @@
         ClassLoaderRegistry.remove(this);
         super.finalize();
     }
-
+    
 }