Dynamic imports were not being resolved 100% correctly; they didn't cycle
through all candidates, nor did they check for class space consistency.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@547006 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 31f97c7..19e4b44 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
@@ -497,10 +497,6 @@
         return null;
     }
 
-// TODO: FRAMEWORK - This implements dynamic imports incorrectly, since it
-//       doesn't iteratively try each "in use" candidate first and it doesn't
-//       do a class space consistency check for the module gaining the
-//       dynamic wire.
     private IWire attemptDynamicImport(IModule importer, String pkgName)
     {
         R4Wire wire = null;
@@ -556,34 +552,30 @@
                         // Lock module manager instance to ensure that nothing changes.
                         synchronized (m_factory)
                         {
-                            // First check "in use" candidates for a match.
-                            PackageSource[] candidates = getInUseCandidates(req);
-                            // If there is an "in use" candidate, just take the first one.
-                            if (candidates.length > 0)
-                            {
-                                candidate = candidates[0];
-                            }
+                            // Get "in use" and "available" candidates and put
+                            // the "in use" candidates first.
+                            PackageSource[] inuse = getInUseCandidates(req);
+                            PackageSource[] available = getUnusedCandidates(req);
+                            PackageSource[] candidates = new PackageSource[inuse.length + available.length];
+                            System.arraycopy(inuse, 0, candidates, 0, inuse.length);
+                            System.arraycopy(available, 0, candidates, inuse.length, available.length);
 
-                            // If there were no "in use" candidates, then try "available"
-                            // candidates.
-                            if (candidate == null)
+                            // Take the first candidate that can resolve.
+                            for (int candIdx = 0;
+                                (candidate == null) && (candIdx < candidates.length);
+                                candIdx++)
                             {
-                                candidates = getUnusedCandidates(req);
-
-                                // Take the first candidate that can resolve.
-                                for (int candIdx = 0;
-                                    (candidate == null) && (candIdx < candidates.length);
-                                    candIdx++)
+                                try
                                 {
-                                    try
+                                    if (resolveDynamicImportCandidate(
+                                        candidates[candIdx].m_module, importer))
                                     {
-                                        resolve(candidates[candIdx].m_module);
                                         candidate = candidates[candIdx];
                                     }
-                                    catch (ResolveException ex)
-                                    {
-                                        // Ignore candidates that cannot resolve.
-                                    }
+                                }
+                                catch (ResolveException ex)
+                                {
+                                    // Ignore candidates that cannot resolve.
                                 }
                             }
 
@@ -622,6 +614,104 @@
         return null;
     }
 
+    private boolean resolveDynamicImportCandidate(IModule provider, IModule importer)
+        throws ResolveException
+    {
+        // If the provider of the dynamically imported package is not
+        // resolved, then we need to calculate the candidates to resolve
+        // it and see if there is a consistent class space for the
+        // provider. If there is no consistent class space, then a resolve
+        // exception is thrown.
+        Map candidatesMap = new HashMap();
+        if (!isResolved(provider))
+        {
+            populateCandidatesMap(candidatesMap, provider);
+            findConsistentClassSpace(candidatesMap, provider);
+        }
+
+        // If the provider can be successfully resolved, then verify that
+        // its class space is consistent with the existing class space of the
+        // module that instigated the dynamic import.
+        Map moduleMap = new HashMap();
+        Map importerPkgMap = getModulePackages(moduleMap, importer, candidatesMap);
+
+        // Now we need to calculate the "uses" constraints of every package
+        // accessible to the provider module based on its current candidates.
+        Map usesMap = usesMap = calculateUsesConstraints(provider, moduleMap, candidatesMap);
+
+        // Verify that none of the provider's implied "uses" constraints
+        // in the uses map conflict with anything in the importing module's
+        // package map.
+        for (Iterator iter = usesMap.entrySet().iterator(); iter.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) iter.next();
+
+            // For the given "used" package, get that package from the
+            // importing module's package map, if present.
+            ResolvedPackage rp = (ResolvedPackage) importerPkgMap.get(entry.getKey());
+
+            // If the "used" package is also visible to the importing
+            // module, make sure there is no conflicts in the implied
+            // "uses" constraints.
+            if (rp != null)
+            {
+                // Clone the resolve package so we can modify it.
+                rp = (ResolvedPackage) rp.clone();
+
+                // Loop through all implied "uses" constraints for the current
+                // "used" package and verify that all package sources are
+                // compatible with the package source of the importing module's
+                // package map.
+                List constraintList = (List) entry.getValue();
+                for (int constIdx = 0; constIdx < constraintList.size(); constIdx++)
+                {
+                    // Get a specific "uses" constraint for the current "used"
+                    // package.
+                    ResolvedPackage rpUses = (ResolvedPackage) constraintList.get(constIdx);
+                    // Determine if the implied "uses" constraint is compatible with
+                    // the improting module's package sources for the given "used"
+                    // package. They are compatible if one is the subset of the other.
+                    // Retain the union of the two sets if they are compatible.
+                    if (rpUses.isSubset(rp))
+                    {
+                        // Do nothing because we already have the superset.
+                    }
+                    else if (rp.isSubset(rpUses))
+                    {
+                        // Keep the superset, i.e., the union.
+                        rp.m_sourceList.clear();
+                        rp.m_sourceList.addAll(rpUses.m_sourceList);
+                    }
+                    else
+                    {
+                        m_logger.log(
+                            Logger.LOG_DEBUG,
+                            "Constraint violation for " + importer
+                            + " detected; module can see "
+                            + rp + " and " + rpUses);
+                        return false;
+                    }
+                }
+            }
+        }
+
+        Map resolvedModuleWireMap = createWires(candidatesMap, provider);
+
+        // Fire resolved events for all resolved modules;
+        // the resolved modules array will only be set if the resolve
+        // was successful.
+        if (resolvedModuleWireMap != null)
+        {
+            Iterator iter = resolvedModuleWireMap.entrySet().iterator();
+            while (iter.hasNext())
+            {
+                fireModuleResolved((IModule) ((Map.Entry) iter.next()).getKey());
+            }
+        }
+
+        return true;
+    }
+
     public String findLibrary(IModule module, String name)
     {
         // Remove leading slash, if present.
@@ -2965,4 +3055,4 @@
 
         return sb.toString();
     }
-}
+}
\ No newline at end of file