Applied patch (FELIX-3716) to provide support for resolving multiple cardinality
requirements.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1414522 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 cdcba43..7dc6d4f 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
@@ -47,13 +47,17 @@
 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;
 
     public ResolverImpl(Logger logger)
     {
@@ -73,7 +77,7 @@
 // TODO: RFC-112 - Need impl-specific type.
 //        Collection<Resource> ondemandFragments = (rc instanceof ResolveContextImpl)
 //            ? ((ResolveContextImpl) rc).getOndemandResources() : Collections.EMPTY_LIST;
-        Collection<Resource> ondemandFragments =  Collections.EMPTY_LIST;
+        Collection<Resource> ondemandFragments = Collections.EMPTY_LIST;
 
         boolean retry;
         do
@@ -88,7 +92,7 @@
                 // Populate mandatory resources; since these are mandatory
                 // resources, failure throws a resolve exception.
                 for (Iterator<Resource> it = mandatoryResources.iterator();
-                    it.hasNext(); )
+                    it.hasNext();)
                 {
                     Resource resource = it.next();
                     if (Util.isFragment(resource) || (rc.getWirings().get(resource) == null))
@@ -166,6 +170,10 @@
 
                     resourcePkgMap.clear();
                     m_packageSourcesCache.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;
 
                     allCandidates = (m_usesPermutations.size() > 0)
                         ? m_usesPermutations.remove(0)
@@ -229,7 +237,7 @@
                     {
                         faultyResource =
                             ((WrappedRequirement) faultyReq)
-                                .getDeclaredRequirement().getResource();
+                            .getDeclaredRequirement().getResource();
                     }
                     // Try to ignore the faulty resource if it is not mandatory.
                     if (optionalResources.remove(faultyResource))
@@ -249,6 +257,13 @@
                 // resolve, so populate the wire map.
                 else
                 {
+                    if (m_multipleCardCandidates != null)
+                    {
+                        // Candidates for multiple cardinality requirements were
+                        // removed in order to provide a consistent class space.
+                        // Use the consistent permutation
+                        allCandidates = m_multipleCardCandidates;
+                    }
                     for (Resource resource : allResources)
                     {
                         Resource target = resource;
@@ -266,8 +281,8 @@
                         {
                             wireMap =
                                 populateWireMap(
-                                    rc, allCandidates.getWrappedHost(target),
-                                    resourcePkgMap, wireMap, allCandidates);
+                                rc, allCandidates.getWrappedHost(target),
+                                resourcePkgMap, wireMap, allCandidates);
                         }
                     }
                 }
@@ -277,6 +292,7 @@
                 // Always clear the state.
                 m_usesPermutations.clear();
                 m_importPermutations.clear();
+                m_multipleCardCandidates = null;
             }
         }
         while (retry);
@@ -285,25 +301,28 @@
     }
 
     /**
-     * Resolves a dynamic requirement for the specified host resource using the specified
-     * {@link ResolveContext}.  The dynamic requirement may contain wild cards in its filter
-     * for the package name.  The matching candidates are used to resolve the requirement and
-     * the resolve context is not asked to find providers for the dynamic requirement.
-     * The host resource is expected to not be a fragment, to already be resolved and
-     * have an existing wiring provided by the resolve context.
-     * <p>
-     * This operation may resolve additional resources in order to resolve the dynamic
-     * requirement.  The returned map will contain entries for each resource that got resolved
-     * in addition to the specified host resource.  The wire list for the host resource
-     * will only contain a single wire which is for the dynamic requirement.
+     * Resolves a dynamic requirement for the specified host resource using the
+     * specified {@link ResolveContext}. The dynamic requirement may contain
+     * wild cards in its filter for the package name. The matching candidates
+     * are used to resolve the requirement and the resolve context is not asked
+     * to find providers for the dynamic requirement. The host resource is
+     * expected to not be a fragment, to already be resolved and have an
+     * existing wiring provided by the resolve context. <p> This operation may
+     * resolve additional resources in order to resolve the dynamic requirement.
+     * The returned map will contain entries for each resource that got resolved
+     * in addition to the specified host resource. The wire list for the host
+     * resource will only contain a single wire which is for the dynamic
+     * requirement.
+     *
      * @param rc the resolve context
      * @param host the hosting resource
      * @param dynamicReq the dynamic requirement
      * @param matches a list of matching capabilities
-     * @param ondemandFragments collection of on demand fragments that will attach to any host that is a candidate
+     * @param ondemandFragments collection of on demand fragments that will
+     * attach to any host that is a candidate
      * @return The new resources and wires required to satisfy the specified
-     *         dynamic requirement. The returned map is the property of the caller
-     *         and can be modified by the caller.
+     * dynamic requirement. The returned map is the property of the caller and
+     * can be modified by the caller.
      * @throws ResolutionException
      */
     public Map<Resource, List<Wire>> resolve(
@@ -422,7 +441,7 @@
                         {
                             faultyResource =
                                 ((WrappedRequirement) faultyReq)
-                                    .getDeclaredRequirement().getResource();
+                                .getDeclaredRequirement().getResource();
                         }
                         // Try to ignore the faulty resource if it is not mandatory.
                         if (ondemandFragments.remove(faultyResource))
@@ -504,10 +523,10 @@
                 Requirement r = wire.getRequirement();
                 if (!r.getResource().equals(wire.getRequirer())
                     || ((r.getDirectives()
-                        .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE) != null)
+                    .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE) != null)
                     && r.getDirectives()
-                        .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE)
-                            .equals(PackageNamespace.RESOLUTION_DYNAMIC)))
+                    .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE)
+                    .equals(PackageNamespace.RESOLUTION_DYNAMIC)))
                 {
                     r = new WrappedRequirement(wire.getRequirer(), r);
                 }
@@ -554,10 +573,7 @@
         {
             for (Requirement req : resource.getRequirements(null))
             {
-                String resolution = req.getDirectives()
-                    .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE);
-                if ((resolution == null)
-                    || !resolution.equals(PackageNamespace.RESOLUTION_DYNAMIC))
+                if (!Util.isDynamic(req))
                 {
                     // Get the candidates for the current requirement.
                     List<Capability> candCaps = allCandidates.getCandidates(req);
@@ -567,10 +583,24 @@
                         continue;
                     }
 
-                    // Grab first (i.e., highest priority) candidate.
-                    Capability cap = candCaps.get(0);
-                    reqs.add(req);
-                    caps.add(cap);
+                    // For multiple cardinality requirements, we need to grab
+                    // all candidates.
+                    if (Util.isMultiple(req))
+                    {
+                        // Use the same requirement, but list each capability separately
+                        for (Capability cap : candCaps)
+                        {
+                            reqs.add(req);
+                            caps.add(cap);
+                        }
+                    }
+                    // Grab first (i.e., highest priority) candidate
+                    else
+                    {
+                        Capability cap = candCaps.get(0);
+                        reqs.add(req);
+                        caps.add(cap);
+                    }
                 }
             }
         }
@@ -593,8 +623,7 @@
             // package, which would be illegal and shouldn't be allowed.
             if (isDynamicImporting && ((i + 1) == reqs.size()))
             {
-                String pkgName = (String)
-                    cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
+                String pkgName = (String) cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
                 if (resourcePkgs.m_exportedPkgs.containsKey(pkgName)
                     || resourcePkgs.m_importedPkgs.containsKey(pkgName)
                     || resourcePkgs.m_requiredPkgs.containsKey(pkgName))
@@ -604,8 +633,7 @@
                         + resource
                         + " cannot dynamically import package '"
                         + pkgName
-                        + "' since it already has access to it."
-                        );
+                        + "' since it already has access to it.");
                 }
             }
 
@@ -652,6 +680,7 @@
                         resourcePkgs,
                         cap,
                         blameReqs,
+                        cap,
                         resourcePkgMap,
                         allCandidates,
                         usesCycleMap);
@@ -665,7 +694,7 @@
                     // Ignore resources that import from themselves.
                     if (!blame.m_cap.getResource().equals(resource))
                     {
-                        List<Requirement> blameReqs = new ArrayList();
+                        List<Requirement> blameReqs = new ArrayList<Requirement>();
                         blameReqs.add(blame.m_reqs.get(0));
 
                         mergeUses(
@@ -674,6 +703,7 @@
                             resourcePkgs,
                             blame.m_cap,
                             blameReqs,
+                            null,
                             resourcePkgMap,
                             allCandidates,
                             usesCycleMap);
@@ -685,7 +715,7 @@
             {
                 for (Blame blame : entry.getValue())
                 {
-                    List<Requirement> blameReqs = new ArrayList();
+                    List<Requirement> blameReqs = new ArrayList<Requirement>();
                     blameReqs.add(blame.m_reqs.get(0));
 
                     mergeUses(
@@ -694,6 +724,7 @@
                         resourcePkgs,
                         blame.m_cap,
                         blameReqs,
+                        null,
                         resourcePkgMap,
                         allCandidates,
                         usesCycleMap);
@@ -758,7 +789,7 @@
                     {
                         String value = w.getRequirement()
                             .getDirectives()
-                                .get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
+                            .get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
                         if ((value != null)
                             && value.equals(BundleNamespace.VISIBILITY_REEXPORT))
                         {
@@ -782,7 +813,7 @@
                     {
                         String value =
                             req.getDirectives()
-                                .get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
+                            .get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
                         if ((value != null)
                             && value.equals(BundleNamespace.VISIBILITY_REEXPORT)
                             && (allCandidates.getCandidates(req) != null))
@@ -814,10 +845,9 @@
             // Merge the candidate capability into the resource's package space
             // for imported or required packages, appropriately.
 
-            String pkgName = (String)
-                candCap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
+            String pkgName = (String) candCap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
 
-            List blameReqs = new ArrayList();
+            List<Requirement> blameReqs = new ArrayList<Requirement>();
             blameReqs.add(currentReq);
 
             Packages currentPkgs = resourcePkgMap.get(current);
@@ -839,7 +869,7 @@
 
     private void mergeUses(
         ResolveContext rc, Resource current, Packages currentPkgs,
-        Capability mergeCap, List<Requirement> blameReqs,
+        Capability mergeCap, List<Requirement> blameReqs, Capability matchingCap,
         Map<Resource, Packages> resourcePkgMap,
         Candidates allCandidates,
         Map<Capability, List<Resource>> cycleMap)
@@ -916,26 +946,28 @@
                     continue;
                 }
 
-                List<Blame> usedCaps = currentPkgs.m_usedPkgs.get(usedPkgName);
-                if (usedCaps == null)
+                List<UsedBlames> usedPkgBlames = currentPkgs.m_usedPkgs.get(usedPkgName);
+                if (usedPkgBlames == null)
                 {
-                    usedCaps = new ArrayList<Blame>();
-                    currentPkgs.m_usedPkgs.put(usedPkgName, usedCaps);
+                    usedPkgBlames = new ArrayList<UsedBlames>();
+                    currentPkgs.m_usedPkgs.put(usedPkgName, usedPkgBlames);
                 }
                 for (Blame blame : candSourceBlames)
                 {
                     if (blame.m_reqs != null)
                     {
-                        List<Requirement> blameReqs2 = new ArrayList(blameReqs);
+                        List<Requirement> blameReqs2 = new ArrayList<Requirement>(blameReqs);
+                        // Only add the last requirement in blame chain because
+                        // that is the requirement wired to the blamed capability
                         blameReqs2.add(blame.m_reqs.get(blame.m_reqs.size() - 1));
-                        usedCaps.add(new Blame(blame.m_cap, blameReqs2));
-                        mergeUses(rc, current, currentPkgs, blame.m_cap, blameReqs2,
+                        addUsedBlame(usedPkgBlames, blame.m_cap, blameReqs2, matchingCap);
+                        mergeUses(rc, current, currentPkgs, blame.m_cap, blameReqs2, matchingCap,
                             resourcePkgMap, allCandidates, cycleMap);
                     }
                     else
                     {
-                        usedCaps.add(new Blame(blame.m_cap, blameReqs));
-                        mergeUses(rc, current, currentPkgs, blame.m_cap, blameReqs,
+                        addUsedBlame(usedPkgBlames, blame.m_cap, blameReqs, matchingCap);
+                        mergeUses(rc, current, currentPkgs, blame.m_cap, blameReqs, matchingCap,
                             resourcePkgMap, allCandidates, cycleMap);
                     }
                 }
@@ -943,6 +975,34 @@
         }
     }
 
+    private static void addUsedBlame(
+        List<UsedBlames> usedBlames, Capability usedCap,
+        List<Requirement> blameReqs, Capability matchingCap)
+    {
+        // Create a new Blame based off the used capability and the
+        // blame chain requirements.
+        Blame newBlame = new Blame(usedCap, blameReqs);
+        // Find UsedBlame that uses the same capablity as the new blame.
+        UsedBlames addToBlame = null;
+        for (UsedBlames usedBlame : usedBlames)
+        {
+            if (usedCap.equals(usedBlame.m_cap))
+            {
+                addToBlame = usedBlame;
+                break;
+            }
+        }
+        if (addToBlame == null)
+        {
+            // If none exist create a new UsedBlame for the capability.
+            addToBlame = new UsedBlames(usedCap);
+            usedBlames.add(addToBlame);
+        }
+        // Add the new Blame and record the matching capability cause
+        // in case the root requirement has multiple cardinality.
+        addToBlame.addBlame(newBlame, matchingCap);
+    }
+
     private void checkPackageSpaceConsistency(
         ResolveContext rc,
         Resource resource,
@@ -1036,18 +1096,26 @@
             {
                 continue;
             }
-            for (Blame usedBlame : pkgs.m_usedPkgs.get(pkgName))
+            for (UsedBlames usedBlames : pkgs.m_usedPkgs.get(pkgName))
             {
-                if (!isCompatible(rc, exportBlame.m_cap, usedBlame.m_cap, resourcePkgMap))
+                if (!isCompatible(rc, exportBlame.m_cap, usedBlames.m_cap, resourcePkgMap))
                 {
-                    // Create a candidate permutation that eliminates all candidates
-                    // that conflict with existing selected candidates.
-                    permutation = (permutation != null)
-                        ? permutation
-                        : allCandidates.copy();
-                    rethrow = (rethrow != null)
-                        ? rethrow
-                        : new ResolutionException(
+                    for (Blame usedBlame : usedBlames.m_blames)
+                    {
+                        if (checkMultiple(usedBlames, usedBlame, allCandidates))
+                        {
+                            // Continue to the next usedBlame, if possible we
+                            // removed the conflicting candidates.
+                            continue;
+                        }
+                        // Create a candidate permutation that eliminates all candidates
+                        // that conflict with existing selected candidates.
+                        permutation = (permutation != null)
+                            ? permutation
+                            : allCandidates.copy();
+                        rethrow = (rethrow != null)
+                            ? rethrow
+                            : new ResolutionException(
                             "Uses constraint violation. Unable to resolve resource "
                             + Util.getSymbolicName(resource)
                             + " [" + resource
@@ -1061,33 +1129,38 @@
                             null,
                             null);
 
-                    mutated = (mutated != null)
-                        ? mutated
-                        : new HashSet<Requirement>();
+                        mutated = (mutated != null)
+                            ? mutated
+                            : new HashSet<Requirement>();
 
-                    for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
-                    {
-                        Requirement req = usedBlame.m_reqs.get(reqIdx);
-
-                        // If we've already permutated this requirement in another
-                        // uses constraint, don't permutate it again just continue
-                        // with the next uses constraint.
-                        if (mutated.contains(req))
+                        for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
                         {
-                            break;
-                        }
+                            Requirement req = usedBlame.m_reqs.get(reqIdx);
+                            // Sanity check for multiple.
+                            if (Util.isMultiple(req))
+                            {
+                                continue;
+                            }
+                            // If we've already permutated this requirement in another
+                            // uses constraint, don't permutate it again just continue
+                            // with the next uses constraint.
+                            if (mutated.contains(req))
+                            {
+                                break;
+                            }
 
-                        // See if we can permutate the candidates for blamed
-                        // requirement; there may be no candidates if the resource
-                        // associated with the requirement is already resolved.
-                        List<Capability> candidates = permutation.getCandidates(req);
-                        if ((candidates != null) && (candidates.size() > 1))
-                        {
-                            mutated.add(req);
-                            // Remove the conflicting candidate.
-                            candidates.remove(0);
-                            // Continue with the next uses constraint.
-                            break;
+                            // See if we can permutate the candidates for blamed
+                            // requirement; there may be no candidates if the resource
+                            // associated with the requirement is already resolved.
+                            List<Capability> candidates = permutation.getCandidates(req);
+                            if ((candidates != null) && (candidates.size() > 1))
+                            {
+                                mutated.add(req);
+                                // Remove the conflicting candidate.
+                                candidates.remove(0);
+                                // Continue with the next uses constraint.
+                                break;
+                            }
                         }
                     }
                 }
@@ -1112,10 +1185,11 @@
         // We combine the imported and required packages here into one map.
         // Imported packages are added after required packages because they shadow or override
         // the packages from required bundles.
-        Map<String, List<Blame>> allImportRequirePkgs = new HashMap<String, List<Blame>>(pkgs.m_requiredPkgs);
+        Map<String, List<Blame>> allImportRequirePkgs =
+            new HashMap<String, List<Blame>>(pkgs.m_requiredPkgs);
         allImportRequirePkgs.putAll(pkgs.m_importedPkgs);
 
-        for (Entry<String, List<Blame>> pkgEntry: allImportRequirePkgs.entrySet())
+        for (Entry<String, List<Blame>> pkgEntry : allImportRequirePkgs.entrySet())
         {
             String pkgName = pkgEntry.getKey();
             for (Blame requirementBlame : pkgEntry.getValue())
@@ -1124,18 +1198,26 @@
                 {
                     continue;
                 }
-                for (Blame usedBlame : pkgs.m_usedPkgs.get(pkgName))
+                for (UsedBlames usedBlames : pkgs.m_usedPkgs.get(pkgName))
                 {
-                    if (!isCompatible(rc, requirementBlame.m_cap, usedBlame.m_cap, resourcePkgMap))
+                    if (!isCompatible(rc, requirementBlame.m_cap, usedBlames.m_cap, resourcePkgMap))
                     {
-                        // Create a candidate permutation that eliminates any candidates
-                        // that conflict with existing selected candidates.
-                        permutation = (permutation != null)
-                            ? permutation
-                            : allCandidates.copy();
-                        rethrow = (rethrow != null)
-                            ? rethrow
-                            : new ResolutionException(
+                        for (Blame usedBlame : usedBlames.m_blames)
+                        {
+                            if (checkMultiple(usedBlames, usedBlame, allCandidates))
+                            {
+                                // Continue to the next usedBlame, if possible we
+                                // removed the conflicting candidates.
+                                continue;
+                            }
+                            // Create a candidate permutation that eliminates all candidates
+                            // that conflict with existing selected candidates.
+                            permutation = (permutation != null)
+                                ? permutation
+                                : allCandidates.copy();
+                            rethrow = (rethrow != null)
+                                ? rethrow
+                                : new ResolutionException(
                                 "Uses constraint violation. Unable to resolve resource "
                                 + Util.getSymbolicName(resource)
                                 + " [" + resource
@@ -1154,33 +1236,38 @@
                                 null,
                                 null);
 
-                        mutated = (mutated != null)
-                            ? mutated
-                            : new HashSet();
+                            mutated = (mutated != null)
+                                ? mutated
+                                : new HashSet<Requirement>();
 
-                        for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
-                        {
-                            Requirement req = usedBlame.m_reqs.get(reqIdx);
-
-                            // If we've already permutated this requirement in another
-                            // uses constraint, don't permutate it again just continue
-                            // with the next uses constraint.
-                            if (mutated.contains(req))
+                            for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
                             {
-                                break;
-                            }
+                                Requirement req = usedBlame.m_reqs.get(reqIdx);
+                                // Sanity check for multiple.
+                                if (Util.isMultiple(req))
+                                {
+                                    continue;
+                                }
+                                // If we've already permutated this requirement in another
+                                // uses constraint, don't permutate it again just continue
+                                // with the next uses constraint.
+                                if (mutated.contains(req))
+                                {
+                                    break;
+                                }
 
-                            // See if we can permutate the candidates for blamed
-                            // requirement; there may be no candidates if the resource
-                            // associated with the requirement is already resolved.
-                            List<Capability> candidates = permutation.getCandidates(req);
-                            if ((candidates != null) && (candidates.size() > 1))
-                            {
-                                mutated.add(req);
-                                // Remove the conflicting candidate.
-                                candidates.remove(0);
-                                // Continue with the next uses constraint.
-                                break;
+                                // See if we can permutate the candidates for blamed
+                                // requirement; there may be no candidates if the resource
+                                // associated with the requirement is already resolved.
+                                List<Capability> candidates = permutation.getCandidates(req);
+                                if ((candidates != null) && (candidates.size() > 1))
+                                {
+                                    mutated.add(req);
+                                    // Remove the conflicting candidate.
+                                    candidates.remove(0);
+                                    // Continue with the next uses constraint.
+                                    break;
+                                }
                             }
                         }
                     }
@@ -1261,16 +1348,46 @@
         }
     }
 
+    private boolean checkMultiple(
+        UsedBlames usedBlames,
+        Blame usedBlame,
+        Candidates permutation)
+    {
+        // Check the root requirement to see if it is a multiple cardinality
+        // requirement.
+        List<Capability> candidates = null;
+        Requirement req = usedBlame.m_reqs.get(0);
+        if (Util.isMultiple(req))
+        {
+            // Create a copy of the current permutation so we can remove the
+            // candidates causing the blame.
+            if (m_multipleCardCandidates == null)
+            {
+                m_multipleCardCandidates = 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.removeAll(usedBlames.getRootCauses(req));
+        }
+        // We only are successful if there is at least one candidate left
+        // for the requirement
+        return (candidates != null) && !candidates.isEmpty();
+    }
+
     private static void permutate(
         Candidates allCandidates, Requirement req, List<Candidates> permutations)
     {
-        List<Capability> candidates = allCandidates.getCandidates(req);
-        if ((candidates != null) && (candidates.size() > 1))
+        if (!Util.isMultiple(req))
         {
-            Candidates perm = allCandidates.copy();
-            candidates = perm.getCandidates(req);
-            candidates.remove(0);
-            permutations.add(perm);
+            List<Capability> candidates = allCandidates.getCandidates(req);
+            if ((candidates != null) && (candidates.size() > 1))
+            {
+                Candidates perm = allCandidates.copy();
+                candidates = perm.getCandidates(req);
+                candidates.remove(0);
+                permutations.add(perm);
+            }
         }
     }
 
@@ -1384,14 +1501,14 @@
 
             List<Capability> currentSources =
                 getPackageSources(
-                    rc,
-                    currentCap,
-                    resourcePkgMap);
+                rc,
+                currentCap,
+                resourcePkgMap);
             List<Capability> candSources =
                 getPackageSources(
-                    rc,
-                    candCap,
-                    resourcePkgMap);
+                rc,
+                candCap,
+                resourcePkgMap);
 
             return currentSources.containsAll(candSources)
                 || candSources.containsAll(currentSources);
@@ -1530,34 +1647,41 @@
                 List<Capability> cands = allCandidates.getCandidates(req);
                 if ((cands != null) && (cands.size() > 0))
                 {
-                    Capability cand = cands.get(0);
-                    // Do not create wires for the osgi.wiring.* namespaces
-                    // if the provider and requirer are the same resource;
-                    // allow such wires for non-OSGi wiring namespaces.
-                    if (!cand.getNamespace().startsWith("osgi.wiring.")
-                        || !resource.equals(cand.getResource()))
+                    for (Capability cand : cands)
                     {
-                        if (!rc.getWirings().containsKey(cand.getResource()))
+                        // Do not create wires for the osgi.wiring.* namespaces
+                        // if the provider and requirer are the same resource;
+                        // allow such wires for non-OSGi wiring namespaces.
+                        if (!cand.getNamespace().startsWith("osgi.wiring.")
+                            || !resource.equals(cand.getResource()))
                         {
-                            populateWireMap(rc, cand.getResource(),
-                                resourcePkgMap, wireMap, allCandidates);
+                            if (!rc.getWirings().containsKey(cand.getResource()))
+                            {
+                                populateWireMap(rc, cand.getResource(),
+                                    resourcePkgMap, wireMap, allCandidates);
+                            }
+                            Wire wire = new WireImpl(
+                                unwrappedResource,
+                                getDeclaredRequirement(req),
+                                getDeclaredResource(cand.getResource()),
+                                getDeclaredCapability(cand));
+                            if (req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+                            {
+                                packageWires.add(wire);
+                            }
+                            else if (req.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
+                            {
+                                bundleWires.add(wire);
+                            }
+                            else
+                            {
+                                capabilityWires.add(wire);
+                            }
                         }
-                        Wire wire = new WireImpl(
-                            unwrappedResource,
-                            getDeclaredRequirement(req),
-                            getDeclaredResource(cand.getResource()),
-                            getDeclaredCapability(cand));
-                        if (req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+                        if (!Util.isMultiple(req))
                         {
-                            packageWires.add(wire);
-                        }
-                        else if (req.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
-                        {
-                            bundleWires.add(wire);
-                        }
-                        else
-                        {
-                            capabilityWires.add(wire);
+                            // If not multiple just create a wire for the first candidate.
+                            break;
                         }
                     }
                 }
@@ -1598,11 +1722,11 @@
                             {
                                 fragmentWires.add(
                                     new WireImpl(
-                                        getDeclaredResource(fragment),
-                                        req,
-                                        unwrappedResource,
-                                        unwrappedResource.getCapabilities(
-                                            HostNamespace.HOST_NAMESPACE).get(0)));
+                                    getDeclaredResource(fragment),
+                                    req,
+                                    unwrappedResource,
+                                    unwrappedResource.getCapabilities(
+                                    HostNamespace.HOST_NAMESPACE).get(0)));
                             }
                             // Otherwise, if the fragment isn't already resolved and
                             // this is the first time we are seeing it, then create
@@ -1637,10 +1761,10 @@
         }
         Capability cand = candidates.get(0);
         return new WireImpl(
-                getDeclaredResource(requirement.getResource()),
-                getDeclaredRequirement(requirement),
-                getDeclaredResource(cand.getResource()),
-                getDeclaredCapability(cand));
+            getDeclaredResource(requirement.getResource()),
+            getDeclaredRequirement(requirement),
+            getDeclaredResource(cand.getResource()),
+            getDeclaredCapability(cand));
     }
 
     private static boolean isPayload(Requirement fragmentReq)
@@ -1680,10 +1804,10 @@
 
         packageWires.add(
             new WireImpl(
-                resource,
-                dynReq,
-                getDeclaredResource(dynCand.getResource()),
-                getDeclaredCapability(dynCand)));
+            resource,
+            dynReq,
+            getDeclaredResource(dynCand.getResource()),
+            getDeclaredCapability(dynCand)));
 
         wireMap.put(resource, packageWires);
 
@@ -1722,7 +1846,7 @@
             System.out.println("    " + entry.getKey() + " - " + entry.getValue());
         }
         System.out.println("  USED");
-        for (Entry<String, List<Blame>> entry : packages.m_usedPkgs.entrySet())
+        for (Entry<String, List<UsedBlames>> entry : packages.m_usedPkgs.entrySet())
         {
             System.out.println("    " + entry.getKey() + " - " + entry.getValue());
         }
@@ -1774,9 +1898,9 @@
                             .get(PackageNamespace.PACKAGE_NAMESPACE).toString());
                         Capability usedCap =
                             getSatisfyingCapability(
-                                rc,
-                                allCandidates,
-                                blame.m_reqs.get(i + 1));
+                            rc,
+                            allCandidates,
+                            blame.m_reqs.get(i + 1));
                         sb.append("; uses:=");
                         sb.append(usedCap.getAttributes()
                             .get(PackageNamespace.PACKAGE_NAMESPACE));
@@ -1798,8 +1922,8 @@
                     sb.append(export.getAttributes().get(export.getNamespace()).toString());
                     if (export.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE)
                         && !export.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE)
-                            .equals(blame.m_cap.getAttributes().get(
-                                PackageNamespace.PACKAGE_NAMESPACE)))
+                        .equals(blame.m_cap.getAttributes().get(
+                        PackageNamespace.PACKAGE_NAMESPACE)))
                     {
                         sb.append("; uses:=");
                         sb.append(blame.m_cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
@@ -1867,7 +1991,7 @@
         public final Map<String, Blame> m_exportedPkgs = new HashMap();
         public final Map<String, List<Blame>> m_importedPkgs = new HashMap();
         public final Map<String, List<Blame>> m_requiredPkgs = new HashMap();
-        public final Map<String, List<Blame>> m_usedPkgs = new HashMap();
+        public final Map<String, List<UsedBlames>> m_usedPkgs = new HashMap();
         public boolean m_isCalculated = false;
 
         public Packages(Resource resource)
@@ -1893,8 +2017,8 @@
             return m_cap.getResource()
                 + "." + m_cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE)
                 + (((m_reqs == null) || m_reqs.isEmpty())
-                    ? " NO BLAME"
-                    : " BLAMED ON " + m_reqs);
+                ? " NO BLAME"
+                : " BLAMED ON " + m_reqs);
         }
 
         @Override
@@ -1904,4 +2028,80 @@
                 && m_cap.equals(((Blame) o).m_cap);
         }
     }
+
+    /*
+     * UsedBlames hold a list of Blame that have a common used capability.
+     * The UsedBlames stores sets of capabilities (root causes) that match a
+     * root requirement with multiple cardinality.  These causes are the
+     * capabilities that pulled in the common used capability.
+     * It is assumed that multiple cardinality requirements can only be
+     * root requirements of a Blame.
+     *
+     * This is only true because capabilities can only use a package
+     * capability.  They cannot use any other kind of capability so we
+     * do not have to worry about transitivity of the uses directive
+     * from other capability types.
+     */
+    private static class UsedBlames
+    {
+        public final Capability m_cap;
+        public final List<Blame> m_blames = new ArrayList<ResolverImpl.Blame>();
+        private Map<Requirement, Set<Capability>> m_rootCauses;
+
+        public UsedBlames(Capability cap)
+        {
+            m_cap = cap;
+        }
+
+        public void addBlame(Blame blame, Capability matchingRootCause)
+        {
+            if (!m_cap.equals(blame.m_cap))
+            {
+                throw new IllegalArgumentException(
+                    "Attempt to add a blame with a different used capability: "
+                    + blame.m_cap);
+            }
+            m_blames.add(blame);
+            if (matchingRootCause != null)
+            {
+                Requirement req = blame.m_reqs.get(0);
+                // Assumption made that the root requirement of the chain is the only
+                // possible multiple cardinality requirement and that the matching root cause
+                // capability is passed down from the beginning of the chain creation.
+                if (Util.isMultiple(req))
+                {
+                    // The root requirement is multiple. Need to store the root cause
+                    // so that we can find it later in case the used capability which the cause
+                    // capability pulled in is a conflict.
+                    if (m_rootCauses == null)
+                    {
+                        m_rootCauses = new HashMap<Requirement, Set<Capability>>();
+                    }
+                    Set<Capability> rootCauses = m_rootCauses.get(req);
+                    if (rootCauses == null)
+                    {
+                        rootCauses = new HashSet<Capability>();
+                        m_rootCauses.put(req, rootCauses);
+                    }
+                    rootCauses.add(matchingRootCause);
+                }
+            }
+        }
+
+        public Set<Capability> getRootCauses(Requirement req)
+        {
+            if (m_rootCauses == null)
+            {
+                return Collections.EMPTY_SET;
+            }
+            Set<Capability> result = m_rootCauses.get(req);
+            return result == null ? Collections.EMPTY_SET : result;
+        }
+
+        @Override
+        public String toString()
+        {
+            return m_blames.toString();
+        }
+    }
 }
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/Util.java b/resolver/src/main/java/org/apache/felix/resolver/Util.java
index dc5a100..84de220 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/Util.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/Util.java
@@ -78,6 +78,18 @@
         return Namespace.RESOLUTION_OPTIONAL.equalsIgnoreCase(resolution);
     }
 
+    public static boolean isMultiple(Requirement req)
+    {
+    	return Namespace.CARDINALITY_MULTIPLE.equals(req.getDirectives()
+            .get(Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE)) && !isDynamic(req);
+    }
+
+    public static boolean isDynamic(Requirement req)
+    {
+    	return PackageNamespace.RESOLUTION_DYNAMIC.equals(req.getDirectives()
+            .get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE));
+    }
+
     public static List<Requirement> getDynamicRequirements(List<Requirement> reqs)
     {
         List<Requirement> result = new ArrayList<Requirement>();
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/GenericCapability.java b/resolver/src/main/java/org/apache/felix/resolver/test/GenericCapability.java
new file mode 100644
index 0000000..26c6ec1
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/GenericCapability.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.resolver.test;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+class GenericCapability implements Capability
+{
+    private final Resource m_resource;
+    private final String m_namespace;
+    private final Map<String, String> m_dirs;
+    private final Map<String, Object> m_attrs;
+
+    public GenericCapability(Resource resource, String namespace)
+    {
+        m_resource = resource;
+        m_namespace = namespace;
+        m_dirs = new HashMap<String, String>();
+        m_attrs = new HashMap<String, Object>();
+    }
+
+    public String getNamespace()
+    {
+        return m_namespace;
+    }
+
+    public void addDirective(String name, String value)
+    {
+        m_dirs.put(name, value);
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_dirs;
+    }
+
+    public void addAttribute(String name, Object value)
+    {
+        m_attrs.put(name, value);
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_attrs;
+    }
+
+    public Resource getResource()
+    {
+        return m_resource;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getNamespace() + "; "
+            + getAttributes();
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/GenericRequirement.java b/resolver/src/main/java/org/apache/felix/resolver/test/GenericRequirement.java
new file mode 100644
index 0000000..69737a9
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/GenericRequirement.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.resolver.test;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+class GenericRequirement implements Requirement
+{
+    private final Resource m_resource;
+    private final String m_namespace;
+    private final Map<String, String> m_dirs;
+    private final Map<String, Object> m_attrs;
+
+    public GenericRequirement(Resource resource, String namespace)
+    {
+        m_resource = resource;
+        m_namespace = namespace;
+        m_dirs = new HashMap<String, String>();
+        m_attrs = new HashMap<String, Object>();
+    }
+
+    public String getNamespace()
+    {
+        return m_namespace;
+    }
+
+    public void addDirective(String name, String value)
+    {
+        m_dirs.put(name, value);
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_dirs;
+    }
+
+    public void addAttribute(String name, Object value)
+    {
+        m_attrs.put(name, value);
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_attrs;
+    }
+
+    public Resource getResource()
+    {
+        return m_resource;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getNamespace() + "; "
+            + getDirectives();
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/Main.java b/resolver/src/main/java/org/apache/felix/resolver/test/Main.java
index f273052..a5a975e 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/test/Main.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/Main.java
@@ -19,6 +19,7 @@
 package org.apache.felix.resolver.test;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -90,6 +91,13 @@
             System.out.println("EXPECTED ResolutionException:");
             e.printStackTrace(System.out);
         }
+
+        System.out.println("\nSCENARIO 6\n");
+        mandatory = populateScenario6(wirings, candMap);
+        rci = new ResolveContextImpl(wirings, candMap, mandatory, Collections.EMPTY_LIST);
+        wireMap = resolver.resolve(rci);
+        System.out.println("RESULT " + wireMap);
+
     }
 
     private static List<Resource> populateScenario1(
@@ -224,7 +232,7 @@
         ResourceImpl c = new ResourceImpl("C");
         c.addRequirement(new BundleRequirement(c, "D"));
         c.addCapability(new BundleCapability(c, "C"));
-        PackageCapability p2 = new PackageCapability(c, "p1");
+        PackageCapability p2 = new PackageCapability(c, "p2");
         p2.addDirective(Namespace.CAPABILITY_USES_DIRECTIVE, "p1");
         c.addCapability(p2);
 
@@ -268,7 +276,7 @@
         ResourceImpl c = new ResourceImpl("C");
         c.addRequirement(new BundleRequirement(c, "D"));
         c.addCapability(new BundleCapability(c, "C"));
-        PackageCapability p2 = new PackageCapability(c, "p1");
+        PackageCapability p2 = new PackageCapability(c, "p2");
         p2.addDirective(Namespace.CAPABILITY_USES_DIRECTIVE, "p1");
         c.addCapability(p2);
 
@@ -293,4 +301,102 @@
         resources.add(x);
         return resources;
     }
+
+    private static List<Resource> populateScenario6(
+            Map<Resource, Wiring> wirings, Map<Requirement, List<Capability>> candMap)
+    {
+        wirings.clear();
+        candMap.clear();
+
+        ResourceImpl a1 = new ResourceImpl("A");
+        a1.addRequirement(new PackageRequirement(a1, "p1"));
+        a1.addRequirement(new PackageRequirement(a1, "p2"));
+        Requirement a1Req = new GenericRequirement(a1, "generic");
+        a1Req.getDirectives().put(Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE, Namespace.CARDINALITY_MULTIPLE);
+        a1.addRequirement(a1Req);
+
+        ResourceImpl a2 = new ResourceImpl("A");
+        a2.addRequirement(new BundleRequirement(a2, "B"));
+        a2.addRequirement(new BundleRequirement(a2, "C"));
+        Requirement a2Req = new GenericRequirement(a2, "generic");
+        a2Req.getDirectives().put(Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE, Namespace.CARDINALITY_MULTIPLE);
+        a2.addRequirement(a2Req);
+
+        ResourceImpl b1 = new ResourceImpl("B");
+        b1.addCapability(new BundleCapability(b1, "B"));
+        Capability b1_p2 = new PackageCapability(b1, "p2");
+        b1_p2.getDirectives().put(Namespace.CAPABILITY_USES_DIRECTIVE, "p1");
+        b1.addCapability(b1_p2);
+        b1.addRequirement(new PackageRequirement(b1, "p1"));
+
+        ResourceImpl b2 = new ResourceImpl("B");
+        b2.addCapability(new BundleCapability(b2, "B"));
+        Capability b2_p2 = new PackageCapability(b2, "p2");
+        b2_p2.getDirectives().put(Namespace.CAPABILITY_USES_DIRECTIVE, "p1");
+        b2.addCapability(b2_p2);
+        b2.addRequirement(new PackageRequirement(b2, "p1"));
+
+        ResourceImpl c1 = new ResourceImpl("C");
+        c1.addCapability(new BundleCapability(c1, "C"));
+        Capability c1_p1 = new PackageCapability(c1, "p1");
+
+        ResourceImpl c2 = new ResourceImpl("C");
+        c2.addCapability(new BundleCapability(c2, "C"));
+        Capability c2_p1 = new PackageCapability(c2, "p1");
+
+        ResourceImpl d1 = new ResourceImpl("D");
+        GenericCapability d1_generic = new GenericCapability(d1, "generic");
+        d1_generic.addDirective(Namespace.CAPABILITY_USES_DIRECTIVE, "p1,p2");
+        d1.addCapability(d1_generic);
+        d1.addRequirement(new PackageRequirement(d1, "p1"));
+        d1.addRequirement(new PackageRequirement(d1, "p2"));
+
+        ResourceImpl d2 = new ResourceImpl("D");
+        GenericCapability d2_generic = new GenericCapability(d2, "generic");
+        d2_generic.addDirective(Namespace.CAPABILITY_USES_DIRECTIVE, "p1,p2");
+        d2.addCapability(d2_generic);
+        d2.addRequirement(new PackageRequirement(d2, "p1"));
+        d2.addRequirement(new PackageRequirement(d2, "p2"));
+
+        candMap.put(
+            a1.getRequirements(null).get(0),
+            Arrays.asList(c2_p1));
+        candMap.put(
+            a1.getRequirements(null).get(1),
+            Arrays.asList(b2_p2));
+        candMap.put(
+            a1.getRequirements(null).get(2),
+            Arrays.asList((Capability) d1_generic, (Capability) d2_generic));
+        candMap.put(
+            a2.getRequirements(null).get(0),
+            c2.getCapabilities(BundleNamespace.BUNDLE_NAMESPACE));
+        candMap.put(
+            a2.getRequirements(null).get(1),
+            b2.getCapabilities(BundleNamespace.BUNDLE_NAMESPACE));
+        candMap.put(
+            a2.getRequirements(null).get(2),
+            Arrays.asList((Capability) d1_generic, (Capability) d2_generic));
+        candMap.put(
+            b1.getRequirements(null).get(0),
+            Arrays.asList(c1_p1, c2_p1));
+        candMap.put(
+            b2.getRequirements(null).get(0),
+            Arrays.asList(c1_p1, c2_p1));
+        candMap.put(
+            d1.getRequirements(null).get(0),
+            Arrays.asList(c1_p1, c2_p1));
+        candMap.put(
+            d1.getRequirements(null).get(1),
+            Arrays.asList(b1_p2, b2_p2));
+        candMap.put(
+            d2.getRequirements(null).get(0),
+            Arrays.asList(c1_p1, c2_p1));
+        candMap.put(
+            d2.getRequirements(null).get(1),
+            Arrays.asList(b1_p2, b2_p2));
+        List<Resource> resources = new ArrayList<Resource>();
+        resources.add(a1);
+        resources.add(a2);
+        return resources;
+    }
 }
\ No newline at end of file