[FELIX-4942] Compute package spaces by stages
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1690727 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 04fecdd..bd339a6 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
@@ -24,7 +24,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -246,7 +245,7 @@
}
// Compute the list of hosts
- Map<Resource, Resource> hosts = new LinkedHashMap<Resource, Resource>();
+ Map<Resource, Resource> hosts = new OpenHashMap<Resource, Resource>();
for (Resource resource : allResources)
{
// If we are resolving a fragment, then get its
@@ -378,24 +377,15 @@
Map<Resource, Resource> hosts,
boolean dynamic)
{
+ // Calculate package spaces
Map<Resource, Packages> resourcePkgMap =
- new HashMap<Resource, Packages>(allCandidates.getNbResources());
- // Reuse a resultCache map for checking package consistency
- // for all resources.
- Map<Resource, Object> resultCache =
- new HashMap<Resource, Object>();
- // Check the package space consistency for all 'root' resources.
+ calculatePackageSpaces(session, allCandidates, hosts.values());
ResolutionError error = null;
+ // Check package consistency
+ Map<Resource, Object> resultCache =
+ new OpenHashMap<Resource, Object>(resourcePkgMap.size());
for (Entry<Resource, Resource> entry : hosts.entrySet())
{
- calculatePackageSpaces(
- session, entry.getValue(), allCandidates,
- resourcePkgMap, new HashMap<Capability, Set<Resource>>(256),
- new HashSet<Resource>(64));
-//System.out.println("+++ PACKAGE SPACES START +++");
-//dumpResourcePkgMap(resourcePkgMap);
-//System.out.println("+++ PACKAGE SPACES END +++");
-
ResolutionError rethrow = checkPackageSpaceConsistency(
session, entry.getValue(),
allCandidates, dynamic, resourcePkgMap, resultCache);
@@ -580,7 +570,6 @@
// Always clear the state.
session.getUsesPermutations().clear();
session.getImportPermutations().clear();
- // TODO these were not cleared out before; but it seems they should be
session.setMultipleCardCandidates(null);
}
}
@@ -590,38 +579,11 @@
return wireMap;
}
- private void calculatePackageSpaces(
- ResolveSession session,
- Resource resource,
- Candidates allCandidates,
- Map<Resource, Packages> resourcePkgMap,
- Map<Capability, Set<Resource>> usesCycleMap,
- Set<Resource> cycle)
+ private static List<WireCandidate> getWireCandidates(ResolveSession session, Candidates allCandidates, Resource resource)
{
- if (cycle.contains(resource))
- {
- return;
- }
- cycle.add(resource);
-
- // Make sure package space hasn't already been calculated.
- Packages resourcePkgs = resourcePkgMap.get(resource);
- if (resourcePkgs != null)
- {
- if (resourcePkgs.m_isCalculated)
- {
- return;
- }
- else
- {
- resourcePkgs.m_isCalculated = true;
- }
- }
-
// Create a list for requirement and proposed candidate
// capability or actual capability if resource is resolved or not.
- List<WireCandidate> wireCandidates = new ArrayList<WireCandidate>();
- boolean isDynamicImporting = false;
+ List<WireCandidate> wireCandidates = new ArrayList<WireCandidate>(256);
Wiring wiring = session.getContext().getWirings().get(resource);
if (wiring != null)
{
@@ -671,7 +633,6 @@
continue;
}
wireCandidates.add(new WireCandidate(req, cap));
- isDynamicImporting = true;
// Can only dynamically import one at a time, so break
// out of the loop after the first.
break;
@@ -710,28 +671,31 @@
}
}
}
+ return wireCandidates;
+ }
- // First, add all exported packages to the target resource's package space.
- calculateExportedPackages(session.getContext(), resource, allCandidates, resourcePkgMap);
- resourcePkgs = resourcePkgMap.get(resource);
+ private static Packages getPackages(
+ ResolveSession session,
+ Candidates allCandidates,
+ Map<Resource, List<WireCandidate>> allWireCandidates,
+ Map<Resource, Packages> allPackages,
+ Resource resource,
+ Packages resourcePkgs)
+ {
+ // First, all all exported packages
+ // This has been done previously
// Second, add all imported packages to the target resource's package space.
- for (int i = 0; i < wireCandidates.size(); i++)
+ for (WireCandidate wire : allWireCandidates.get(resource))
{
- WireCandidate wireCandidate = wireCandidates.get(i);
- Requirement req = wireCandidate.requirement;
- Capability cap = wireCandidate.capability;
- calculateExportedPackages(
- session.getContext(), cap.getResource(), allCandidates, resourcePkgMap);
-
// If this resource is dynamically importing, then the last requirement
// is the dynamic import being resolved, since it is added last to the
// parallel lists above. For the dynamically imported package, make
// sure that the resource doesn't already have a provider for that
// package, which would be illegal and shouldn't be allowed.
- if (isDynamicImporting && ((i + 1) == wireCandidates.size()))
+ if (Util.isDynamic(wire.requirement))
{
- String pkgName = (String) cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
+ String pkgName = (String) wire.capability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
if (resourcePkgs.m_exportedPkgs.containsKey(pkgName)
|| resourcePkgs.m_importedPkgs.containsKey(pkgName)
|| resourcePkgs.m_requiredPkgs.containsKey(pkgName))
@@ -746,19 +710,27 @@
}
mergeCandidatePackages(
- session.getContext(), resource, req, cap, resourcePkgMap, allCandidates,
- new HashMap<Resource, Set<Capability>>(), new HashMap<Resource, Set<Resource>>());
+ session,
+ allPackages,
+ allCandidates,
+ resourcePkgs,
+ wire.requirement,
+ wire.capability,
+ new HashSet<Capability>(),
+ new HashSet<Resource>());
}
- // Third, have all candidates to calculate their package spaces.
- for (WireCandidate w : wireCandidates)
- {
- Capability cap = w.capability;
- calculatePackageSpaces(
- session, cap.getResource(), allCandidates, resourcePkgMap,
- usesCycleMap, cycle);
- }
+ return resourcePkgs;
+ }
+ private static void computeUses(
+ ResolveSession session,
+ Map<Resource, List<WireCandidate>> allWireCandidates,
+ Map<Resource, Packages> resourcePkgMap,
+ Resource resource)
+ {
+ List<WireCandidate> wireCandidates = allWireCandidates.get(resource);
+ Packages resourcePkgs = resourcePkgMap.get(resource);
// Fourth, if the target resource is unresolved or is dynamically importing,
// then add all the uses constraints implied by its imported and required
// packages to its package space.
@@ -768,6 +740,13 @@
// only exception is if a resolved resource is dynamically importing, then
// we need to calculate its uses constraints again to make sure the new
// import is consistent with the existing package space.
+ Wiring wiring = session.getContext().getWirings().get(resource);
+ Set<Capability> usesCycleMap = new HashSet<Capability>();
+
+ int size = wireCandidates.size();
+ boolean isDynamicImporting = size > 0
+ && Util.isDynamic(wireCandidates.get(size - 1).requirement);
+
if ((wiring == null) || isDynamicImporting)
{
// Merge uses constraints from required capabilities.
@@ -835,19 +814,17 @@
}
}
- private void mergeCandidatePackages(
- ResolveContext rc, Resource current, Requirement currentReq,
- Capability candCap, Map<Resource, Packages> resourcePkgMap,
- Candidates allCandidates, Map<Resource, Set<Capability>> cycles,
- HashMap<Resource, Set<Resource>> visitedRequiredBundlesMap)
+ private static void mergeCandidatePackages(
+ ResolveSession session,
+ Map<Resource, Packages> resourcePkgMap,
+ Candidates allCandidates,
+ Packages packages,
+ Requirement currentReq,
+ Capability candCap,
+ Set<Capability> capabilityCycles,
+ Set<Resource> visitedRequiredBundles)
{
- Set<Capability> cycleCaps = cycles.get(current);
- if (cycleCaps == null)
- {
- cycleCaps = new HashSet<Capability>();
- cycles.put(current, cycleCaps);
- }
- if (!cycleCaps.add(candCap))
+ if (!capabilityCycles.add(candCap))
{
return;
}
@@ -855,33 +832,22 @@
if (candCap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
mergeCandidatePackage(
- resourcePkgMap.get(current).m_importedPkgs,
+ packages.m_importedPkgs,
currentReq, candCap);
}
else if (candCap.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
{
-// TODO: FELIX3 - THIS NEXT LINE IS A HACK. IMPROVE HOW/WHEN WE CALCULATE EXPORTS.
- calculateExportedPackages(
- rc, candCap.getResource(), allCandidates, resourcePkgMap);
// Get the candidate's package space to determine which packages
// will be visible to the current resource.
- Packages candPkgs = resourcePkgMap.get(candCap.getResource());
-
- Set<Resource> visitedRequiredBundles = visitedRequiredBundlesMap.get(current);
- if (visitedRequiredBundles == null)
- {
- visitedRequiredBundles = new HashSet<Resource>();
- visitedRequiredBundlesMap.put(current, visitedRequiredBundles);
- }
if (visitedRequiredBundles.add(candCap.getResource()))
{
// We have to merge all exported packages from the candidate,
// since the current resource requires it.
- for (Blame blame : candPkgs.m_exportedPkgs.values())
+ for (Blame blame : resourcePkgMap.get(candCap.getResource()).m_exportedPkgs.values())
{
mergeCandidatePackage(
- resourcePkgMap.get(current).m_requiredPkgs,
+ packages.m_requiredPkgs,
currentReq,
blame.m_cap);
}
@@ -889,7 +855,7 @@
// If the candidate requires any other bundles with reexport visibility,
// then we also need to merge their packages too.
- Wiring candWiring = rc.getWirings().get(candCap.getResource());
+ Wiring candWiring = session.getContext().getWirings().get(candCap.getResource());
if (candWiring != null)
{
for (Wire w : candWiring.getRequiredResourceWires(null))
@@ -900,13 +866,14 @@
if (Util.isReexport(w.getRequirement()))
{
mergeCandidatePackages(
- rc,
- current,
- currentReq,
- w.getCapability(),
+ session,
resourcePkgMap,
allCandidates,
- cycles, visitedRequiredBundlesMap);
+ packages,
+ currentReq,
+ w.getCapability(),
+ capabilityCycles,
+ visitedRequiredBundles);
}
}
}
@@ -920,24 +887,23 @@
if (Util.isReexport(req))
{
Capability cap = allCandidates.getFirstCandidate(req);
- if (cap != null) {
+ if (cap != null)
+ {
mergeCandidatePackages(
- rc,
- current,
- currentReq,
- cap,
+ session,
resourcePkgMap,
allCandidates,
- cycles,
- visitedRequiredBundlesMap);
+ packages,
+ currentReq,
+ cap,
+ capabilityCycles,
+ visitedRequiredBundles);
}
}
}
}
}
}
-
- cycles.remove(current);
}
private static void mergeCandidatePackage(
@@ -964,7 +930,7 @@
ResolveSession session, Resource current, Packages currentPkgs,
Capability mergeCap, List<Requirement> blameReqs, Capability matchingCap,
Map<Resource, Packages> resourcePkgMap,
- Map<Capability, Set<Resource>> cycleMap)
+ Set<Capability> cycleMap)
{
// If there are no uses, then just return.
// If the candidate resource is the same as the current resource,
@@ -976,13 +942,7 @@
}
// Check for cycles.
- Set<Resource> set = cycleMap.get(mergeCap);
- if (set == null)
- {
- set = new HashSet<Resource>();
- cycleMap.put(mergeCap, set);
- }
- if (!set.add(current))
+ if (!cycleMap.add(mergeCap))
{
return;
}
@@ -1068,6 +1028,68 @@
}
}
+ private static void getWireCandidatesAndRecurse(
+ ResolveSession session,
+ Candidates allCandidates,
+ Map<Resource, List<WireCandidate>> allWireCandidates,
+ Resource resource
+ )
+ {
+ List<WireCandidate> wireCandidates = getWireCandidates(session, allCandidates, resource);
+ allWireCandidates.put(resource, wireCandidates);
+ for (WireCandidate w : wireCandidates)
+ {
+ Resource r = w.capability.getResource();
+ if (!allWireCandidates.containsKey(r))
+ {
+ getWireCandidatesAndRecurse(session, allCandidates, allWireCandidates, r);
+ }
+ }
+ }
+
+ private static Map<Resource, Packages> calculatePackageSpaces(
+ final ResolveSession session,
+ final Candidates allCandidates,
+ Collection<Resource> hosts)
+ {
+ // Parallel compute wire candidates
+ final Map<Resource, List<WireCandidate>> allWireCandidates = new OpenHashMap<Resource, List<WireCandidate>>();
+ for (Resource resource : hosts)
+ {
+ getWireCandidatesAndRecurse(session, allCandidates, allWireCandidates, resource);
+ }
+
+ // Parallel get all exported packages
+ final Map<Resource, Packages> allPackages = new OpenHashMap<Resource, Packages>(allCandidates.getNbResources());
+ for (final Resource resource : allWireCandidates.keySet())
+ {
+ final Packages packages = new Packages(resource);
+ allPackages.put(resource, packages);
+ calculateExportedPackages(session, allCandidates, resource, packages.m_exportedPkgs);
+ }
+
+ // Parallel compute package lists
+ for (final Resource resource : allWireCandidates.keySet())
+ {
+ getPackages(session, allCandidates, allWireCandidates, allPackages, resource, allPackages.get(resource));
+ }
+
+ // Sequential compute package sources
+ // TODO: make that parallel
+ for (Resource resource : allWireCandidates.keySet())
+ {
+ getPackageSourcesInternal(session, allPackages, resource);
+ }
+
+ // Parallel compute uses
+ for (final Resource resource : allWireCandidates.keySet())
+ {
+ computeUses(session, allWireCandidates, allPackages, resource);
+ }
+
+ return allPackages;
+ }
+
private static List<String> parseUses(String s) {
int nb = 1;
int l = s.length();
@@ -1457,25 +1479,17 @@
return (candidates != null) && !candidates.isEmpty();
}
- private static void calculateExportedPackages(
- ResolveContext rc,
- Resource resource,
- Candidates allCandidates,
- Map<Resource, Packages> resourcePkgMap)
+ private static OpenHashMap<String, Blame> calculateExportedPackages(
+ ResolveSession session,
+ Candidates allCandidates,
+ Resource resource,
+ OpenHashMap<String, Blame> exports)
{
- Packages packages = resourcePkgMap.get(resource);
- if (packages != null)
- {
- return;
- }
- packages = new Packages(resource);
-
// Get all exported packages.
- Wiring wiring = rc.getWirings().get(resource);
+ Wiring wiring = session.getContext().getWirings().get(resource);
List<Capability> caps = (wiring != null)
? wiring.getResourceCapabilities(null)
: resource.getCapabilities(null);
- Map<String, Blame> exports = packages.m_exportedPkgs;
for (Capability cap : caps)
{
if (cap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
@@ -1512,8 +1526,7 @@
}
}
}
-
- resourcePkgMap.put(resource, packages);
+ return exports;
}
private static boolean isCompatible(
@@ -1901,7 +1914,6 @@
public final OpenHashMap<String, List<Blame>> m_requiredPkgs;
public final OpenHashMap<String, ArrayMap<Capability, UsedBlames>> m_usedPkgs;
public final OpenHashMap<Capability, Set<Capability>> m_sources;
- public boolean m_isCalculated = false;
public Packages(Resource resource)
{