Make implicit boot delegation configurable. (FELIX-712)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@808561 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
index d07390f..581cdb6 100644
--- a/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
@@ -123,6 +123,9 @@
     private final String[] m_bootPkgs;
     private final boolean[] m_bootPkgWildcards;
 
+    // Boolean flag to enable/disable implicit boot delegation.
+    private final boolean m_implicitBootDelegation;
+
     // Re-usable security manager for accessing class context.
     private static SecurityManagerEx m_sm = new SecurityManagerEx();
 
@@ -167,6 +170,7 @@
         m_declaredActivationPolicy = EAGER_ACTIVATION;
         m_activationExcludes = null;
         m_activationIncludes = null;
+        m_implicitBootDelegation = false;
     }
 
     public ModuleImpl(
@@ -187,6 +191,12 @@
         m_bootPkgs = bootPkgs;
         m_bootPkgWildcards = bootPkgWildcards;
 
+        m_implicitBootDelegation =
+            (m_configMap.get(FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP) == null)
+            || Boolean.valueOf(
+                (String) m_configMap.get(
+                    FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP)).booleanValue();
+
         ManifestParser mp = new ManifestParser(m_logger, m_configMap, m_headerMap);
 
         // Record some of the parsed metadata. Note, if this is an extension
@@ -1374,82 +1384,87 @@
                 : (Object) wire.getResource(name);
         }
 
-        // At this point, the class/resource could not be found by the bundle's
-        // static or dynamic imports, nor its own content. Before we throw
-        // an exception, we will try to determine if the instigator of the
-        // class/resource load was a class from a bundle or not. This is necessary
-        // because the specification mandates that classes on the class path
-        // should be hidden (except for java.*), but it does allow for these
-        // classes/resources to be exposed by the system bundle as an export.
-        // However, in some situations classes on the class path make the faulty
-        // assumption that they can access everything on the class path from
-        // every other class loader that they come in contact with. This is
-        // not true if the class loader in question is from a bundle. Thus,
-        // this code tries to detect that situation. If the class
-        // instigating the load request was NOT from a bundle, then we will
-        // make the assumption that the caller actually wanted to use the
-        // parent class loader and we will delegate to it. If the class was
-        // from a bundle, then we will enforce strict class loading rules
-        // for the bundle and throw an exception.
-
-        // Get the class context to see the classes on the stack.
-        Class[] classes = m_sm.getClassContext();
-        // Start from 1 to skip security manager class.
-        for (int i = 1; i < classes.length; i++)
+        // If implicit boot delegation is enabled, then try to guess whether
+        // we should boot delegate.
+        if (m_implicitBootDelegation)
         {
-            // Find the first class on the call stack that is not from
-            // the class loader that loaded the Felix classes or is not
-            // 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 ignore inner classes of class loaders, since we can
-            // assume they are a class loader too.
+            // At this point, the class/resource could not be found by the bundle's
+            // static or dynamic imports, nor its own content. Before we throw
+            // an exception, we will try to determine if the instigator of the
+            // class/resource load was a class from a bundle or not. This is necessary
+            // because the specification mandates that classes on the class path
+            // should be hidden (except for java.*), but it does allow for these
+            // classes/resources to be exposed by the system bundle as an export.
+            // However, in some situations classes on the class path make the faulty
+            // assumption that they can access everything on the class path from
+            // every other class loader that they come in contact with. This is
+            // not true if the class loader in question is from a bundle. Thus,
+            // this code tries to detect that situation. If the class instigating
+            // the load request was NOT from a bundle, then we will make the
+            // assumption that the caller actually wanted to use the parent class
+            // loader and we will delegate to it. If the class was
+            // from a bundle, then we will enforce strict class loading rules
+            // for the bundle and throw an exception.
+
+            // Get the class context to see the classes on the stack.
+            Class[] classes = m_sm.getClassContext();
+            // Start from 1 to skip security manager class.
+            for (int i = 1; i < classes.length; i++)
+            {
+                // Find the first class on the call stack that is not from
+                // the class loader that loaded the Felix classes or is not
+                // 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 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.
-            if (Thread.class.equals(classes[i]))
-            {
-                break;
-            }
-            else if (isClassNotLoadedFromBundle(classes[i]))
-            {
-                // 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())
+                // 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.
+                if (Thread.class.equals(classes[i]))
                 {
-                    if (ModuleClassLoader.class.isInstance(cl))
-                    {
-                        delegate = false;
-                        break;
-                    }
+                    break;
                 }
-                // 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.isAssignableFrom(classes[i - 1]))
+                else if (isClassNotLoadedFromBundle(classes[i]))
                 {
-                    try
+                    // 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())
                     {
-                        // Return the class or resource from the parent class loader.
-                        return (isClass)
-                            ? (Object) this.getClass().getClassLoader().loadClass(name)
-                            : (Object) this.getClass().getClassLoader().getResource(name);
+                        if (ModuleClassLoader.class.isInstance(cl))
+                        {
+                            delegate = false;
+                            break;
+                        }
                     }
-                    catch (NoClassDefFoundError ex)
+                    // 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.isAssignableFrom(classes[i - 1]))
                     {
-                        // Ignore, will return null
+                        try
+                        {
+                            // Return the class or resource from the parent class loader.
+                            return (isClass)
+                                ? (Object) this.getClass().getClassLoader().loadClass(name)
+                                : (Object) this.getClass().getClassLoader().getResource(name);
+                        }
+                        catch (NoClassDefFoundError ex)
+                        {
+                            // Ignore, will return null
+                        }
                     }
+                    break;
                 }
-                break;
             }
         }
 
diff --git a/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java b/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
index 6131ea3..e3a46ba 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
@@ -51,6 +51,7 @@
     public static final String BUNDLE_STARTLEVEL_PROP
         = "felix.startlevel.bundle";
     public static final String SERVICE_URLHANDLERS_PROP = "felix.service.urlhandlers";
+    public static final String IMPLICIT_BOOT_DELEGATION_PROP = "felix.bootdelegation.implicit";
 
     // Start level-related constants.
     public static final int FRAMEWORK_INACTIVE_STARTLEVEL = 0;
diff --git a/main/src/main/resources/config.properties b/main/src/main/resources/config.properties
index 515e7c5..0134b89 100644
--- a/main/src/main/resources/config.properties
+++ b/main/src/main/resources/config.properties
@@ -31,6 +31,11 @@
 # available to all bundles. You should avoid using this property.
 #org.osgi.framework.bootdelegation=sun.*,com.sun.*
 
+# Felix tries to guess when to implicitly boot delegate in certain
+# situations to ease integration without outside code. This feature
+# is enabled by default, uncomment the following line to disable it.
+#felix.bootdelegation.implicit=false
+
 # The following property explicitly specifies the location of the bundle
 # cache, which defaults to "felix-cache" in the current working directory.
 # If this value is not absolute, then the felix.cache.rootdir controls