[FELIX-4656] Avoid trying to solve the same set multiple times

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1667214 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 1cb929e..f46844b 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
@@ -72,6 +72,8 @@
 
     private final Map<Capability, Requirement> m_subtitutableMap;
 
+    private final OpenHashMapSet<Requirement, Capability> m_path;
+
     /**
      * Private copy constructor used by the copy() method.
      */
@@ -82,7 +84,8 @@
         Map<Resource, WrappedResource> wrappedHosts, Map<Resource, Object> populateResultCache,
         boolean fragmentsPresent,
         Map<Resource, Boolean> onDemandResources,
-        Map<Capability, Requirement> substitutableMap)
+        Map<Capability, Requirement> substitutableMap,
+        OpenHashMapSet<Requirement, Capability> path)
     {
         m_mandatoryResources = mandatoryResources;
         m_dependentMap = dependentMap;
@@ -92,6 +95,7 @@
         m_fragmentsPresent = fragmentsPresent;
         m_validOnDemandResources = onDemandResources;
         m_subtitutableMap = substitutableMap;
+        m_path = path;
     }
 
     /**
@@ -106,6 +110,11 @@
         m_populateResultCache = new HashMap<Resource, Object>();
         m_validOnDemandResources = validOnDemandResources;
         m_subtitutableMap = new HashMap<Capability, Requirement>();
+        m_path = new OpenHashMapSet<Requirement, Capability>(3);
+    }
+
+    public Object getPath() {
+        return m_path;
     }
 
     /**
@@ -803,17 +812,31 @@
     {
         List<Capability> candidates = m_candidateMap.get(req);
         // Remove the conflicting candidate.
-        candidates.remove(0);
+        Capability cap = candidates.remove(0);
         if (candidates.isEmpty())
         {
             m_candidateMap.remove(req);
         }
+        // Update resolution path
+        CopyOnWriteSet<Capability> capPath = m_path.get(req);
+        if (capPath == null) {
+            capPath = new CopyOnWriteSet<Capability>();
+            m_path.put(req, capPath);
+        }
+        capPath.add(cap);
     }
 
     public List<Capability> clearCandidates(Requirement req, Collection<Capability> caps)
     {
         List<Capability> l = m_candidateMap.get(req);
         l.removeAll(caps);
+        // Update resolution path
+        CopyOnWriteSet<Capability> capPath = m_path.get(req);
+        if (capPath == null) {
+            capPath = new CopyOnWriteSet<Capability>();
+            m_path.put(req, capPath);
+        }
+        capPath.addAll(caps);
         return l;
     }
 
@@ -1231,7 +1254,8 @@
                 m_populateResultCache,
                 m_fragmentsPresent,
                 m_validOnDemandResources,
-                m_subtitutableMap);
+                m_subtitutableMap,
+                m_path.deepClone());
     }
 
     public void dump(ResolveContext rc)
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 1a032dc..1ea56ec 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
@@ -202,9 +202,24 @@
                     }
                 }
 
+                Set<Object> donePaths = new HashSet<Object>();
                 Map<Resource, ResolutionException> faultyResources = null;
                 do
                 {
+                    allCandidates = (usesPermutations.size() > 0)
+                            ? usesPermutations.remove(0)
+                            : (importPermutations.size() > 0
+                                    ? importPermutations.remove(0)
+                                    : null);
+                    if (allCandidates == null)
+                    {
+                        break;
+                    }
+                    if (!donePaths.add(allCandidates.getPath()))
+                    {
+                        continue;
+                    }
+
                     rethrow = null;
 
                     resourcePkgMap.clear();
@@ -214,9 +229,6 @@
                     // delta of the current permutation.
                     session.setMultipleCardCandidates(null);
 
-                    allCandidates = (usesPermutations.size() > 0)
-                        ? usesPermutations.remove(0)
-                        : importPermutations.remove(0);
 //allCandidates.dump();
 
                     Map<Resource, ResolutionException> currentFaultyResources = null;
@@ -295,8 +307,7 @@
                         }
                     }
                 }
-                while ((rethrow != null)
-                    && ((usesPermutations.size() > 0) || (importPermutations.size() > 0)));
+                while (rethrow != null);
 
                 // If there is a resolve exception, then determine if an
                 // optionally resolved resource is to blame (typically a fragment).