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;
         }