Apply patch (FELIX-3707) to ensure uses constraints are properly checked
for all requirements.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1399332 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
index 35ec2b8..cdcba43 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
@@ -171,7 +171,11 @@
? 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 @@
{
checkPackageSpaceConsistency(
rc, allCandidates.getWrappedHost(target),
- allCandidates, resourcePkgMap, new HashMap());
+ allCandidates, resourcePkgMap, resultCache);
}
catch (ResolutionException ex)
{
@@ -973,6 +977,9 @@
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 @@
if (rethrow != null)
{
- if (mutated.size() > 0)
+ if (!mutated.isEmpty())
{
m_usesPermutations.add(permutation);
}
@@ -1101,19 +1108,25 @@
}
}
- // 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 @@
+ "] 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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
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)
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/Main.java b/resolver/src/main/java/org/apache/felix/resolver/test/Main.java
index 6392e73..f273052 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/test/Main.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/Main.java
@@ -25,6 +25,7 @@
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 @@
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 @@
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