Applied patch (FELIX-962) to improve our attempts to determine when it is
necessary to boot delegate.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@750088 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java
index d02bf26..0276ee2 100644
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java
@@ -37,7 +37,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.StringTokenizer;
 import java.util.Vector;
 import org.apache.felix.framework.Felix.FelixResolver;
 import org.apache.felix.framework.Logger;
@@ -432,149 +431,137 @@
 
     public Class getClassByDelegation(String name) throws ClassNotFoundException
     {
-        Set requestSet = (Set) m_cycleCheck.get();
-        if (requestSet == null)
-        {
-            requestSet = new HashSet();
-            m_cycleCheck.set(requestSet);
-        }
-        if (!requestSet.contains(name))
-        {
-            requestSet.add(name);
-            try
-            {
-                return getClassLoader().loadClass(name);
-            }
-            finally
-            {
-                requestSet.remove(name);
-            }
-        }
-        return null;
+        return getClassLoader().loadClass(name);
     }
 
     public URL getResourceByDelegation(String name)
     {
-        Set requestSet = (Set) m_cycleCheck.get();
-        if (requestSet == null)
+        try
         {
-            requestSet = new HashSet();
-            m_cycleCheck.set(requestSet);
+            return (URL) findClassOrResourceByDelegation(name, false);
         }
-        if (!requestSet.contains(name))
+        catch (ClassNotFoundException ex)
         {
-            requestSet.add(name);
-            try
-            {
-                return (URL) findClassOrResourceByDelegation(name, false);
-            }
-            catch (ClassNotFoundException ex)
-            {
-                // This should never be thrown because we are loading resources.
-            }
-            catch (ResourceNotFoundException ex)
-            {
-                m_logger.log(
-                    Logger.LOG_DEBUG,
-                    ex.getMessage(),
-                    ex);
-            }
-            finally
-            {
-                requestSet.remove(name);
-            }
+            // This should never be thrown because we are loading resources.
         }
-
+        catch (ResourceNotFoundException ex)
+        {
+            m_logger.log(
+                Logger.LOG_DEBUG,
+                ex.getMessage(),
+                ex);
+        }
         return null;
     }
 
     private Object findClassOrResourceByDelegation(String name, boolean isClass)
         throws ClassNotFoundException, ResourceNotFoundException
     {
-        // First, try to resolve the originating module.
-        try
-        {
-            m_resolver.resolve(this);
-        }
-        catch (ResolveException ex)
-        {
-            if (isClass)
-            {
-                // We do not use the resolve exception as the
-                // cause of the exception, since this would
-                // potentially leak internal module information.
-                throw new ClassNotFoundException(
-                    name + ": cannot resolve package "
-                    + ex.getRequirement());
-            }
-            else
-            {
-                // The spec states that if the bundle cannot be resolved, then
-                // only the local bundle's resources should be searched. So we
-                // will ask the module's own class path.
-                URL url = getResourceLocal(name);
-                if (url != null)
-                {
-                    return url;
-                }
-
-                // We need to throw a resource not found exception.
-                throw new ResourceNotFoundException(
-                    name + ": cannot resolve package "
-                    + ex.getRequirement());
-            }
-        }
-
-        // Get the package of the target class/resource.
-        String pkgName = (isClass)
-            ? Util.getClassPackage(name)
-            : Util.getResourcePackage(name);
-
-        // Delegate any packages listed in the boot delegation
-        // property to the parent class loader.
         Object result = null;
-        if (shouldBootDelegate(pkgName))
+
+        Set requestSet = (Set) m_cycleCheck.get();
+        if (requestSet == null)
+        {
+            requestSet = new HashSet();
+            m_cycleCheck.set(requestSet);
+        }
+        if (requestSet.add(name))
         {
             try
             {
-                result = (isClass)
-                    ? (Object) getClass().getClassLoader().loadClass(name)
-                    : (Object) getClass().getClassLoader().getResource(name);
-                // If this is a java.* package, then always terminate the
-                // search; otherwise, continue to look locally if not found.
-                if (pkgName.startsWith("java.") || (result != null))
+                // First, try to resolve the originating module.
+                m_resolver.resolve(this);
+
+                // Get the package of the target class/resource.
+                String pkgName = (isClass)
+                    ? Util.getClassPackage(name)
+                    : Util.getResourcePackage(name);
+
+                // Delegate any packages listed in the boot delegation
+                // property to the parent class loader.
+                if (shouldBootDelegate(pkgName))
                 {
-                    return result;
+                    try
+                    {
+                        result = (isClass)
+                            ? (Object) getClass().getClassLoader().loadClass(name)
+                            : (Object) getClass().getClassLoader().getResource(name);
+                        // If this is a java.* package, then always terminate the
+                        // search; otherwise, continue to look locally if not found.
+                        if (pkgName.startsWith("java.") || (result != null))
+                        {
+                            return result;
+                        }
+                    }
+                    catch (ClassNotFoundException ex)
+                    {
+                        // If this is a java.* package, then always terminate the
+                        // search; otherwise, continue to look locally if not found.
+                        if (pkgName.startsWith("java."))
+                        {
+                            throw ex;
+                        }
+                    }
+                }
+
+                // Look in the module's imports. Note that the search may
+                // be aborted if this method throws an exception, otherwise
+                // it continues if a null is returned.
+                result = searchImports(name, isClass);
+
+                // If not found, try the module's own class path.
+                if (result == null)
+                {
+                    result = (isClass)
+                        ? (Object) getClassLoader().findClass(name)
+                        : (Object) getResourceLocal(name);
+
+                    // If still not found, then try the module's dynamic imports.
+                    if (result == null)
+                    {
+                        result = searchDynamicImports(name, pkgName, isClass);
+                    }
                 }
             }
-            catch (ClassNotFoundException ex)
+            catch (ResolveException ex)
             {
-                // If this is a java.* package, then always terminate the
-                // search; otherwise, continue to look locally if not found.
-                if (pkgName.startsWith("java."))
+                if (isClass)
                 {
-                    throw ex;
+                    // We do not use the resolve exception as the
+                    // cause of the exception, since this would
+                    // potentially leak internal module information.
+                    throw new ClassNotFoundException(
+                        name + ": cannot resolve package "
+                        + ex.getRequirement());
                 }
+                else
+                {
+                    // The spec states that if the bundle cannot be resolved, then
+                    // only the local bundle's resources should be searched. So we
+                    // will ask the module's own class path.
+                    URL url = getResourceLocal(name);
+                    if (url != null)
+                    {
+                        return url;
+                    }
+
+                    // We need to throw a resource not found exception.
+                    throw new ResourceNotFoundException(
+                        name + ": cannot resolve package "
+                        + ex.getRequirement());
+                }
+            }
+            finally
+            {
+                requestSet.remove(name);
             }
         }
-
-        // Look in the module's imports. Note that the search may
-        // be aborted if this method throws an exception, otherwise
-        // it continues if a null is returned.
-        result = searchImports(name, isClass);
-
-        // If not found, try the module's own class path.
-        if (result == null)
+        else
         {
-            result = (isClass)
-                ? (Object) getClassLoader().findClass(name)
-                : (Object) getResourceLocal(name);
-
-            // If still not found, then try the module's dynamic imports.
-            if (result == null)
-            {
-                result = searchDynamicImports(name, pkgName, isClass);
-            }
+            // If a cycle is detected, we should return null to break the
+            // cycle. This should only ever be return to internal class
+            // loading code and not to the actual instigator of the class load.
+            return null;
         }
 
         if (result == null)
@@ -1217,28 +1204,27 @@
             // a class loader or class itself, because we want to ignore
             // calls to ClassLoader.loadClass() and Class.forName() since
             // we are trying to find out who instigated the class load.
-            // Also since Felix uses threads for changing the start level
+            // Also ignore inner classes of class loaders, since we can
+            // assume they are a class loader too.
+
+// TODO: FRAMEWORK - This check is a hack and we should see if we can think
+// of another way to do it, since it won't necessarily work in all situations.
+            // Since Felix uses threads for changing the start level
             // and refreshing packages, it is possible that there is no
             // module classes on the call stack; therefore, as soon as we
             // see Thread on the call stack we exit this loop. Other cases
             // where modules actually use threads are not an issue because
             // the module classes will be on the call stack before the
             // Thread class.
-// TODO: FRAMEWORK - This check is a hack and we should see if we can think
-// of another way to do it, since it won't necessarily work in all situations.
             if (Thread.class.equals(classes[i]))
             {
                 break;
             }
-            else if ((this.getClass().getClassLoader() != classes[i].getClassLoader())
-                && !ClassLoader.class.isAssignableFrom(classes[i])
-                && !Class.class.equals(classes[i])
-                && !Proxy.class.equals(classes[i]))
+            else if (isClassNotLoadedFromBundle(classes[i]))
             {
-                // If there are no bundles providing exports for this
-                // package and if the instigating class was not from a
-                // bundle, then delegate to the parent class loader.
-                // Otherwise, break out of loop and return null.
+                // If the instigating class was not from a bundle,
+                // then delegate to the parent class loader; otherwise,
+                // break out of loop and return null.
                 boolean delegate = true;
                 for (ClassLoader cl = classes[i].getClassLoader(); cl != null; cl = cl.getClass().getClassLoader())
                 {
@@ -1248,12 +1234,9 @@
                         break;
                     }
                 }
-                // If delegate is true then there are no bundles
-                // providing exports for this package and the instigating
-                // class was not from a bundle. Therefore,
-                // delegate to the parent class loader in case
-                // that this is not due to outside code calling a method
-                // on the bundle interface (e.g., Bundle.loadClass()).
+                // Delegate to the parent class loader unless this call
+                // is due to outside code calling a method on the bundle
+                // interface (e.g., Bundle.loadClass()).
                 if (delegate && !Bundle.class.isInstance(classes[i - 1]))
                 {
                     try
@@ -1275,6 +1258,27 @@
         return null;
     }
 
+    private boolean isClassNotLoadedFromBundle(Class clazz) throws ClassNotFoundException
+    {
+        int idx = clazz.getName().lastIndexOf('$');
+        if (idx > 0)
+        {
+            if (clazz.getClassLoader() != null)
+            {
+                Class outerClass = clazz.getClassLoader().loadClass(
+                    clazz.getName().substring(0, idx));
+                if (outerClass != null)
+                {
+                    clazz = outerClass;
+                }
+            }
+        }
+        return this.getClass().getClassLoader() != clazz.getClassLoader()
+            && !ClassLoader.class.isAssignableFrom(clazz)
+            && !Class.class.equals(clazz)
+            && !Proxy.class.equals(clazz);
+    }
+
     private boolean shouldBootDelegate(String pkgName)
     {
         boolean result = false;
@@ -1320,6 +1324,7 @@
 
     private static final Constructor m_dexFileClassConstructor;
     private static final Method m_dexFileClassLoadClass;
+
     static
     {
         Constructor dexFileClassConstructor = null;