Apply patch (FELIX-3715) to make resolver thread safe.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1420414 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 7dc6d4f..4fa2d0b 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
@@ -47,17 +47,62 @@
public class ResolverImpl implements Resolver
{
private final Logger m_logger;
- // Holds candidate permutations based on permutating "uses" chains.
- // These permutations are given higher priority.
- private final List<Candidates> m_usesPermutations = new ArrayList<Candidates>();
- // Holds candidate permutations based on permutating requirement candidates.
- // These permutations represent backtracking on previous decisions.
- private final List<Candidates> m_importPermutations = new ArrayList<Candidates>();
- // Holds candidate permutations based on removing candidates that satisfy
- // multiple cardinality requirements.
- // This permutation represents a permutation that is consistent because we have
- // removed the offending capabilities
- private Candidates m_multipleCardCandidates = null;
+
+ // Note this class is not thread safe.
+ // Only use in the context of a single thread.
+ class ResolveSession
+ {
+ // Holds the resolve context for this session
+ private final ResolveContext m_resolveContext;
+ // Holds candidate permutations based on permutating "uses" chains.
+ // These permutations are given higher priority.
+ private final List<Candidates> m_usesPermutations = new ArrayList<Candidates>();
+ // Holds candidate permutations based on permutating requirement candidates.
+ // These permutations represent backtracking on previous decisions.
+ private final List<Candidates> m_importPermutations = new ArrayList<Candidates>();
+ // Holds candidate permutations based on removing candidates that satisfy
+ // multiple cardinality requirements.
+ // This permutation represents a permutation that is consistent because we have
+ // removed the offending capabilities
+ private Candidates m_multipleCardCandidates = null;
+
+ private final Map<Capability, List<Capability>> m_packageSourcesCache = new HashMap();
+
+ ResolveSession(ResolveContext resolveContext)
+ {
+ m_resolveContext = resolveContext;
+ }
+
+ List<Candidates> getUsesPermutations()
+ {
+ return m_usesPermutations;
+ }
+
+ List<Candidates> getImportPermutations()
+ {
+ return m_importPermutations;
+ }
+
+ Candidates getMultipleCardCandidates()
+ {
+ return m_multipleCardCandidates;
+ }
+
+ void setMultipleCardCandidates(Candidates multipleCardCandidates)
+ {
+ m_multipleCardCandidates = multipleCardCandidates;
+ }
+
+ Map<Capability, List<Capability>> getPackageSourcesCache()
+ {
+ return m_packageSourcesCache;
+ }
+
+ ResolveContext getContext()
+ {
+ return m_resolveContext;
+ }
+ }
public ResolverImpl(Logger logger)
{
@@ -66,6 +111,7 @@
public Map<Resource, List<Wire>> resolve(ResolveContext rc) throws ResolutionException
{
+ ResolveSession session = new ResolveSession(rc);
Map<Resource, List<Wire>> wireMap =
new HashMap<Resource, List<Wire>>();
Map<Resource, Packages> resourcePkgMap =
@@ -144,8 +190,11 @@
}
}
+ List<Candidates> usesPermutations = session.getUsesPermutations();
+ List<Candidates> importPermutations = session.getImportPermutations();
+
// Record the initial candidate permutation.
- m_usesPermutations.add(allCandidates);
+ usesPermutations.add(allCandidates);
ResolutionException rethrow = null;
@@ -169,15 +218,15 @@
rethrow = null;
resourcePkgMap.clear();
- m_packageSourcesCache.clear();
+ session.getPackageSourcesCache().clear();
// Null out each time a new permutation is attempted.
// We only use this to store a valid permutation which is a
// delta of the current permutation.
- m_multipleCardCandidates = null;
+ session.setMultipleCardCandidates(null);
- allCandidates = (m_usesPermutations.size() > 0)
- ? m_usesPermutations.remove(0)
- : m_importPermutations.remove(0);
+ allCandidates = (usesPermutations.size() > 0)
+ ? usesPermutations.remove(0)
+ : importPermutations.remove(0);
//allCandidates.dump();
// Reuse a resultCache map for checking package consistency
// for all resources.
@@ -198,7 +247,7 @@
}
calculatePackageSpaces(
- rc, allCandidates.getWrappedHost(target), allCandidates,
+ session, allCandidates.getWrappedHost(target), allCandidates,
resourcePkgMap, new HashMap(), new HashSet());
//System.out.println("+++ PACKAGE SPACES START +++");
//dumpResourcePkgMap(resourcePkgMap);
@@ -207,7 +256,7 @@
try
{
checkPackageSpaceConsistency(
- rc, allCandidates.getWrappedHost(target),
+ session, allCandidates.getWrappedHost(target),
allCandidates, resourcePkgMap, resultCache);
}
catch (ResolutionException ex)
@@ -217,7 +266,7 @@
}
}
while ((rethrow != null)
- && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
+ && ((usesPermutations.size() > 0) || (importPermutations.size() > 0)));
// If there is a resolve exception, then determine if an
// optionally resolved resource is to blame (typically a fragment).
@@ -257,12 +306,12 @@
// resolve, so populate the wire map.
else
{
- if (m_multipleCardCandidates != null)
+ if (session.getMultipleCardCandidates() != null)
{
// Candidates for multiple cardinality requirements were
// removed in order to provide a consistent class space.
// Use the consistent permutation
- allCandidates = m_multipleCardCandidates;
+ allCandidates = session.getMultipleCardCandidates();
}
for (Resource resource : allResources)
{
@@ -290,9 +339,11 @@
finally
{
// Always clear the state.
- m_usesPermutations.clear();
- m_importPermutations.clear();
- m_multipleCardCandidates = null;
+ session.getUsesPermutations().clear();
+ session.getImportPermutations().clear();
+ session.setMultipleCardCandidates(null);
+ // TODO this was not cleared out before; but it seems it should be
+ session.getPackageSourcesCache().clear();
}
}
while (retry);
@@ -330,6 +381,7 @@
List<Capability> matches, Collection<Resource> ondemandFragments)
throws ResolutionException
{
+ ResolveSession session = new ResolveSession(rc);
Map<Resource, List<Wire>> wireMap = new HashMap<Resource, List<Wire>>();
// We can only create a dynamic import if the following
@@ -380,8 +432,11 @@
// Merge any fragments into hosts.
allCandidates.prepare(rc);
+ List<Candidates> usesPermutations = session.getUsesPermutations();
+ List<Candidates> importPermutations = session.getImportPermutations();
+
// Record the initial candidate permutation.
- m_usesPermutations.add(allCandidates);
+ usesPermutations.add(allCandidates);
ResolutionException rethrow = null;
@@ -390,11 +445,11 @@
rethrow = null;
resourcePkgMap.clear();
- m_packageSourcesCache.clear();
+ session.getPackageSourcesCache().clear();
- allCandidates = (m_usesPermutations.size() > 0)
- ? m_usesPermutations.remove(0)
- : m_importPermutations.remove(0);
+ allCandidates = (usesPermutations.size() > 0)
+ ? usesPermutations.remove(0)
+ : importPermutations.remove(0);
//allCandidates.dump();
// For a dynamic import, the instigating resource
@@ -402,7 +457,7 @@
// execute code, so we don't need to check for
// this case like we do for a normal resolve.
- calculatePackageSpaces(rc,
+ calculatePackageSpaces(session,
allCandidates.getWrappedHost(host), allCandidates,
resourcePkgMap, new HashMap(), new HashSet());
//System.out.println("+++ PACKAGE SPACES START +++");
@@ -411,7 +466,7 @@
try
{
- checkDynamicPackageSpaceConsistency(rc,
+ checkDynamicPackageSpaceConsistency(session,
allCandidates.getWrappedHost(host),
allCandidates, resourcePkgMap, new HashMap());
}
@@ -421,7 +476,7 @@
}
}
while ((rethrow != null)
- && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
+ && ((usesPermutations.size() > 0) || (importPermutations.size() > 0)));
// If there is a resolve exception, then determine if an
// optionally resolved resource is to blame (typically a fragment).
@@ -457,6 +512,14 @@
// resolve, so populate the wire map.
else
{
+ if (session.getMultipleCardCandidates() != null)
+ {
+ // TODO this was not done before; but I think it should be;
+ // Candidates for multiple cardinality requirements were
+ // removed in order to provide a consistent class space.
+ // Use the consistent permutation
+ allCandidates = session.getMultipleCardCandidates();
+ }
wireMap = populateDynamicWireMap(rc,
host, dynamicReq, resourcePkgMap, wireMap, allCandidates);
}
@@ -464,8 +527,11 @@
finally
{
// Always clear the state.
- m_usesPermutations.clear();
- m_importPermutations.clear();
+ session.getUsesPermutations().clear();
+ session.getImportPermutations().clear();
+ // TODO these were not cleared out before; but it seems they should be
+ session.setMultipleCardCandidates(null);
+ session.getPackageSourcesCache().clear();
}
}
while (retry);
@@ -475,7 +541,7 @@
}
private void calculatePackageSpaces(
- ResolveContext rc,
+ ResolveSession session,
Resource resource,
Candidates allCandidates,
Map<Resource, Packages> resourcePkgMap,
@@ -509,7 +575,7 @@
List<Requirement> reqs = new ArrayList();
List<Capability> caps = new ArrayList();
boolean isDynamicImporting = false;
- Wiring wiring = rc.getWirings().get(resource);
+ Wiring wiring = session.getContext().getWirings().get(resource);
if (wiring != null)
{
// Use wires to get actual requirements and satisfying capabilities.
@@ -606,7 +672,7 @@
}
// First, add all exported packages to the target resource's package space.
- calculateExportedPackages(rc, resource, allCandidates, resourcePkgMap);
+ calculateExportedPackages(session.getContext(), resource, allCandidates, resourcePkgMap);
resourcePkgs = resourcePkgMap.get(resource);
// Second, add all imported packages to the target resource's package space.
@@ -614,7 +680,8 @@
{
Requirement req = reqs.get(i);
Capability cap = caps.get(i);
- calculateExportedPackages(rc, cap.getResource(), allCandidates, resourcePkgMap);
+ 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
@@ -638,7 +705,7 @@
}
mergeCandidatePackages(
- rc, resource, req, cap, resourcePkgMap, allCandidates,
+ session.getContext(), resource, req, cap, resourcePkgMap, allCandidates,
new HashMap<Resource, List<Capability>>());
}
@@ -646,7 +713,7 @@
for (int i = 0; i < caps.size(); i++)
{
calculatePackageSpaces(
- rc, caps.get(i).getResource(), allCandidates, resourcePkgMap,
+ session, caps.get(i).getResource(), allCandidates, resourcePkgMap,
usesCycleMap, cycle);
}
@@ -675,7 +742,7 @@
blameReqs.add(req);
mergeUses(
- rc,
+ session,
resource,
resourcePkgs,
cap,
@@ -698,7 +765,7 @@
blameReqs.add(blame.m_reqs.get(0));
mergeUses(
- rc,
+ session,
resource,
resourcePkgs,
blame.m_cap,
@@ -719,7 +786,7 @@
blameReqs.add(blame.m_reqs.get(0));
mergeUses(
- rc,
+ session,
resource,
resourcePkgs,
blame.m_cap,
@@ -868,7 +935,7 @@
}
private void mergeUses(
- ResolveContext rc, Resource current, Packages currentPkgs,
+ ResolveSession session, Resource current, Packages currentPkgs,
Capability mergeCap, List<Requirement> blameReqs, Capability matchingCap,
Map<Resource, Packages> resourcePkgMap,
Candidates allCandidates,
@@ -893,7 +960,7 @@
list.add(current);
cycleMap.put(mergeCap, list);
- for (Capability candSourceCap : getPackageSources(rc, mergeCap, resourcePkgMap))
+ for (Capability candSourceCap : getPackageSources(session, mergeCap, resourcePkgMap))
{
List<String> uses;
// TODO: RFC-112 - Need impl-specific type
@@ -961,13 +1028,13 @@
// that is the requirement wired to the blamed capability
blameReqs2.add(blame.m_reqs.get(blame.m_reqs.size() - 1));
addUsedBlame(usedPkgBlames, blame.m_cap, blameReqs2, matchingCap);
- mergeUses(rc, current, currentPkgs, blame.m_cap, blameReqs2, matchingCap,
+ mergeUses(session, current, currentPkgs, blame.m_cap, blameReqs2, matchingCap,
resourcePkgMap, allCandidates, cycleMap);
}
else
{
addUsedBlame(usedPkgBlames, blame.m_cap, blameReqs, matchingCap);
- mergeUses(rc, current, currentPkgs, blame.m_cap, blameReqs, matchingCap,
+ mergeUses(session, current, currentPkgs, blame.m_cap, blameReqs, matchingCap,
resourcePkgMap, allCandidates, cycleMap);
}
}
@@ -1004,22 +1071,22 @@
}
private void checkPackageSpaceConsistency(
- ResolveContext rc,
+ ResolveSession session,
Resource resource,
Candidates allCandidates,
Map<Resource, Packages> resourcePkgMap,
Map<Resource, Object> resultCache) throws ResolutionException
{
- if (rc.getWirings().containsKey(resource))
+ if (session.getContext().getWirings().containsKey(resource))
{
return;
}
checkDynamicPackageSpaceConsistency(
- rc, resource, allCandidates, resourcePkgMap, resultCache);
+ session, resource, allCandidates, resourcePkgMap, resultCache);
}
private void checkDynamicPackageSpaceConsistency(
- ResolveContext rc,
+ ResolveSession session,
Resource resource,
Candidates allCandidates,
Map<Resource, Packages> resourcePkgMap,
@@ -1036,6 +1103,9 @@
Candidates permutation = null;
Set<Requirement> mutated = null;
+ List<Candidates> importPermutations = session.getImportPermutations();
+ List<Candidates> usesPermutations = session.getUsesPermutations();
+
// 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
@@ -1054,9 +1124,9 @@
else if (!sourceBlame.m_cap.getResource().equals(blame.m_cap.getResource()))
{
// Try to permutate the conflicting requirement.
- permutate(allCandidates, blame.m_reqs.get(0), m_importPermutations);
+ permutate(allCandidates, blame.m_reqs.get(0), importPermutations);
// Try to permutate the source requirement.
- permutate(allCandidates, sourceBlame.m_reqs.get(0), m_importPermutations);
+ permutate(allCandidates, sourceBlame.m_reqs.get(0), importPermutations);
// Report conflict.
ResolutionException ex = new ResolutionException(
"Uses constraint violation. Unable to resolve resource "
@@ -1071,9 +1141,9 @@
+ Util.getSymbolicName(blame.m_cap.getResource())
+ " [" + blame.m_cap.getResource()
+ "] via two dependency chains.\n\nChain 1:\n"
- + toStringBlame(rc, allCandidates, sourceBlame)
+ + toStringBlame(session.getContext(), allCandidates, sourceBlame)
+ "\n\nChain 2:\n"
- + toStringBlame(rc, allCandidates, blame),
+ + toStringBlame(session.getContext(), allCandidates, blame),
null,
Collections.singleton(blame.m_reqs.get(0)));
m_logger.log(
@@ -1098,11 +1168,11 @@
}
for (UsedBlames usedBlames : pkgs.m_usedPkgs.get(pkgName))
{
- if (!isCompatible(rc, exportBlame.m_cap, usedBlames.m_cap, resourcePkgMap))
+ if (!isCompatible(session, exportBlame.m_cap, usedBlames.m_cap, resourcePkgMap))
{
for (Blame usedBlame : usedBlames.m_blames)
{
- if (checkMultiple(usedBlames, usedBlame, allCandidates))
+ if (checkMultiple(session, usedBlames, usedBlame, allCandidates))
{
// Continue to the next usedBlame, if possible we
// removed the conflicting candidates.
@@ -1125,7 +1195,7 @@
+ Util.getSymbolicName(usedBlame.m_cap.getResource())
+ " [" + usedBlame.m_cap.getResource()
+ "] via the following dependency chain:\n\n"
- + toStringBlame(rc, allCandidates, usedBlame),
+ + toStringBlame(session.getContext(), allCandidates, usedBlame),
null,
null);
@@ -1170,7 +1240,7 @@
{
if (!mutated.isEmpty())
{
- m_usesPermutations.add(permutation);
+ usesPermutations.add(permutation);
}
m_logger.log(
Logger.LOG_DEBUG,
@@ -1200,11 +1270,11 @@
}
for (UsedBlames usedBlames : pkgs.m_usedPkgs.get(pkgName))
{
- if (!isCompatible(rc, requirementBlame.m_cap, usedBlames.m_cap, resourcePkgMap))
+ if (!isCompatible(session, requirementBlame.m_cap, usedBlames.m_cap, resourcePkgMap))
{
for (Blame usedBlame : usedBlames.m_blames)
{
- if (checkMultiple(usedBlames, usedBlame, allCandidates))
+ if (checkMultiple(session, usedBlames, usedBlame, allCandidates))
{
// Continue to the next usedBlame, if possible we
// removed the conflicting candidates.
@@ -1230,9 +1300,9 @@
+ Util.getSymbolicName(usedBlame.m_cap.getResource())
+ " [" + usedBlame.m_cap.getResource()
+ "] via two dependency chains.\n\nChain 1:\n"
- + toStringBlame(rc, allCandidates, requirementBlame)
+ + toStringBlame(session.getContext(), allCandidates, requirementBlame)
+ "\n\nChain 2:\n"
- + toStringBlame(rc, allCandidates, usedBlame),
+ + toStringBlame(session.getContext(), allCandidates, usedBlame),
null,
null);
@@ -1284,7 +1354,7 @@
// Add uses permutation if we mutated any candidates.
if (!mutated.isEmpty())
{
- m_usesPermutations.add(permutation);
+ usesPermutations.add(permutation);
}
// Try to permutate the candidate for the original
@@ -1297,7 +1367,7 @@
// with existing import decisions, we may end up trying
// to permutate the same import a lot of times, so we should
// try to check if that the case and only permutate it once.
- permutateIfNeeded(allCandidates, req, m_importPermutations);
+ permutateIfNeeded(allCandidates, req, importPermutations);
}
m_logger.log(
@@ -1316,7 +1386,7 @@
// current resource depends. Keep track of the current number
// 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();
+ int permCount = usesPermutations.size() + importPermutations.size();
for (Requirement req : resource.getRequirements(null))
{
List<Capability> cands = allCandidates.getCandidates(req);
@@ -1328,7 +1398,7 @@
try
{
checkPackageSpaceConsistency(
- rc, cap.getResource(),
+ session, cap.getResource(),
allCandidates, resourcePkgMap, resultCache);
}
catch (ResolutionException ex)
@@ -1337,9 +1407,9 @@
// then we should create an import permutation for the
// requirement with the dependency on the failing resource
// to backtrack on our current candidate selection.
- if (permCount == (m_usesPermutations.size() + m_importPermutations.size()))
+ if (permCount == (usesPermutations.size() + importPermutations.size()))
{
- permutate(allCandidates, req, m_importPermutations);
+ permutate(allCandidates, req, importPermutations);
}
throw ex;
}
@@ -1349,6 +1419,7 @@
}
private boolean checkMultiple(
+ ResolveSession session,
UsedBlames usedBlames,
Blame usedBlame,
Candidates permutation)
@@ -1361,13 +1432,13 @@
{
// Create a copy of the current permutation so we can remove the
// candidates causing the blame.
- if (m_multipleCardCandidates == null)
+ if (session.getMultipleCardCandidates() == null)
{
- m_multipleCardCandidates = permutation.copy();
+ session.setMultipleCardCandidates(permutation.copy());
}
// Get the current candidate list and remove all the offending root
// cause candidates from a copy of the current permutation.
- candidates = m_multipleCardCandidates.getCandidates(req);
+ candidates = session.getMultipleCardCandidates().getCandidates(req);
candidates.removeAll(usedBlames.getRootCauses(req));
}
// We only are successful if there is at least one candidate left
@@ -1489,7 +1560,7 @@
}
private boolean isCompatible(
- ResolveContext rc, Capability currentCap, Capability candCap,
+ ResolveSession session, Capability currentCap, Capability candCap,
Map<Resource, Packages> resourcePkgMap)
{
if ((currentCap != null) && (candCap != null))
@@ -1501,12 +1572,12 @@
List<Capability> currentSources =
getPackageSources(
- rc,
+ session,
currentCap,
resourcePkgMap);
List<Capability> candSources =
getPackageSources(
- rc,
+ session,
candCap,
resourcePkgMap);
@@ -1516,20 +1587,19 @@
return true;
}
- private Map<Capability, List<Capability>> m_packageSourcesCache = new HashMap();
-
private List<Capability> getPackageSources(
- ResolveContext rc, Capability cap, Map<Resource, Packages> resourcePkgMap)
+ ResolveSession session, Capability cap, Map<Resource, Packages> resourcePkgMap)
{
+ Map<Capability, List<Capability>> packageSourcesCache = session.getPackageSourcesCache();
// If it is a package, then calculate sources for it.
if (cap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
- List<Capability> sources = m_packageSourcesCache.get(cap);
+ List<Capability> sources = packageSourcesCache.get(cap);
if (sources == null)
{
sources = getPackageSourcesInternal(
- rc, cap, resourcePkgMap, new ArrayList(), new HashSet());
- m_packageSourcesCache.put(cap, sources);
+ session.getContext(), cap, resourcePkgMap, new ArrayList(), new HashSet());
+ packageSourcesCache.put(cap, sources);
}
return sources;
}