[FELIX-4987] Dynamic package resolution with unresolvable or fragment package exports can lead to invalid wirings
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1696779 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/resolver/src/main/java/org/apache/felix/resolver/Candidates.java b/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
index a764137..28a1b56 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
@@ -123,6 +123,29 @@
return m_populateResultCache.size();
}
+ public Map<Resource, Resource> getHosts()
+ {
+ Map<Resource, Resource> hosts = new HashMap<Resource, Resource>();
+ for (Resource res : m_mandatoryResources)
+ {
+ if (res instanceof WrappedResource)
+ {
+ res = ((WrappedResource) res).getDeclaredResource();
+ }
+ hosts.put(res, getWrappedHost(res));
+ }
+ for (Capability cap : m_dependentMap.keySet())
+ {
+ Resource res = cap.getResource();
+ if (res instanceof WrappedResource)
+ {
+ res = ((WrappedResource) res).getDeclaredResource();
+ }
+ hosts.put(res, getWrappedHost(res));
+ }
+ return hosts;
+ }
+
/**
* Returns the delta which is the differences in the candidates from the
* original Candidates permutation.
@@ -460,7 +483,6 @@
// cannot resolve.
// TODO: verify the two following statements
LinkedList<Resource> toPopulate = new LinkedList<Resource>();
- toPopulate.add(resource);
ResolutionError rethrow = processCandidates(rc, toPopulate, req, candidates);
// Add the dynamic imports candidates.
@@ -468,6 +490,18 @@
// fragment candidates are properly hosted before adding the candidates list which makes a copy
addCandidates(req, candidates);
+ populate(rc, toPopulate);
+
+ CopyOnWriteList<Capability> caps = m_candidateMap.get(req);
+ if (caps != null)
+ {
+ candidates.retainAll(caps);
+ }
+ else
+ {
+ candidates.clear();
+ }
+
if (candidates.isEmpty())
{
if (rethrow == null)
@@ -1092,9 +1126,12 @@
if (!Util.isOptional(r))
{
PopulateResult result = m_populateResultCache.get(r.getResource());
- result.success = false;
- result.error =
- new MissingRequirementError(r, m_populateResultCache.get(c.getResource()).error);
+ if (result != null)
+ {
+ result.success = false;
+ result.error =
+ new MissingRequirementError(r, m_populateResultCache.get(c.getResource()).error);
+ }
unresolvedResources.add(r.getResource());
}
}
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 a4a2d48..3b2b6b5 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
@@ -572,7 +572,7 @@
new DumbExecutor(),
session, usesPermutations, importPermutations, allCandidates,
new OpenHashMap<Resource, ResolutionError>(resourcePkgMap.size()),
- Collections.singletonMap(host, allCandidates.getWrappedHost(host)),
+ allCandidates.getHosts(),
true);
}
while ((rethrow != null)
diff --git a/resolver/src/test/java/org/apache/felix/resolver/test/ResolverTest.java b/resolver/src/test/java/org/apache/felix/resolver/test/ResolverTest.java
index a48dfed..55580fd 100644
--- a/resolver/src/test/java/org/apache/felix/resolver/test/ResolverTest.java
+++ b/resolver/src/test/java/org/apache/felix/resolver/test/ResolverTest.java
@@ -426,7 +426,155 @@
// should be wired to A and C1
assertEquals(resA, wiresB.get(0).getProvider());
assertEquals(resC1, wiresB.get(1).getProvider());
+ }
+ /**
+ * Test dynamic resolution with a resolved fragment
+ */
+ @Test
+ public void testScenario10() throws Exception
+ {
+ ResolverImpl resolver = new ResolverImpl(new Logger(Logger.LOG_DEBUG), 1);
+
+ Map<Resource, Wiring> wirings = new HashMap<Resource, Wiring>();
+ Map<Requirement, List<Capability>> candMap = new HashMap<Requirement, List<Capability>>();
+
+ ResourceImpl a1 = new ResourceImpl("A");
+ Capability a1_hostCap = addCap(a1, HostNamespace.HOST_NAMESPACE, "A");
+
+ ResourceImpl f1 = new ResourceImpl("F1", IdentityNamespace.TYPE_FRAGMENT);
+ Requirement f1_hostReq = addReq(f1, HostNamespace.HOST_NAMESPACE, "A");
+ Capability f1_pkgCap = addCap(f1, PackageNamespace.PACKAGE_NAMESPACE, "org.foo.a");
+
+ ResourceImpl b1 = new ResourceImpl("B");
+ Requirement b_pkgReq1 = addReq(b1, PackageNamespace.PACKAGE_NAMESPACE, "org.foo.a");
+
+ candMap.put(b_pkgReq1, Collections.singletonList(f1_pkgCap));
+
+ Map<Resource, List<Wire>> wires = new HashMap<Resource, List<Wire>>();
+ wires.put(a1, new ArrayList<Wire>());
+ wires.put(b1, new ArrayList<Wire>());
+ wires.put(f1, new ArrayList<Wire>());
+ wires.get(f1).add(new SimpleWire(f1_hostReq, a1_hostCap));
+
+ Map<Resource, List<Wire>> invertedWires = new HashMap<Resource, List<Wire>>();
+ invertedWires.put(a1, new ArrayList<Wire>());
+ invertedWires.put(b1, new ArrayList<Wire>());
+ invertedWires.put(f1, new ArrayList<Wire>());
+ invertedWires.get(a1).add(new SimpleWire(f1_hostReq, a1_hostCap));
+
+ wirings.put(a1, new SimpleWiring(a1, Arrays.asList(a1_hostCap, f1_pkgCap), wires, invertedWires));
+ wirings.put(b1, new SimpleWiring(b1, Collections.<Capability>emptyList(), wires, invertedWires));
+ wirings.put(f1, new SimpleWiring(f1, Collections.<Capability>emptyList(), wires, invertedWires));
+
+ ResolveContextImpl rci = new ResolveContextImpl(wirings, candMap, Collections.<Resource>emptyList(), Collections.<Resource> emptyList());
+
+ List<Capability> caps = new ArrayList<Capability>();
+ caps.add(f1_pkgCap);
+ Map<Resource, List<Wire>> wireMap = resolver.resolve(rci, b1, b_pkgReq1, caps);
+
+ assertEquals(1, wireMap.size());
+ List<Wire> wiresB = wireMap.get(b1);
+ assertNotNull(wiresB);
+ assertEquals(1, wiresB.size());
+ // should be wired to A through the fragment capability
+ assertEquals(a1, wiresB.get(0).getProvider());
+ assertEquals(f1_pkgCap, wiresB.get(0).getCapability());
+ }
+
+ /**
+ * Test dynamic resolution with an unresolved fragment
+ */
+ @Test
+ public void testScenario11() throws Exception
+ {
+ ResolverImpl resolver = new ResolverImpl(new Logger(Logger.LOG_DEBUG), 1);
+
+ Map<Resource, Wiring> wirings = new HashMap<Resource, Wiring>();
+ Map<Requirement, List<Capability>> candMap = new HashMap<Requirement, List<Capability>>();
+
+ ResourceImpl a1 = new ResourceImpl("A");
+ Capability a1_hostCap = addCap(a1, HostNamespace.HOST_NAMESPACE, "A");
+
+ ResourceImpl f1 = new ResourceImpl("F1", IdentityNamespace.TYPE_FRAGMENT);
+ Requirement f1_hostReq = addReq(f1, HostNamespace.HOST_NAMESPACE, "A");
+ Capability f1_pkgCap = addCap(f1, PackageNamespace.PACKAGE_NAMESPACE, "org.foo.a");
+
+ ResourceImpl b1 = new ResourceImpl("B");
+ Requirement b_pkgReq1 = addReq(b1, PackageNamespace.PACKAGE_NAMESPACE, "org.foo.a");
+
+ candMap.put(b_pkgReq1, Collections.singletonList(f1_pkgCap));
+ candMap.put(f1_hostReq, Collections.singletonList(a1_hostCap));
+
+ Map<Resource, List<Wire>> wires = new HashMap<Resource, List<Wire>>();
+ wires.put(a1, new ArrayList<Wire>());
+ wires.put(b1, new ArrayList<Wire>());
+
+ Map<Resource, List<Wire>> invertedWires = new HashMap<Resource, List<Wire>>();
+ invertedWires.put(a1, new ArrayList<Wire>());
+ invertedWires.put(b1, new ArrayList<Wire>());
+
+ wirings.put(a1, new SimpleWiring(a1, Collections.<Capability>emptyList(), wires, invertedWires));
+ wirings.put(b1, new SimpleWiring(b1, Collections.<Capability>emptyList(), wires, invertedWires));
+
+ ResolveContextImpl rci = new ResolveContextImpl(wirings, candMap, Collections.<Resource>emptyList(), Collections.<Resource> emptyList());
+
+ List<Capability> caps = new ArrayList<Capability>();
+ caps.add(f1_pkgCap);
+ Map<Resource, List<Wire>> wireMap = resolver.resolve(rci, b1, b_pkgReq1, caps);
+
+ assertEquals(1, wireMap.size());
+ List<Wire> wiresB = wireMap.get(b1);
+ assertNotNull(wiresB);
+ assertEquals(1, wiresB.size());
+ // should be wired to A through the fragment capability
+ assertEquals(a1, wiresB.get(0).getProvider());
+ assertEquals(f1_pkgCap, wiresB.get(0).getCapability());
+ }
+
+ /**
+ * Test dynamic resolution with an unresolvable host
+ */
+ @Test(expected = ResolutionException.class)
+ public void testScenario12() throws Exception
+ {
+ ResolverImpl resolver = new ResolverImpl(new Logger(Logger.LOG_DEBUG), 1);
+
+ Map<Resource, Wiring> wirings = new HashMap<Resource, Wiring>();
+ Map<Requirement, List<Capability>> candMap = new HashMap<Requirement, List<Capability>>();
+
+ ResourceImpl a1 = new ResourceImpl("A");
+ Capability a1_hostCap = addCap(a1, HostNamespace.HOST_NAMESPACE, "A");
+
+ ResourceImpl b1 = new ResourceImpl("B");
+ Requirement b_pkgReq1 = addReq(b1, PackageNamespace.PACKAGE_NAMESPACE, "org.foo.a");
+
+ ResourceImpl c1 = new ResourceImpl("C");
+ Capability c_hostCap = addCap(c1, HostNamespace.HOST_NAMESPACE, "A");
+ Capability c_pkgCap = addCap(c1, PackageNamespace.PACKAGE_NAMESPACE, "org.foo.a");
+ Requirement c_pkgReq1 = addReq(c1, PackageNamespace.PACKAGE_NAMESPACE, "org.foo.b");
+
+ candMap.put(b_pkgReq1, Collections.singletonList(c_pkgCap));
+ candMap.put(c_pkgReq1, Collections.<Capability>emptyList());
+
+ Map<Resource, List<Wire>> wires = new HashMap<Resource, List<Wire>>();
+ wires.put(a1, new ArrayList<Wire>());
+ wires.put(b1, new ArrayList<Wire>());
+
+ Map<Resource, List<Wire>> invertedWires = new HashMap<Resource, List<Wire>>();
+ invertedWires.put(a1, new ArrayList<Wire>());
+ invertedWires.put(b1, new ArrayList<Wire>());
+
+ wirings.put(a1, new SimpleWiring(a1, Collections.<Capability>emptyList(), wires, invertedWires));
+ wirings.put(b1, new SimpleWiring(b1, Collections.<Capability>emptyList(), wires, invertedWires));
+
+ ResolveContextImpl rci = new ResolveContextImpl(wirings, candMap, Collections.<Resource>emptyList(), Collections.<Resource> emptyList());
+
+ List<Capability> caps = new ArrayList<Capability>();
+ caps.add(c_pkgCap);
+ Map<Resource, List<Wire>> wireMap = resolver.resolve(rci, b1, b_pkgReq1, caps);
+
+ assertEquals(0, wireMap.size());
}
private static String getResourceName(Resource r)