You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2012/10/17 18:39:22 UTC

svn commit: r1399332 - in /felix/trunk/resolver/src/main/java/org/apache/felix/resolver: ResolverImpl.java test/Main.java

Author: rickhall
Date: Wed Oct 17 16:39:22 2012
New Revision: 1399332

URL: http://svn.apache.org/viewvc?rev=1399332&view=rev
Log:
Apply patch (FELIX-3707) to ensure uses constraints are properly checked
for all requirements.

Modified:
    felix/trunk/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
    felix/trunk/resolver/src/main/java/org/apache/felix/resolver/test/Main.java

Modified: felix/trunk/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java?rev=1399332&r1=1399331&r2=1399332&view=diff
==============================================================================
--- felix/trunk/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java (original)
+++ felix/trunk/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java Wed Oct 17 16:39:22 2012
@@ -171,7 +171,11 @@ public class ResolverImpl implements Res
                         ? m_usesPermutations.remove(0)
                         : m_importPermutations.remove(0);
 //allCandidates.dump();
-
+                    // Reuse a resultCache map for checking package consistency
+                    // for all resources.
+                    Map<Resource, Object> resultCache =
+                        new HashMap<Resource, Object>(allResources.size());
+                    // Check the package space consistency for all 'root' resources.
                     for (Resource resource : allResources)
                     {
                         Resource target = resource;
@@ -196,7 +200,7 @@ public class ResolverImpl implements Res
                         {
                             checkPackageSpaceConsistency(
                                 rc, allCandidates.getWrappedHost(target),
-                                allCandidates, resourcePkgMap, new HashMap());
+                                allCandidates, resourcePkgMap, resultCache);
                         }
                         catch (ResolutionException ex)
                         {
@@ -973,6 +977,9 @@ public class ResolverImpl implements Res
         Set<Requirement> mutated = null;
 
         // Check for conflicting imports from fragments.
+        // TODO: Is this only needed for imports or are generic and bundle requirements also needed?
+        //       I think this is only a special case for fragment imports because they can overlap
+        //       host imports, which is not allowed in normal metadata.
         for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
         {
             if (entry.getValue().size() > 1)
@@ -1088,7 +1095,7 @@ public class ResolverImpl implements Res
 
             if (rethrow != null)
             {
-                if (mutated.size() > 0)
+                if (!mutated.isEmpty())
                 {
                     m_usesPermutations.add(permutation);
                 }
@@ -1101,19 +1108,25 @@ public class ResolverImpl implements Res
             }
         }
 
-        // Check if there are any uses conflicts with imported packages.
-        for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
+        // Check if there are any uses conflicts with imported and required packages.
+        // We combine the imported and required packages here into one map.
+        // Imported packages are added after required packages because they shadow or override
+        // the packages from required bundles.
+        Map<String, List<Blame>> allImportRequirePkgs = new HashMap<String, List<Blame>>(pkgs.m_requiredPkgs);
+        allImportRequirePkgs.putAll(pkgs.m_importedPkgs);
+
+        for (Entry<String, List<Blame>> pkgEntry: allImportRequirePkgs.entrySet())
         {
-            for (Blame importBlame : entry.getValue())
+            String pkgName = pkgEntry.getKey();
+            for (Blame requirementBlame : pkgEntry.getValue())
             {
-                String pkgName = entry.getKey();
                 if (!pkgs.m_usedPkgs.containsKey(pkgName))
                 {
                     continue;
                 }
                 for (Blame usedBlame : pkgs.m_usedPkgs.get(pkgName))
                 {
-                    if (!isCompatible(rc, importBlame.m_cap, usedBlame.m_cap, resourcePkgMap))
+                    if (!isCompatible(rc, requirementBlame.m_cap, usedBlame.m_cap, resourcePkgMap))
                     {
                         // Create a candidate permutation that eliminates any candidates
                         // that conflict with existing selected candidates.
@@ -1129,13 +1142,13 @@ public class ResolverImpl implements Res
                                 + "] because it is exposed to package '"
                                 + pkgName
                                 + "' from resources "
-                                + Util.getSymbolicName(importBlame.m_cap.getResource())
-                                + " [" + importBlame.m_cap.getResource()
+                                + Util.getSymbolicName(requirementBlame.m_cap.getResource())
+                                + " [" + requirementBlame.m_cap.getResource()
                                 + "] and "
                                 + Util.getSymbolicName(usedBlame.m_cap.getResource())
                                 + " [" + usedBlame.m_cap.getResource()
                                 + "] via two dependency chains.\n\nChain 1:\n"
-                                + toStringBlame(rc, allCandidates, importBlame)
+                                + toStringBlame(rc, allCandidates, requirementBlame)
                                 + "\n\nChain 2:\n"
                                 + toStringBlame(rc, allCandidates, usedBlame),
                                 null,
@@ -1182,7 +1195,7 @@ public class ResolverImpl implements Res
                 if (rethrow != null)
                 {
                     // Add uses permutation if we mutated any candidates.
-                    if (mutated.size() > 0)
+                    if (!mutated.isEmpty())
                     {
                         m_usesPermutations.add(permutation);
                     }
@@ -1190,7 +1203,7 @@ public class ResolverImpl implements Res
                     // Try to permutate the candidate for the original
                     // import requirement; only permutate it if we haven't
                     // done so already.
-                    Requirement req = importBlame.m_reqs.get(0);
+                    Requirement req = requirementBlame.m_reqs.get(0);
                     if (!mutated.contains(req))
                     {
                         // Since there may be lots of uses constraint violations
@@ -1217,16 +1230,18 @@ public class ResolverImpl implements Res
         // of permutations so we know if the lower level check was
         // able to create a permutation or not in the case of failure.
         int permCount = m_usesPermutations.size() + m_importPermutations.size();
-        for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
+        for (Requirement req : resource.getRequirements(null))
         {
-            for (Blame importBlame : entry.getValue())
+            List<Capability> cands = allCandidates.getCandidates(req);
+            if (cands != null && !cands.isEmpty())
             {
-                if (!resource.equals(importBlame.m_cap.getResource()))
+                Capability cap = cands.get(0);
+                if (!resource.equals(cap.getResource()))
                 {
                     try
                     {
                         checkPackageSpaceConsistency(
-                            rc, importBlame.m_cap.getResource(),
+                            rc, cap.getResource(),
                             allCandidates, resourcePkgMap, resultCache);
                     }
                     catch (ResolutionException ex)
@@ -1237,7 +1252,6 @@ public class ResolverImpl implements Res
                         // to backtrack on our current candidate selection.
                         if (permCount == (m_usesPermutations.size() + m_importPermutations.size()))
                         {
-                            Requirement req = importBlame.m_reqs.get(0);
                             permutate(allCandidates, req, m_importPermutations);
                         }
                         throw ex;
@@ -1644,7 +1658,7 @@ public class ResolverImpl implements Res
         return true;
     }
 
-	private static Map<Resource, List<Wire>> populateDynamicWireMap(
+    private static Map<Resource, List<Wire>> populateDynamicWireMap(
         ResolveContext rc, Resource resource, Requirement dynReq,
         Map<Resource, Packages> resourcePkgMap,
         Map<Resource, List<Wire>> wireMap, Candidates allCandidates)

Modified: felix/trunk/resolver/src/main/java/org/apache/felix/resolver/test/Main.java
URL: http://svn.apache.org/viewvc/felix/trunk/resolver/src/main/java/org/apache/felix/resolver/test/Main.java?rev=1399332&r1=1399331&r2=1399332&view=diff
==============================================================================
--- felix/trunk/resolver/src/main/java/org/apache/felix/resolver/test/Main.java (original)
+++ felix/trunk/resolver/src/main/java/org/apache/felix/resolver/test/Main.java Wed Oct 17 16:39:22 2012
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Map;
 import org.apache.felix.resolver.Logger;
 import org.apache.felix.resolver.ResolverImpl;
+import org.osgi.framework.namespace.BundleNamespace;
 import org.osgi.framework.namespace.PackageNamespace;
 import org.osgi.resource.Capability;
 import org.osgi.resource.Namespace;
@@ -61,6 +62,34 @@ public class Main
         rci = new ResolveContextImpl(wirings, candMap, mandatory, Collections.EMPTY_LIST);
         wireMap = resolver.resolve(rci);
         System.out.println("RESULT " + wireMap);
+
+        System.out.println("\nSCENARIO 4\n");
+        mandatory = populateScenario4(wirings, candMap);
+        rci = new ResolveContextImpl(wirings, candMap, mandatory, Collections.EMPTY_LIST);
+        try
+        {
+            wireMap = resolver.resolve(rci);
+            System.err.println("UNEXPECTED RESULT " + wireMap);
+        }
+        catch (ResolutionException e)
+        {
+            System.out.println("EXPECTED ResolutionException:");
+            e.printStackTrace(System.out);
+        }
+
+        System.out.println("\nSCENARIO 5\n");
+        mandatory = populateScenario5(wirings, candMap);
+        rci = new ResolveContextImpl(wirings, candMap, mandatory, Collections.EMPTY_LIST);
+        try
+        {
+            wireMap = resolver.resolve(rci);
+            System.err.println("UNEXPECTED RESULT " + wireMap);
+        }
+        catch (ResolutionException e)
+        {
+            System.out.println("EXPECTED ResolutionException:");
+            e.printStackTrace(System.out);
+        }
     }
 
     private static List<Resource> populateScenario1(
@@ -177,4 +206,91 @@ public class Main
         resources.add(e);
         return resources;
     }
+
+    private static List<Resource> populateScenario4(
+            Map<Resource, Wiring> wirings, Map<Requirement, List<Capability>> candMap)
+    {
+        wirings.clear();
+        candMap.clear();
+
+        ResourceImpl a = new ResourceImpl("A");
+        a.addRequirement(new BundleRequirement(a, "B"));
+        a.addRequirement(new BundleRequirement(a, "C"));
+
+        ResourceImpl b = new ResourceImpl("B");
+        b.addCapability(new BundleCapability(b, "B"));
+        b.addCapability(new PackageCapability(b, "p1"));
+
+        ResourceImpl c = new ResourceImpl("C");
+        c.addRequirement(new BundleRequirement(c, "D"));
+        c.addCapability(new BundleCapability(c, "C"));
+        PackageCapability p2 = new PackageCapability(c, "p1");
+        p2.addDirective(Namespace.CAPABILITY_USES_DIRECTIVE, "p1");
+        c.addCapability(p2);
+
+        ResourceImpl d = new ResourceImpl("D");
+        d.addCapability(new BundleCapability(d, "D"));
+        d.addCapability(new PackageCapability(d, "p1"));
+
+        candMap.put(
+            a.getRequirements(null).get(0),
+            b.getCapabilities(BundleNamespace.BUNDLE_NAMESPACE));
+        candMap.put(
+            a.getRequirements(null).get(1),
+            c.getCapabilities(BundleNamespace.BUNDLE_NAMESPACE));
+        candMap.put(
+            c.getRequirements(null).get(0),
+            d.getCapabilities(BundleNamespace.BUNDLE_NAMESPACE));
+
+        List<Resource> resources = new ArrayList<Resource>();
+        resources.add(a);
+        return resources;
+    }
+
+    private static List<Resource> populateScenario5(
+        Map<Resource, Wiring> wirings, Map<Requirement, List<Capability>> candMap)
+    {
+        wirings.clear();
+        candMap.clear();
+
+        ResourceImpl x = new ResourceImpl("X");
+        x.addRequirement(new BundleRequirement(x, "A"));
+
+        ResourceImpl a = new ResourceImpl("A");
+        a.addCapability(new BundleCapability(a, "A"));
+        a.addRequirement(new BundleRequirement(a, "B"));
+        a.addRequirement(new BundleRequirement(a, "C"));
+
+        ResourceImpl b = new ResourceImpl("B");
+        b.addCapability(new BundleCapability(b, "B"));
+        b.addCapability(new PackageCapability(b, "p1"));
+
+        ResourceImpl c = new ResourceImpl("C");
+        c.addRequirement(new BundleRequirement(c, "D"));
+        c.addCapability(new BundleCapability(c, "C"));
+        PackageCapability p2 = new PackageCapability(c, "p1");
+        p2.addDirective(Namespace.CAPABILITY_USES_DIRECTIVE, "p1");
+        c.addCapability(p2);
+
+        ResourceImpl d = new ResourceImpl("D");
+        d.addCapability(new BundleCapability(d, "D"));
+        d.addCapability(new PackageCapability(d, "p1"));
+
+        candMap.put(
+                x.getRequirements(null).get(0),
+                a.getCapabilities(BundleNamespace.BUNDLE_NAMESPACE));
+        candMap.put(
+            a.getRequirements(null).get(0),
+            b.getCapabilities(BundleNamespace.BUNDLE_NAMESPACE));
+        candMap.put(
+            a.getRequirements(null).get(1),
+            c.getCapabilities(BundleNamespace.BUNDLE_NAMESPACE));
+        candMap.put(
+            c.getRequirements(null).get(0),
+            d.getCapabilities(BundleNamespace.BUNDLE_NAMESPACE));
+
+        List<Resource> resources = new ArrayList<Resource>();
+        resources.add(x);
+        return resources;
+    }
 }
\ No newline at end of file