Fixed a bug where class loader delegation for dynamic imports was happening
when it shouldn't. Specifically, if a bundle exports a given package or
has any sort of wire for that package (i.e., import-package or require-bundle),
then it should not be able to dynamically import that package. Previously,
only existing imported packages were being checked, not exported packages
or packages acquired through require-bundle. (FELIX-724)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@695618 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
index e22f08d..1c8b60f 100755
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
@@ -613,59 +613,15 @@
         return null;
     }
 
-    private IRequirement createDynamicRequirementTarget(
-        IRequirement dynReq, String pkgName)
-    {
-        IRequirement req = null;
-
-        // First check to see if the dynamic requirement matches the
-        // package name; this means we have to do wildcard matching.
-        String dynPkgName = ((Requirement) dynReq).getPackageName();
-        boolean wildcard = (dynPkgName.lastIndexOf(".*") >= 0);
-        dynPkgName = (wildcard)
-            ? dynPkgName.substring(0, dynPkgName.length() - 2) : dynPkgName;
-        // If the dynamic requirement matches the package name, then
-        // create a new requirement for the specific package.
-        if (dynPkgName.equals("*") ||
-            pkgName.equals(dynPkgName) ||
-            (wildcard && pkgName.startsWith(dynPkgName + ".")))
-        {
-            // Create a new requirement based on the dynamic requirement,
-            // but substitute the precise package name for which we are
-            // looking, because it is not possible to use the potentially
-            // wildcarded version in the dynamic requirement.
-            R4Directive[] dirs = ((Requirement) dynReq).getDirectives();
-            R4Attribute[] attrs = ((Requirement) dynReq).getAttributes();
-            R4Attribute[] newAttrs = new R4Attribute[attrs.length];
-            System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
-            for (int attrIdx = 0; attrIdx < newAttrs.length; attrIdx++)
-            {
-                if (newAttrs[attrIdx].getName().equals(ICapability.PACKAGE_PROPERTY))
-                {
-                    newAttrs[attrIdx] = new R4Attribute(
-                        ICapability.PACKAGE_PROPERTY, pkgName, false);
-                    break;
-                }
-            }
-            req = new Requirement(ICapability.PACKAGE_NAMESPACE, dirs, newAttrs);
-        }
-
-        return req;
-    }
-
     private IWire attemptDynamicImport(IModule importer, String pkgName)
     {
         R4Wire wire = null;
         PackageSource candidate = null;
 
-        // There is an overriding assumption here that a package is
-        // never split across bundles. If a package can be split
-        // across bundles, then this will fail.
-
-        // Only attempt to dynamically import a package if the module does
-        // not already have a wire for the package; this may be the case if
-        // the class being searched for actually does not exist.
-        if (Util.getWire(importer, pkgName) == null)
+        // We can only search dynamic imports if the bundle
+        // doesn't import, export, nor require the package in
+        // question. Check these conditions first.
+        if (isDynamicImportAllowed(importer, pkgName))
         {
             // Loop through the importer's dynamic requirements to determine if
             // there is a matching one for the package from which we want to
@@ -746,6 +702,73 @@
         return null;
     }
 
+    private boolean isDynamicImportAllowed(IModule importer, String pkgName)
+    {
+        // If any of the module exports this package, then we cannot
+        // attempt to dynamically import it.
+        ICapability[] caps = importer.getDefinition().getCapabilities();
+        for (int i = 0; (caps != null) && (i < caps.length); i++)
+        {
+            if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
+                && caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
+            {
+                return false;
+            }
+        }
+        // If any of our wires have this package, then we cannot
+        // attempt to dynamically import it.
+        IWire[] wires = importer.getWires();
+        for (int i = 0; (wires != null) && (i < wires.length); i++)
+        {
+            if (wires[i].hasPackage(pkgName))
+            {
+                return false;
+            }
+        }
+        // Ok to attempt to dynamically import the package.
+        return true;
+    }
+
+    private IRequirement createDynamicRequirementTarget(
+        IRequirement dynReq, String pkgName)
+    {
+        IRequirement req = null;
+
+        // First check to see if the dynamic requirement matches the
+        // package name; this means we have to do wildcard matching.
+        String dynPkgName = ((Requirement) dynReq).getPackageName();
+        boolean wildcard = (dynPkgName.lastIndexOf(".*") >= 0);
+        dynPkgName = (wildcard)
+            ? dynPkgName.substring(0, dynPkgName.length() - 2) : dynPkgName;
+        // If the dynamic requirement matches the package name, then
+        // create a new requirement for the specific package.
+        if (dynPkgName.equals("*") ||
+            pkgName.equals(dynPkgName) ||
+            (wildcard && pkgName.startsWith(dynPkgName + ".")))
+        {
+            // Create a new requirement based on the dynamic requirement,
+            // but substitute the precise package name for which we are
+            // looking, because it is not possible to use the potentially
+            // wildcarded version in the dynamic requirement.
+            R4Directive[] dirs = ((Requirement) dynReq).getDirectives();
+            R4Attribute[] attrs = ((Requirement) dynReq).getAttributes();
+            R4Attribute[] newAttrs = new R4Attribute[attrs.length];
+            System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
+            for (int attrIdx = 0; attrIdx < newAttrs.length; attrIdx++)
+            {
+                if (newAttrs[attrIdx].getName().equals(ICapability.PACKAGE_PROPERTY))
+                {
+                    newAttrs[attrIdx] = new R4Attribute(
+                        ICapability.PACKAGE_PROPERTY, pkgName, false);
+                    break;
+                }
+            }
+            req = new Requirement(ICapability.PACKAGE_NAMESPACE, dirs, newAttrs);
+        }
+
+        return req;
+    }
+
     private boolean resolveDynamicImportCandidate(IModule provider, IModule importer)
         throws ResolveException
     {
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4Wire.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4Wire.java
index 7085e59..e4fdfed 100755
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4Wire.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4Wire.java
@@ -27,10 +27,10 @@
 
 public class R4Wire implements IWire
 {
-    private IModule m_importer = null;
-    private IRequirement m_requirement = null;
-    private IModule m_exporter = null;
-    private ICapability m_capability = null;
+    private final IModule m_importer;
+    private final IRequirement m_requirement;
+    private final IModule m_exporter;
+    private final ICapability m_capability;
 
     public R4Wire(IModule importer, IRequirement requirement,
         IModule exporter, ICapability capability)
@@ -76,6 +76,15 @@
     /* (non-Javadoc)
      * @see org.apache.felix.framework.searchpolicy.IWire#getClass(java.lang.String)
      */
+    public boolean hasPackage(String pkgName)
+    {
+        return (m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
+            m_capability.getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName));
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.felix.framework.searchpolicy.IWire#getClass(java.lang.String)
+     */
     public Class getClass(String name) throws ClassNotFoundException
     {
         Class clazz = null;
@@ -181,4 +190,4 @@
         }
         return m_importer + " -> " + m_capability + " -> " + m_exporter;
     }
-}
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java
index 1f18f79..b60967f 100644
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java
@@ -30,12 +30,12 @@
 
 public class R4WireModule implements IWire
 {
-    private IModule m_importer = null;
-    private IRequirement m_requirement = null;
-    private IModule m_exporter = null;
-    private ICapability m_capability = null;
-    private Map m_pkgMap = null;
-    
+    private final IModule m_importer;
+    private final IRequirement m_requirement;
+    private final IModule m_exporter;
+    private final ICapability m_capability;
+    private final Map m_pkgMap;
+
     public R4WireModule(IModule importer, IRequirement requirement,
         IModule exporter, ICapability capability, Map pkgMap)
     {
@@ -45,7 +45,7 @@
         m_capability = capability;
         m_pkgMap = pkgMap;
     }
-    
+
     /* (non-Javadoc)
      * @see org.apache.felix.framework.searchpolicy.IWire#getImporter()
      */
@@ -53,7 +53,7 @@
     {
         return m_importer;
     }
-    
+
     /* (non-Javadoc)
      * @see org.apache.felix.framework.searchpolicy.IWire#getRequirement()
      */
@@ -69,7 +69,7 @@
     {
         return m_exporter;
     }
-    
+
     /* (non-Javadoc)
      * @see org.apache.felix.framework.searchpolicy.IWire#getCapability()
      */
@@ -79,6 +79,14 @@
     }
 
     /* (non-Javadoc)
+     * @see org.apache.felix.framework.searchpolicy.IWire#hasPackage(java.lang.String)
+     */
+    public boolean hasPackage(String pkgName)
+    {
+        return (m_pkgMap.get(pkgName) != null);
+    }
+
+    /* (non-Javadoc)
      * @see org.apache.felix.framework.searchpolicy.IWire#getClass(java.lang.String)
      */
     public Class getClass(String name) throws ClassNotFoundException
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/IWire.java b/framework/src/main/java/org/apache/felix/moduleloader/IWire.java
index abb871e..f0f0204 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/IWire.java
+++ b/framework/src/main/java/org/apache/felix/moduleloader/IWire.java
@@ -27,6 +27,7 @@
     public IRequirement getRequirement();
     public IModule getExporter();
     public ICapability getCapability();
+    public boolean hasPackage(String pkgName);
     public Class getClass(String name) throws ClassNotFoundException;
     public URL getResource(String name) throws ResourceNotFoundException;
     public Enumeration getResources(String name) throws ResourceNotFoundException;