You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2010/04/01 21:33:00 UTC

svn commit: r930066 - in /felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver: Main.java Resolver.java ResolverStateImpl.java proto3/Proto3Resolver.java

Author: rickhall
Date: Thu Apr  1 19:33:00 2010
New Revision: 930066

URL: http://svn.apache.org/viewvc?rev=930066&view=rev
Log:
Align with trunk resolver.

Modified:
    felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Main.java
    felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Resolver.java
    felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/ResolverStateImpl.java
    felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto3/Proto3Resolver.java

Modified: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Main.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Main.java?rev=930066&r1=930065&r2=930066&view=diff
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Main.java (original)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Main.java Thu Apr  1 19:33:00 2010
@@ -156,9 +156,7 @@ public class Main
     }
 
     // SOLUTION:
-    // A: bar->B, woz->C
-    // C: bar->B, dit->D
-    // D: dot->E
+    // A: bar->B, woz->E
     private static Module scenario2(List<Module> moduleList)
     {
         Module m, target;
@@ -614,7 +612,7 @@ public class Main
     }
 
     // SOLUTION:
-    // A: require->[B,C]
+    // A: bar->[B,C]
     // B: foo->D
     // C: foo->E
     private static Module scenario12(List<Module> moduleList)
@@ -689,7 +687,7 @@ public class Main
     }
 
     // SOLUTION:
-    // A: require->[B,C]
+    // A: bar->[B,C]
     private static Module scenario14(List<Module> moduleList)
     {
         Module m, target;
@@ -716,7 +714,7 @@ public class Main
 
     // SOLUTION:
     // A: bar->B, foo->F
-    // B: require->[C,D]
+    // B: bar->[C,D]
     // D: foo->F
     private static Module scenario15(List<Module> moduleList)
     {
@@ -758,7 +756,7 @@ public class Main
 
     // SOLUTION:
     // A: foo->B, bar->C
-    // B: require->[C,D]
+    // B: bar->[C,D]
     private static Module scenario16(List<Module> moduleList)
     {
         Module m, target;
@@ -841,8 +839,8 @@ public class Main
 
     // SOLUTION:
     // A: foo->B, bar->D, woz->BB
-    // B: require->[C,D]
-    // BB: require->[CC,D]
+    // B: bar->[C,D]
+    // BB: bar->[CC,D]
     private static Module scenario18(List<Module> moduleList)
     {
         Module m, target;
@@ -954,7 +952,7 @@ public class Main
     }
 
     // SOLUTION:
-    // B: foo->A, require->[A]
+    // B: foo->A, foo->[A]
 // TODO: FELIX3 - This is a little odd since B's export doesn't match its import.
 //       The resolver detected this as a conflict, so we had to loosen our conflict
 //       detection in mergeCandidatePackage().
@@ -979,7 +977,8 @@ public class Main
     }
 
     // SOLUTION:
-    // A: require->[B]->[C]
+    // A: foo->[B], bar->[B]
+    // B: foo->[C]
     private static Module scenario22(List<Module> moduleList)
     {
         Module m, target;
@@ -1005,14 +1004,15 @@ public class Main
     }
 
     // SOLUTION:
-    // A: require->[B]->[C]
+    // A: foo->[B], bar->[C]
+    // C: foo->[A], bar->[A]
     private static Module scenario23(List<Module> moduleList)
     {
         Module m, target;
 
         // Bundle B
         moduleList.add(
-            target = (m = new Module("B"))
+            (m = new Module("B"))
                 .providing(new CapabilityImpl(m, Capability.MODULE_NAMESPACE).with("bundle-symbolic-name=B"))
                 .providing(new CapabilityImpl(m, Capability.PACKAGE_NAMESPACE).with("package=foo")));
         // Bundle C
@@ -1023,7 +1023,7 @@ public class Main
                 .requiring(new RequirementImpl(Capability.MODULE_NAMESPACE).with("bundle-symbolic-name=A")));
         // Bundle A
         moduleList.add(
-            (m = new Module("A"))
+            target = (m = new Module("A"))
                 .providing(new CapabilityImpl(m, Capability.MODULE_NAMESPACE).with("bundle-symbolic-name=A"))
                 .requiring(new RequirementImpl(Capability.MODULE_NAMESPACE).with("bundle-symbolic-name=C").with("visibility:=reexport"))
                 .requiring(new RequirementImpl(Capability.MODULE_NAMESPACE).with("bundle-symbolic-name=B").with("visibility:=reexport")));
@@ -1032,7 +1032,8 @@ public class Main
     }
 
     // SOLUTION:
-    // A: require->[B]->[C]
+    // A: bar->[B]
+    // B: bar->[A]
     private static Module scenario24(List<Module> moduleList)
     {
         Module m, target;

Modified: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Resolver.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Resolver.java?rev=930066&r1=930065&r2=930066&view=diff
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Resolver.java (original)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/Resolver.java Thu Apr  1 19:33:00 2010
@@ -32,5 +32,7 @@ public interface Resolver
     public static interface ResolverState
     {
         Set<Capability> getCandidates(Module module, Requirement req, boolean obeyMandatory);
+        void checkExecutionEnvironment(Module module) throws ResolveException;
+        void checkNativeLibraries(Module module) throws ResolveException;
     }
 }
\ No newline at end of file

Modified: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/ResolverStateImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/ResolverStateImpl.java?rev=930066&r1=930065&r2=930066&view=diff
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/ResolverStateImpl.java (original)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/ResolverStateImpl.java Thu Apr  1 19:33:00 2010
@@ -88,4 +88,14 @@ public class ResolverStateImpl implement
 
         return result;
     }
+
+    public void checkExecutionEnvironment(Module module) throws ResolveException
+    {
+        // Ignore.
+    }
+
+    public void checkNativeLibraries(Module module) throws ResolveException
+    {
+        // Ignore.
+    }
 }
\ No newline at end of file

Modified: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto3/Proto3Resolver.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto3/Proto3Resolver.java?rev=930066&r1=930065&r2=930066&view=diff
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto3/Proto3Resolver.java (original)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto3/Proto3Resolver.java Thu Apr  1 19:33:00 2010
@@ -19,6 +19,7 @@
 package org.apache.felix.resolver.proto3;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -67,6 +68,8 @@ System.out.println("+++ PROTO3 RESOLVER"
 
     public Map<Module, List<Wire>> resolve(ResolverState state, Module module)
     {
+        m_invokeCounts.clear();
+
         if (m_isInvokeCount)
         {
             String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
@@ -87,8 +90,7 @@ System.out.println("+++ RESOLVING " + mo
             Map<Requirement, Set<Capability>> candidateMap =
                 new HashMap<Requirement, Set<Capability>>();
 
-            populateCandidates(state, module,
-                candidateMap, new HashMap<Module, Object>());
+            populateCandidates(state, module, candidateMap, new HashMap<Module, Object>());
             m_candidatePermutations.add(candidateMap);
 
             ResolveException rethrow = null;
@@ -138,6 +140,14 @@ dumpCandidateMap(state, candidateMap);
 
     public Map<Module, List<Wire>> resolve(ResolverState state, Module module, String pkgName)
     {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
         Capability candidate = null;
 
         // We can only create a dynamic import if the following
@@ -150,18 +160,18 @@ dumpCandidateMap(state, candidateMap);
         // The following call checks all of these conditions and returns
         // a matching dynamic requirement if possible.
         Map<Requirement, Set<Capability>> candidateMap =
-            new HashMap<Requirement, Set<Capability>>();
-        if (isAllowedDynamicImport(state, module, pkgName, candidateMap))
+            getDynamicImportCandidates(state, module, pkgName);
+        if (candidateMap != null)
         {
             m_candidatePermutations.clear();
 
-            Map<Module, List<Wire>> wireMap = new HashMap<Module, List<Wire>>();
-
-            Map<Module, Packages> modulePkgMap = new HashMap<Module, Packages>();
+            Map<Module, List<Wire>> wireMap = new HashMap();
+            Map<Module, Packages> modulePkgMap = new HashMap();
 
 //System.out.println("+++ DYNAMICALLY RESOLVING " + module + " - " + pkgName);
             populateDynamicCandidates(state, module, candidateMap);
             m_candidatePermutations.add(candidateMap);
+
             ResolveException rethrow = null;
 
             do
@@ -205,17 +215,22 @@ dumpCandidateMap(state, candidateMap);
     }
 
     // TODO: FELIX3 - It would be nice to make this private.
-    // TODO: FELIX3 - At a minimum, figure out a different way than passing in the
-    //       candidate map.
-    public static boolean isAllowedDynamicImport(
-        ResolverState state, Module module, String pkgName, Map<Requirement,
-        Set<Capability>> candidateMap)
+    public static Map<Requirement, Set<Capability>> getDynamicImportCandidates(
+        ResolverState state, Module module, String pkgName)
     {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
         // Unresolved modules cannot dynamically import, nor can the default
         // package be dynamically imported.
         if (!module.isResolved() || pkgName.length() == 0)
         {
-            return false;
+            return null;
         }
 
         // If any of the module exports this package, then we cannot
@@ -226,7 +241,7 @@ dumpCandidateMap(state, candidateMap);
             if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
                 && caps.get(i).getAttribute(Capability.PACKAGE_ATTR).getValue().equals(pkgName))
             {
-                return false;
+                return null;
             }
         }
         // If any of our wires have this package, then we cannot
@@ -236,17 +251,18 @@ dumpCandidateMap(state, candidateMap);
         {
             if (wires.get(i).hasPackage(pkgName))
             {
-                return false;
+                return null;
             }
         }
 
         // Loop through the importer's dynamic requirements to determine if
         // there is a matching one for the package from which we want to
         // load a class.
-        List<Directive> dirs = new ArrayList(0);
+        List<Directive> dirs = Collections.EMPTY_LIST;
         List<Attribute> attrs = new ArrayList(1);
         attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
-        Requirement req = new RequirementImpl(Capability.PACKAGE_NAMESPACE, dirs, attrs);
+        Requirement req = new RequirementImpl(
+            Capability.PACKAGE_NAMESPACE, dirs, attrs);
         Set<Capability> candidates = state.getCandidates(module, req, false);
         List<Requirement> dynamics = module.getDynamicRequirements();
 
@@ -279,18 +295,21 @@ dumpCandidateMap(state, candidateMap);
                     itCand.remove();
                 }
             }
-            
-            if (candidates.size() > 0)
-            {
-                candidateMap.put(dynReq, candidates);
-            }
         }
         else
         {
             candidates.clear();
         }
 
-        return !candidates.isEmpty();
+
+        if (candidates.size() > 0)
+        {
+            Map<Requirement, Set<Capability>> candidateMap = new HashMap();
+            candidateMap.put(dynReq, candidates);
+            return candidateMap;
+        }
+
+        return null;
     }
 
     private static void dumpCandidateMap(
@@ -426,6 +445,12 @@ dumpCandidateMap(state, candidateMap);
         // do some one-time checks and initialization.
         if ((remainingReqs == null) && (localCandidateMap == null))
         {
+            // Verify that any required execution environment is satisfied.
+            state.checkExecutionEnvironment(module);
+
+            // Verify that any native libraries match the current platform.
+            state.checkNativeLibraries(module);
+
             // Record cycle count.
             cycleCount = new Integer(0);
 
@@ -696,11 +721,7 @@ System.out.println("+++ TRYING CAND " + 
                     {
 System.out.println("RE: " + ex);
 ex.printStackTrace();
-
-// TODO: FELIX3 RESOLVER - Is it ok to remove the failed candidate? By removing
-//       it we keep the candidateMap up to date with the selected candidate, but
-//       theoretically this eliminates some potential combinations. Are those
-//       combinations guaranteed to be failures so eliminating them is ok?
+                        // Remove the current candidate since it failed to resolve.
                         it.remove();
                         if (!it.hasNext() && !req.isOptional())
                         {
@@ -787,10 +808,7 @@ ex.printStackTrace();
                 {
 System.out.println("RE: " + ex);
 ex.printStackTrace();
-// TODO: FELIX3 RESOLVER - Is it ok to remove the failed candidate? By removing
-//       it we keep the candidateMap up to date with the selected candidate, but
-//       theoretically this eliminates some potential combinations. Are those
-//       combinations guaranteed to be failures so eliminating them is ok?
+                    // Remove the current candidate since it failed to resolve.
                     it.remove();
                     if (!it.hasNext() && !req.isOptional())
                     {
@@ -821,7 +839,7 @@ ex.printStackTrace();
         {
             for (int i = 0; i < caps.size(); i++)
             {
-// TODO: PROTO3 RESOLVER - Assume if a module imports the same package it
+// TODO: FELIX3 - Assume if a module imports the same package it
 //       exports that the import will overlap the export.
                 if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
                     && !hasOverlappingImport(module, caps.get(i)))
@@ -882,8 +900,8 @@ ex.printStackTrace();
             // will be visible to the current module.
             Packages candPkgs = modulePkgMap.get(candCap.getModule());
 
-// TODO: PROTO3 RESOLVER - For now assume only exports, but eventually we also
-//       have to support re-exported packages.
+            // We have to merge all exported packages from the candidate,
+            // since the current module requires it.
             for (Entry<String, Blame> entry : candPkgs.m_exportedPkgs.entrySet())
             {
                 mergeCandidatePackage(
@@ -893,31 +911,21 @@ ex.printStackTrace();
                     modulePkgMap,
                     candidateMap);
             }
-            for (Entry<String, List<Blame>> entry : candPkgs.m_requiredPkgs.entrySet())
-            {
-                List<Blame> blames = entry.getValue();
-                for (Blame blame : blames)
-                {
-// TODO: FELIX3 RESOLVER - Since a single module requirement can include many packages,
-//       it is likely we call merge too many times for the same module req. If we knew
-//       which candidates were being used to resolve this candidate's module dependencies,
-//       then we could just try to merge them directly. This info would also help in
-//       in creating wires, since we ultimately want to create wires for the selected
-//       candidates, which we are trying to deduce from the package space, but if we
-//       knew the selected candidates, we'd be done.
-                    if (blame.m_cap.getModule().equals(current))
-                    {
-                        continue;
-                    }
 
-                    Directive dir = blame.m_reqs.get(blame.m_reqs.size() - 1)
-                        .getDirective(Constants.VISIBILITY_DIRECTIVE);
-                    if ((dir != null) && dir.getValue().equals(Constants.VISIBILITY_REEXPORT))
+            // If the candidate requires any other bundles with reexport visibility,
+            // then we also need to merge their packages too.
+            for (Requirement req : candCap.getModule().getRequirements())
+            {
+                if (req.getNamespace().equals(Capability.MODULE_NAMESPACE))
+                {
+                    Directive dir = req.getDirective(Constants.VISIBILITY_DIRECTIVE);
+                    if ((dir != null) && dir.getValue().equals(Constants.VISIBILITY_REEXPORT)
+                        && (candidateMap.get(req) != null))
                     {
-                        mergeCandidatePackage(
+                        mergeCandidatePackages(
                             current,
-                            true,
-                            new Blame(outgoingReqs, blame.m_cap),
+                            outgoingReqs,
+                            candidateMap.get(req).iterator().next(),
                             modulePkgMap,
                             candidateMap);
                     }
@@ -939,7 +947,7 @@ ex.printStackTrace();
             m_invokeCounts.put(methodName, count);
         }
 
-// TODO: PROTO3 RESOLVER - Check for merging where module imports from itself,
+// TODO: FELIX3 - Check for merging where module imports from itself,
 //       then it should be listed as an export for requiring bundles.
         if (candBlame.m_cap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
         {
@@ -1013,7 +1021,7 @@ ex.printStackTrace();
                     currentRequiredBlames = new ArrayList<Blame>();
                     currentPkgsCopy.m_requiredPkgs.put(pkgName, currentRequiredBlames);
                 }
-// TODO: PROTO3 RESOLVER - This is potentially modifying the original, we need to modify a copy.
+// TODO: FELIX3 - This is potentially modifying the original, we need to modify a copy.
                 currentRequiredBlames.add(candBlame);
             }
             else
@@ -1048,6 +1056,14 @@ dumpModulePkgs(current, currentPkgs);
         Blame candBlame, Map<Module, Packages> modulePkgMap,
         Map<Requirement, Set<Capability>> candidateMap)
     {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
         for (int i = 0; (currentUsedBlames != null) && (i < currentUsedBlames.size()); i++)
         {
 //System.out.println("+++ CHECK " + candBlame + " IN EXISTING " + currentUsedBlames.get(i));
@@ -1065,7 +1081,7 @@ dumpModulePkgs(current, currentPkgs);
         }
     }
 
-// TODO: PROTO3 RESOLVER - We end up with duplicates in uses constraints,
+// TODO: FELIX3 - We end up with duplicates in uses constraints,
 //       see scenario 2 for an example.
     private void verifyAndMergeUses(
         Module current, Packages currentPkgs,
@@ -1101,7 +1117,7 @@ dumpModulePkgs(current, currentPkgs);
             {
                 Blame currentExportedBlame = currentPkgs.m_exportedPkgs.get(usedPkgName);
                 Blame currentImportedBlame = currentPkgs.m_importedPkgs.get(usedPkgName);
-// TODO: PROTO3 RESOLVER - What do we do with required packages?
+// TODO: FELIX3 - What do we do with required packages?
                 List<Blame> currentRequiredBlames = currentPkgs.m_requiredPkgs.get(usedPkgName);
 
                 Packages candSourcePkgs = modulePkgMap.get(candSourceCap.getModule());
@@ -1175,10 +1191,16 @@ dumpModulePkgs(current, currentPkgs);
         Map<Requirement, Set<Capability>> candidateMap)
         throws ResolveException
     {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
 // TODO: FELIX3 - I think permutation is not as efficient as it could be, since
-//       the check for subsets is costly. Maybe we can just check if the candidates
-//       for the blamed requirements are a subset, rather than computing the
-//       entire permutation first.
+//       the check for subsets is costly.
 
         // When we detect a conflict, we need to permutate the candidate map
         // we when we try again, we'll select different candidates. To achieve
@@ -1245,6 +1267,14 @@ dumpModulePkgs(current, currentPkgs);
     private static boolean isSubsetPermutation(
         Map<Requirement, Set<Capability>> orig, Map<Requirement, Set<Capability>> copy)
     {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
         for (Entry<Requirement, Set<Capability>> entry : orig.entrySet())
         {
             Set<Capability> copyCands = copy.get(entry.getKey());
@@ -1263,6 +1293,14 @@ dumpModulePkgs(current, currentPkgs);
     private static boolean isCompatible(
         Capability currentCap, Capability candCap, Map<Module, Packages> modulePkgMap)
     {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
         if ((currentCap != null) && (candCap != null))
         {
             List<Capability> currentSources =
@@ -1287,6 +1325,14 @@ dumpModulePkgs(current, currentPkgs);
         Capability cap, Map<Module, Packages> modulePkgMap, List<Capability> sources,
         Set<Capability> cycleMap)
     {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
         if (cap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
         {
             if (cycleMap.contains(cap))
@@ -1295,9 +1341,23 @@ dumpModulePkgs(current, currentPkgs);
             }
             cycleMap.add(cap);
 
-            Packages pkgs = modulePkgMap.get(cap.getModule());
-            sources.add(cap);
+            // Get the package name associated with the capability.
             String pkgName = cap.getAttribute(Capability.PACKAGE_ATTR).getValue().toString();
+
+            // Since a module can export the same package more than once, get
+            // all package capabilities for the specified package name.
+            List<Capability> caps = cap.getModule().getCapabilities();
+            for (int capIdx = 0; capIdx < caps.size(); capIdx++)
+            {
+                if (caps.get(capIdx).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
+                    && caps.get(capIdx).getAttribute(Capability.PACKAGE_ATTR).getValue().equals(pkgName))
+                {
+                    sources.add(caps.get(capIdx));
+                }
+            }
+
+            // Then get any addition sources for the package from required bundles.
+            Packages pkgs = modulePkgMap.get(cap.getModule());
             List<Blame> required = pkgs.m_requiredPkgs.get(pkgName);
             if (required != null)
             {
@@ -1314,6 +1374,14 @@ dumpModulePkgs(current, currentPkgs);
     private static Map<Requirement, Set<Capability>> copyCandidateMap(
         Map<Requirement, Set<Capability>> candidateMap)
     {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
         Map<Requirement, Set<Capability>> copy =
             new HashMap<Requirement, Set<Capability>>();
         for (Entry<Requirement, Set<Capability>> entry : candidateMap.entrySet())
@@ -1498,8 +1566,11 @@ dumpModulePkgs(current, currentPkgs);
 
         public String toString()
         {
-            return m_cap.getModule() + "." + m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue()
-                + " BLAMED ON " + m_reqs;
+            return m_cap.getModule()
+                + "." + m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue()
+                + ((m_reqs.size() == 0)
+                    ? " NO BLAME"
+                    : " BLAMED ON " + m_reqs.get(m_reqs.size() - 1));
         }
 
         public boolean equals(Object o)