Rewrite of the new prototype resolver's core data structure and algorithm.
The code still needs a bit of cleaning, but this captures the nearly fully
working algorithm for safety before tinkering with it. (FELIX-2035,
FELIX-2036, FELIX-2037)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@935394 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 982ce59..590cc56 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -3043,7 +3043,7 @@
List<Directive> dirs = new ArrayList<Directive>(0);
List<Attribute> attrs = new ArrayList<Attribute>(1);
attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
- Requirement req = new RequirementImpl(Capability.PACKAGE_NAMESPACE, dirs, attrs);
+ Requirement req = new RequirementImpl(null, Capability.PACKAGE_NAMESPACE, dirs, attrs);
Set<Capability> exports = m_resolverState.getCandidates(null, req, false);
// We only want resolved capabilities.
@@ -3184,7 +3184,7 @@
List<Attribute> attrs = new ArrayList<Attribute>(1);
attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
Requirement req =
- new RequirementImpl(Capability.PACKAGE_NAMESPACE, dirs, attrs);
+ new RequirementImpl(null, Capability.PACKAGE_NAMESPACE, dirs, attrs);
Set<Capability> exports = m_resolverState.getCandidates(null, req, false);
// We only want resolved capabilities.
for (Iterator<Capability> it = exports.iterator(); it.hasNext(); )
@@ -3356,7 +3356,7 @@
throw new BundleException(
"Unresolved constraint in bundle " + b + ": "
+ ((ex.getRequirement() == null)
- ? ex.getMessage() : ex.getRequirement().toString()));
+ ? ex.getMessage() : ex.getMessage() + " - " + ex.getRequirement().toString()));
}
else
{
diff --git a/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
index e20a1ce..06c446c 100644
--- a/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
@@ -2085,7 +2085,7 @@
List<Attribute> attrs = new ArrayList(1);
attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
Requirement req = new RequirementImpl(
- Capability.PACKAGE_NAMESPACE, dirs, attrs);
+ module, Capability.PACKAGE_NAMESPACE, dirs, attrs);
Set<Capability> exporters = resolver.getCandidates(module, req, false);
Wire wire = null;
@@ -2124,7 +2124,7 @@
List<Attribute> attrs = new ArrayList(1);
attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
Requirement req = new RequirementImpl(
- Capability.PACKAGE_NAMESPACE, dirs, attrs);
+ module, Capability.PACKAGE_NAMESPACE, dirs, attrs);
Set<Capability> exports = resolver.getCandidates(module, req, false);
if (exports.size() > 0)
{
@@ -2244,6 +2244,11 @@
return m_fragment;
}
+ public Module getModule()
+ {
+ return m_req.getModule();
+ }
+
public String getNamespace()
{
return m_req.getNamespace();
diff --git a/framework/src/main/java/org/apache/felix/framework/capabilityset/Requirement.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/Requirement.java
index e6e789d..7934280 100644
--- a/framework/src/main/java/org/apache/felix/framework/capabilityset/Requirement.java
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/Requirement.java
@@ -19,9 +19,11 @@
package org.apache.felix.framework.capabilityset;
import java.util.List;
+import org.apache.felix.framework.resolver.Module;
public interface Requirement
{
+ Module getModule();
String getNamespace();
SimpleFilter getFilter();
boolean isOptional();
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
index 751a96b..43546c5 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
@@ -38,9 +38,6 @@
import org.apache.felix.framework.util.manifestparser.RequirementImpl;
import org.osgi.framework.Constants;
-// 1. Treat hard pkg constraints separately from implied package constraints
-// 2. Map pkg constraints to a set of capabilities, not a single capability.
-// 3. Uses constraints cannot conflict with other uses constraints, only with hard constraints.
public class ResolverImpl implements Resolver
{
private final Logger m_logger;
@@ -60,7 +57,13 @@
m_isInvokeCount = (v == null) ? false : Boolean.valueOf(v);
}
- private final List<Map<Requirement, Set<Capability>>> m_candidatePermutations =
+ // Holds candidate permutations based on permutating "uses" chains.
+ // These permutations are given higher priority.
+ private final List<Map<Requirement, Set<Capability>>> m_usesPermutations =
+ new ArrayList<Map<Requirement, Set<Capability>>>();
+ // Holds candidate permutations based on permutating requirement candidates.
+ // These permutations represent backtracking on previous decisions.
+ private final List<Map<Requirement, Set<Capability>>> m_importPermutations =
new ArrayList<Map<Requirement, Set<Capability>>>();
public Map<Module, List<Wire>> resolve(ResolverState state, Module module)
@@ -81,32 +84,42 @@
if (!module.isResolved())
{
- m_candidatePermutations.clear();
+ m_usesPermutations.clear();
+ m_importPermutations.clear();
//System.out.println("+++ RESOLVING " + module);
Map<Requirement, Set<Capability>> candidateMap =
new HashMap<Requirement, Set<Capability>>();
populateCandidates(state, module, candidateMap, new HashMap<Module, Object>());
- m_candidatePermutations.add(candidateMap);
+ m_usesPermutations.add(candidateMap);
ResolveException rethrow = null;
+ Map<Capability, Set<Requirement>> capDepSet = new HashMap();
+
do
{
rethrow = null;
- candidateMap = m_candidatePermutations.remove(0);
+ modulePkgMap.clear();
+ capDepSet.clear();
+
+ candidateMap = (m_usesPermutations.size() > 0)
+ ? m_usesPermutations.remove(0)
+ : m_importPermutations.remove(0);
//dumpCandidateMap(state, candidateMap);
+ calculatePackageSpaces(
+ module, candidateMap, modulePkgMap, capDepSet, new HashSet());
+//System.out.println("+++ PACKAGE SPACES START +++");
+//dumpModulePkgMap(modulePkgMap);
+//System.out.println("+++ PACKAGE SPACES END +++");
+
try
{
- findConsistentCandidates(
- module,
- new ArrayList(),
- candidateMap,
- modulePkgMap,
- new HashMap<Module, Object>());
+ checkPackageSpaceConsistency(
+ module, candidateMap, modulePkgMap, capDepSet, new HashMap());
}
catch (ResolveException ex)
{
@@ -114,13 +127,13 @@
System.out.println("RE: " + ex);
}
}
- while ((rethrow != null) && (m_candidatePermutations.size() > 0));
+ while ((rethrow != null)
+ && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
if (rethrow != null)
{
throw rethrow;
}
-//dumpModulePkgMap(modulePkgMap);
wireMap =
populateWireMap(module, modulePkgMap, wireMap,
@@ -160,31 +173,37 @@
getDynamicImportCandidates(state, module, pkgName);
if (candidateMap != null)
{
- m_candidatePermutations.clear();
+ m_usesPermutations.clear();
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);
+ m_usesPermutations.add(candidateMap);
ResolveException rethrow = null;
+ Map<Capability, Set<Requirement>> capDepSet = new HashMap();
+
do
{
rethrow = null;
- candidateMap = m_candidatePermutations.remove(0);
-//dumpCandidateMap(state, candidateMap);
+ modulePkgMap.clear();
+ capDepSet.clear();
+
+ candidateMap = (m_usesPermutations.size() > 0)
+ ? m_usesPermutations.remove(0)
+ : m_importPermutations.remove(0);
+
+ calculateDynamicPackageSpaces(
+ module, candidateMap, modulePkgMap, capDepSet);
try
{
- findConsistentDynamicCandidate(
- module,
- new ArrayList(),
- candidateMap,
- modulePkgMap);
+ checkPackageSpaceConsistency(
+ module, candidateMap, modulePkgMap, capDepSet, new HashMap());
}
catch (ResolveException ex)
{
@@ -192,7 +211,8 @@
System.out.println("RE: " + ex);
}
}
- while ((rethrow != null) && (m_candidatePermutations.size() > 0));
+ while ((rethrow != null)
+ && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
if (rethrow != null)
{
@@ -259,7 +279,7 @@
List<Attribute> attrs = new ArrayList(1);
attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
Requirement req = new RequirementImpl(
- Capability.PACKAGE_NAMESPACE, dirs, attrs);
+ module, Capability.PACKAGE_NAMESPACE, dirs, attrs);
Set<Capability> candidates = state.getCandidates(module, req, false);
List<Requirement> dynamics = module.getDynamicRequirements();
@@ -298,7 +318,6 @@
candidates.clear();
}
-
if (candidates.size() > 0)
{
Map<Requirement, Set<Capability>> candidateMap = new HashMap();
@@ -322,7 +341,7 @@
Set<Capability> candidates = candidateMap.get(req);
if ((candidates != null) && (candidates.size() > 0))
{
- System.out.println(" " + req + ": " + candidates);
+ System.out.println(" " + req + ": " + candidates);
}
}
for (Requirement req : module.getDynamicRequirements())
@@ -578,18 +597,19 @@
// Add existing wires as candidates.
for (Wire wire : module.getWires())
{
+// TODO: FELIX3 - HOW ARE CAPABILITIES BEING SORTED NOW?
Set<Capability> cs = new TreeSet();
cs.add(wire.getCapability());
candidateMap.put(wire.getRequirement(), cs);
}
}
-// TODO: FELIX3 - Modify to not be recursive.
- private void findConsistentCandidates(
- Module module, List<Requirement> incomingReqs,
+ private void calculatePackageSpaces(
+ Module module,
Map<Requirement, Set<Capability>> candidateMap,
Map<Module, Packages> modulePkgMap,
- Map<Module, Object> resultCache)
+ Map<Capability, Set<Requirement>> capDepSet,
+ Set<Module> cycle)
{
if (m_isInvokeCount)
{
@@ -598,83 +618,88 @@
count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
m_invokeCounts.put(methodName, count);
}
-
- // Determine if we've already checked candidate consistency for this
- // module. The result cache will have one of two values:
- // 1. Boolean.TRUE if candidate consistency has been checked.
- // 2. An array containing the cycle count and a list of wires for an
- // already resolved (and consistent) module or a list of remaining
- // requirments whose candidates still need to be checked for an
- // unresolved module.
- // For case 1, return immediately. For case 2, this means we are in a
- // cycle so we should just continue checking consistency where we left
- // off.
-
- Integer cycleCount = null;
-
- Object o = resultCache.get(module);
-
- if (o instanceof Boolean)
+ if (cycle.contains(module))
{
return;
}
- else if (o == null)
- {
- List list;
- if (module.isResolved())
- {
- list = new ArrayList(module.getWires());
- }
- else
- {
- list = new ArrayList(module.getRequirements());
- }
- resultCache.put(module, o = new Object[] { cycleCount = new Integer(0), list });
- calculateExportedPackages(module, incomingReqs, modulePkgMap);
- }
- else
- {
- // Increment and get the cycle count.
- cycleCount = (Integer)
- (((Object[]) o)[0]
- = new Integer(((Integer) ((Object[]) o)[0]).intValue() + 1));
- }
-
-//System.out.println("+++ RESOLVING " + module);
+ cycle.add(module);
if (module.isResolved())
{
- List<Wire> wires = (List<Wire>) ((Object[]) o)[1];
+ // First, add all exported packages to our package space.
+ calculateExportedPackages(module, modulePkgMap);
+ Packages modulePkgs = modulePkgMap.get(module);
+ // Second, add all imported packages to our candidate space.
+ List<Wire> wires = new ArrayList(module.getWires());
+ List<Capability> selected = new ArrayList();
while (wires.size() > 0)
{
Wire wire = wires.remove(0);
-
- // Try to resolve the candidate.
- findConsistentCandidates(
- wire.getCapability().getModule(),
- incomingReqs,
- candidateMap,
- modulePkgMap,
- resultCache);
-
- // If we are here, the candidate was consistent. Try to
- // merge the candidate into the target module's packages.
- mergeCandidatePackages(
+ selected.add(wire.getCapability());
+ calculateExportedPackages(wire.getCapability().getModule(), modulePkgMap);
+ mergeCandidatePackagesNoUses(
module,
- incomingReqs,
+ wire.getRequirement(),
wire.getCapability(),
modulePkgMap,
candidateMap);
+ addCapabilityDependency(wire.getCapability(), wire.getRequirement(), capDepSet);
+ }
+
+ // Third, ask our candidates to calculate their package space.
+ while (selected.size() > 0)
+ {
+ Capability candidate = selected.remove(0);
+ calculatePackageSpaces(candidate.getModule(), candidateMap, modulePkgMap, capDepSet, cycle);
+ }
+
+ // Fourth, add all of the uses constraints implied by our imported
+ // and required packages.
+ for (Entry<String, Blame> entry : modulePkgs.m_importedPkgs.entrySet())
+ {
+ List<Requirement> blameReqs = new ArrayList();
+ blameReqs.add(entry.getValue().m_reqs.get(0));
+
+ mergeUses(
+ module,
+ modulePkgs,
+ entry.getValue().m_cap,
+ blameReqs,
+ modulePkgMap,
+ candidateMap,
+ new HashMap<String, List<Module>>());
+ }
+ for (Entry<String, List<Blame>> entry : modulePkgs.m_requiredPkgs.entrySet())
+ {
+ for (Blame blame : entry.getValue())
+ {
+ List<Requirement> blameReqs = new ArrayList();
+ blameReqs.add(blame.m_reqs.get(0));
+
+ mergeUses(
+ module,
+ modulePkgs,
+ blame.m_cap,
+ blameReqs,
+ modulePkgMap,
+ candidateMap,
+ new HashMap<String, List<Module>>());
+ }
}
}
else
{
- List<Requirement> reqs = (List<Requirement>) ((Object[]) o)[1];
+ // First, add all exported packages to our package space.
+ calculateExportedPackages(module, modulePkgMap);
+ Packages modulePkgs = modulePkgMap.get(module);
- while (reqs.size() > 0)
+ // Second, add all imported packages to our candidate space.
+ List<Requirement> list = new ArrayList(module.getRequirements());
+ List<Capability> selected = new ArrayList();
+ while (list.size() > 0)
{
- Requirement req = reqs.remove(0);
+ Requirement req = list.remove(0);
// Get the candidates for the current requirement.
Set<Capability> candCaps = candidateMap.get(req);
@@ -684,68 +709,208 @@
continue;
}
- List<Requirement> outgoingReqs = new ArrayList<Requirement>(incomingReqs);
- outgoingReqs.add(req);
+ calculateExportedPackages(module, modulePkgMap);
+ Capability candidate = candCaps.iterator().next();
+ selected.add(candidate);
+ calculateExportedPackages(candidate.getModule(), modulePkgMap);
+ mergeCandidatePackagesNoUses(module, req, candidate, modulePkgMap, candidateMap);
+ addCapabilityDependency(candidate, req, capDepSet);
+ }
- for (Iterator<Capability> it = candCaps.iterator(); it.hasNext(); )
+ // Third, ask our candidates to calculate their package space.
+ while (selected.size() > 0)
+ {
+ Capability candidate = selected.remove(0);
+ calculatePackageSpaces(candidate.getModule(), candidateMap, modulePkgMap, capDepSet, cycle);
+ }
+
+ // Fourth, add all of the uses constraints implied by our imported
+ // and required packages.
+// TODO: FELIX3 - DUPLICATES CODE ABOVE FOR RESOLVED MODULES.
+ for (Entry<String, Blame> entry : modulePkgs.m_importedPkgs.entrySet())
+ {
+ List<Requirement> blameReqs = new ArrayList();
+ blameReqs.add(entry.getValue().m_reqs.get(0));
+
+ mergeUses(
+ module,
+ modulePkgs,
+ entry.getValue().m_cap,
+ blameReqs,
+ modulePkgMap,
+ candidateMap,
+ new HashMap<String, List<Module>>());
+ }
+ for (Entry<String, List<Blame>> entry : modulePkgs.m_requiredPkgs.entrySet())
+ {
+ for (Blame blame : entry.getValue())
{
- Capability candCap = it.next();
-//System.out.println("+++ TRYING CAND " + candCap + " FOR " + req);
- try
- {
- // Try to resolve the candidate.
- findConsistentCandidates(
- candCap.getModule(),
- outgoingReqs,
- candidateMap,
- modulePkgMap,
- resultCache);
+ List<Requirement> blameReqs = new ArrayList();
+ blameReqs.add(blame.m_reqs.get(0));
- // If we are here, the candidate was consistent. Try to
- // merge the candidate into the target module's packages.
- mergeCandidatePackages(
- module,
- outgoingReqs,
- candCap,
+ mergeUses(
+ module,
+ modulePkgs,
+ blame.m_cap,
+ blameReqs,
+ modulePkgMap,
+ candidateMap,
+ new HashMap<String, List<Module>>());
+ }
+ }
+ }
+ }
+
+// TODO: FELIX3 - This code duplicates a lot of calculatePackageSpaces()
+ private void calculateDynamicPackageSpaces(
+ Module module,
+ Map<Requirement, Set<Capability>> candidateMap,
+ Map<Module, Packages> modulePkgMap,
+ Map<Capability, Set<Requirement>> capDepSet)
+ {
+ 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);
+ }
+
+ // First, add all exported packages to our package space.
+ calculateExportedPackages(module, modulePkgMap);
+ Packages modulePkgs = modulePkgMap.get(module);
+
+ // Second, add all imported packages to our candidate space.
+ List<Requirement> reqs = new ArrayList(module.getRequirements());
+ reqs.addAll(module.getDynamicRequirements());
+ List<Capability> selected = new ArrayList();
+ while (reqs.size() > 0)
+ {
+ Requirement req = reqs.remove(0);
+
+ // Get the candidates for the current requirement.
+ Set<Capability> candCaps = candidateMap.get(req);
+ // Optional requirements may not have any candidates.
+ if (candCaps == null)
+ {
+ continue;
+ }
+
+ calculateExportedPackages(module, modulePkgMap);
+ Capability candidate = candCaps.iterator().next();
+ selected.add(candidate);
+ calculateExportedPackages(candidate.getModule(), modulePkgMap);
+ mergeCandidatePackagesNoUses(module, req, candidate, modulePkgMap, candidateMap);
+ addCapabilityDependency(candidate, req, capDepSet);
+ }
+
+ // Third, ask our candidates to calculate their package space.
+ while (selected.size() > 0)
+ {
+ Capability candidate = selected.remove(0);
+ calculatePackageSpaces(
+ candidate.getModule(), candidateMap,
+ modulePkgMap, capDepSet, new HashSet());
+ }
+
+ // Fourth, add all of the uses constraints implied by our imported
+ // and required packages.
+ for (Entry<String, Blame> entry : modulePkgs.m_importedPkgs.entrySet())
+ {
+ List<Requirement> blameReqs = new ArrayList();
+ blameReqs.add(entry.getValue().m_reqs.get(0));
+
+ mergeUses(
+ module,
+ modulePkgs,
+ entry.getValue().m_cap,
+ blameReqs,
+ modulePkgMap,
+ candidateMap,
+ new HashMap<String, List<Module>>());
+ }
+ for (Entry<String, List<Blame>> entry : modulePkgs.m_requiredPkgs.entrySet())
+ {
+ for (Blame blame : entry.getValue())
+ {
+ List<Requirement> blameReqs = new ArrayList();
+ blameReqs.add(blame.m_reqs.get(0));
+
+ mergeUses(
+ module,
+ modulePkgs,
+ blame.m_cap,
+ blameReqs,
+ modulePkgMap,
+ candidateMap,
+ new HashMap<String, List<Module>>());
+ }
+ }
+ }
+
+ private void mergeCandidatePackagesNoUses(
+ Module current, Requirement currentReq, Capability candCap,
+ 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);
+ }
+
+ if (candCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ mergeCandidatePackageNoUses(
+ current, false, currentReq, candCap, modulePkgMap);
+ }
+ else if (candCap.getNamespace().equals(Capability.MODULE_NAMESPACE))
+ {
+// TODO: FELIX3 - THIS NEXT LINE IS A HACK. IMPROVE HOW/WHEN WE CALCULATE EXPORTS.
+ calculateExportedPackages(candCap.getModule(), modulePkgMap);
+
+ // Get the candidate's package space to determine which packages
+ // will be visible to the current module.
+ Packages candPkgs = modulePkgMap.get(candCap.getModule());
+
+ // 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())
+ {
+ mergeCandidatePackageNoUses(
+ current,
+ true,
+ currentReq,
+ entry.getValue().m_cap,
+ modulePkgMap);
+ }
+
+ // 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))
+ {
+ mergeCandidatePackagesNoUses(
+ current,
+ currentReq,
+ candidateMap.get(req).iterator().next(),
modulePkgMap,
candidateMap);
-
- // If we are here, we merged the candidate successfully,
- // so we can continue with the next requirement
- break;
- }
- catch (ResolveException ex)
- {
-//System.out.println("RE: " + ex);
-//ex.printStackTrace();
- // Remove the current candidate since it failed to resolve.
- it.remove();
- if (!it.hasNext() && !req.isOptional())
- {
- throw new ResolveException("Unresolved constraint "
- + req + " in " + module, module, req);
- }
}
}
}
}
-
- // If we are exiting from a cycle then decrement
- // cycle counter, otherwise record the result.
- if (cycleCount.intValue() > 0)
- {
- ((Object[]) o)[0] = new Integer(cycleCount.intValue() - 1);
- }
- else if (cycleCount.intValue() == 0)
- {
- // Record that the module was successfully populated.
- resultCache.put(module, Boolean.TRUE);
- }
}
- private void findConsistentDynamicCandidate(
- Module module, List<Requirement> incomingReqs,
- Map<Requirement, Set<Capability>> candidateMap,
+ private void mergeCandidatePackageNoUses(
+ Module current, boolean requires,
+ Requirement currentReq, Capability candCap,
Map<Module, Packages> modulePkgMap)
{
if (m_isInvokeCount)
@@ -756,69 +921,68 @@
m_invokeCounts.put(methodName, count);
}
-//System.out.println("+++ RESOLVING " + module);
- calculateExportedPackages(module, incomingReqs, modulePkgMap);
-
- List<Requirement> reqs = new ArrayList(module.getRequirements());
- reqs.addAll(module.getDynamicRequirements());
- for (Requirement req : reqs)
+// TODO: FELIX3 - Check for merging where module imports from itself,
+// then it should be listed as an export for requiring bundles.
+ if (candCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
{
- // Get the candidates for the current requirement.
- Set<Capability> candCaps = candidateMap.get(req);
- // Optional requirements may not have any candidates.
- if (candCaps == null)
+//System.out.println("+++ MERGING " + candBlame.m_cap + " INTO " + current);
+ String pkgName = (String)
+ candCap.getAttribute(Capability.PACKAGE_ATTR).getValue();
+
+ // Since this capability represents a package, it will become
+ // a hard constraint on the module's package space, so we need
+ // to make sure it doesn't conflict with any other hard constraints
+ // or any other uses constraints.
+
+ List blameReqs = new ArrayList();
+ blameReqs.add(currentReq);
+
+ //
+ // First, check to see if the capability conflicts with
+ // any existing hard constraints.
+ //
+
+ Packages currentPkgs = modulePkgMap.get(current);
+ List<Blame> currentRequiredBlames = currentPkgs.m_requiredPkgs.get(pkgName);
+
+ if (requires)
{
- continue;
+ if (currentRequiredBlames == null)
+ {
+ currentRequiredBlames = new ArrayList<Blame>();
+ currentPkgs.m_requiredPkgs.put(pkgName, currentRequiredBlames);
+ }
+ currentRequiredBlames.add(new Blame(candCap, blameReqs));
+ }
+ else
+ {
+// TODO: FELIX3 - We might need to make this a list, since fragments can add duplicates.
+ currentPkgs.m_importedPkgs.put(pkgName, new Blame(candCap, blameReqs));
}
- List<Requirement> outgoingReqs = new ArrayList<Requirement>(incomingReqs);
- outgoingReqs.add(req);
-
- for (Iterator<Capability> it = candCaps.iterator(); it.hasNext(); )
- {
- Capability candCap = it.next();
-//System.out.println("+++ TRYING CAND " + candCap + " FOR " + req);
- try
- {
- // Try to resolve the candidate.
- findConsistentCandidates(
- candCap.getModule(),
- outgoingReqs,
- candidateMap,
- modulePkgMap,
- new HashMap());
-
- // If we are here, the candidate was consistent. Try to
- // merge the candidate into the target module's packages.
- mergeCandidatePackages(
- module,
- outgoingReqs,
- candCap,
- modulePkgMap,
- candidateMap);
-
- // If we are here, we merged the candidate successfully,
- // so we can continue with the next requirement
- break;
- }
- catch (ResolveException ex)
- {
-System.out.println("RE: " + ex);
-ex.printStackTrace();
- // Remove the current candidate since it failed to resolve.
- it.remove();
- if (!it.hasNext() && !req.isOptional())
- {
- throw new ResolveException("Unresolved constraint "
- + req + " in " + module, module, req);
- }
- }
- }
+//dumpModulePkgs(current, currentPkgs);
}
}
- private static void calculateExportedPackages(
- Module module, List<Requirement> incomingReqs, Map<Module, Packages> modulePkgMap)
+ private static void addCapabilityDependency(
+ Capability cap, Requirement req, Map<Capability, Set<Requirement>> capDepSet)
+ {
+ Set<Requirement> reqs = capDepSet.get(cap);
+ if (reqs == null)
+ {
+ reqs = new HashSet();
+ capDepSet.put(cap, reqs);
+ }
+ reqs.add(req);
+ }
+
+// TODO: FELIX3 - We end up with duplicates in uses constraints,
+// see scenario 2 for an example.
+ private static void mergeUses(
+ Module current, Packages currentPkgs,
+ Capability mergeCap, List<Requirement> blameReqs, Map<Module, Packages> modulePkgMap,
+ Map<Requirement, Set<Capability>> candidateMap,
+ Map<String, List<Module>> cycleMap)
{
if (m_isInvokeCount)
{
@@ -828,7 +992,328 @@
m_invokeCounts.put(methodName, count);
}
- Packages packages = new Packages();
+ if (!mergeCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ return;
+ }
+ // If the candidate module is the same as the current module,
+ // then we don't need to verify and merge the uses constraints
+ // since this will happen as we build up the package space.
+ else if (current == mergeCap.getModule())
+ {
+ return;
+ }
+
+ // Check for cycles.
+ String pkgName = (String)
+ mergeCap.getAttribute(Capability.PACKAGE_ATTR).getValue();
+ List<Module> list = cycleMap.get(pkgName);
+ if ((list != null) && list.contains(current))
+ {
+ return;
+ }
+ list = (list == null) ? new ArrayList<Module>() : list;
+ list.add(current);
+ cycleMap.put(pkgName, list);
+
+//System.out.println("+++ MERGING USES " + current + " FOR " + candBlame);
+ for (Capability candSourceCap : getPackageSources(
+ mergeCap, modulePkgMap, new ArrayList<Capability>(), new HashSet<Capability>()))
+ {
+ for (String usedPkgName : candSourceCap.getUses())
+ {
+ Packages candSourcePkgs = modulePkgMap.get(candSourceCap.getModule());
+ Blame candSourceBlame = candSourcePkgs.m_exportedPkgs.get(usedPkgName);
+ candSourceBlame = (candSourceBlame != null)
+ ? candSourceBlame
+ : candSourcePkgs.m_importedPkgs.get(usedPkgName);
+
+ if (candSourceBlame == null)
+ {
+ continue;
+ }
+
+ List<Blame> usedCaps = currentPkgs.m_usedPkgs.get(usedPkgName);
+ if (usedCaps == null)
+ {
+ usedCaps = new ArrayList<Blame>();
+ currentPkgs.m_usedPkgs.put(usedPkgName, usedCaps);
+ }
+ if (candSourceBlame.m_reqs != null)
+ {
+ List<Requirement> blameReqs2 = new ArrayList(blameReqs);
+ blameReqs2.add(candSourceBlame.m_reqs.get(candSourceBlame.m_reqs.size() - 1));
+ usedCaps.add(new Blame(candSourceBlame.m_cap, blameReqs2));
+ mergeUses(current, currentPkgs, candSourceBlame.m_cap, blameReqs2,
+ modulePkgMap, candidateMap, cycleMap);
+ }
+ else
+ {
+ usedCaps.add(new Blame(candSourceBlame.m_cap, blameReqs));
+ mergeUses(current, currentPkgs, candSourceBlame.m_cap, blameReqs,
+ modulePkgMap, candidateMap, cycleMap);
+ }
+ }
+ }
+ }
+
+ private void checkPackageSpaceConsistency(
+ Module module, Map<Requirement, Set<Capability>> candidateMap,
+ Map<Module, Packages> modulePkgMap, Map<Capability, Set<Requirement>> capDepSet,
+ Map<Module, Object> resultCache)
+ {
+ 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 (module.isResolved())
+ {
+ return;
+ }
+ else if (resultCache.containsKey(module))
+ {
+ return;
+ }
+
+//System.out.println("+++ checkPackageSpaceConsistency(" + module + ")");
+ Packages pkgs = modulePkgMap.get(module);
+
+ ResolveException rethrow = null;
+ Map<Requirement, Set<Capability>> copyConflict = null;
+ Set<Requirement> mutated = null;
+
+ Set<Module> checkModules = new HashSet();
+
+ for (Entry<String, Blame> entry : pkgs.m_exportedPkgs.entrySet())
+ {
+ String pkgName = entry.getKey();
+ if (!pkgs.m_usedPkgs.containsKey(pkgName))
+ {
+ continue;
+ }
+ for (Blame blame : pkgs.m_usedPkgs.get(pkgName))
+ {
+ if (!isCompatible(entry.getValue().m_cap, blame.m_cap, modulePkgMap))
+ {
+ copyConflict = (copyConflict != null)
+ ? copyConflict
+ : copyCandidateMap(candidateMap);
+ rethrow = (rethrow != null)
+ ? rethrow
+ : new ResolveException(
+ "3Constraint violation for package '"
+ + pkgName + "' when resolving module "
+ + module + " between existing exported constraint "
+ + entry.getValue() + " and uses constraint "
+ + blame, null, null);
+ mutated = (mutated != null)
+ ? mutated
+ : new HashSet();
+// TODO: FELIX3 - I think we need to walk up this chain too.
+// TODO: FELIX3 - What about uses and import permutations?
+ Requirement req = blame.m_reqs.get(blame.m_reqs.size() - 1);
+ if (!mutated.contains(req))
+ {
+ mutated.add(req);
+ Set<Capability> caps = copyConflict.get(req);
+ Iterator it = caps.iterator();
+ it.next();
+ it.remove();
+ if (caps.size() == 0)
+ {
+ removeInvalidateCandidate(req.getModule(), capDepSet, copyConflict);
+ }
+ }
+ }
+ }
+ if (rethrow != null)
+ {
+ m_usesPermutations.add(copyConflict);
+ throw rethrow;
+ }
+ }
+
+ // Check if there are any conflicts with imported packages.
+ for (Entry<String, Blame> entry : pkgs.m_importedPkgs.entrySet())
+ {
+ if (!module.equals(entry.getValue().m_cap.getModule()))
+ {
+ checkModules.add(entry.getValue().m_cap.getModule());
+ }
+
+ String pkgName = entry.getKey();
+ if (!pkgs.m_usedPkgs.containsKey(pkgName))
+ {
+ continue;
+ }
+ for (Blame blame : pkgs.m_usedPkgs.get(pkgName))
+ {
+ if (!isCompatible(entry.getValue().m_cap, blame.m_cap, modulePkgMap))
+ {
+ // Create a candidate permutation that eliminates any candidates
+ // that conflict with existing selected candidates.
+ copyConflict = (copyConflict != null)
+ ? copyConflict
+ : copyCandidateMap(candidateMap);
+ rethrow = (rethrow != null)
+ ? rethrow
+ : new ResolveException(
+ "4Constraint violation for package '"
+ + pkgName + "' when resolving module "
+ + module + " between existing imported constraint "
+ + entry.getValue() + " and uses constraint "
+ + blame, null, null);
+
+ mutated = (mutated != null)
+ ? mutated
+ : new HashSet();
+
+ for (int reqIdx = blame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
+ {
+ Requirement req = blame.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))
+ {
+ break;
+ }
+
+ // See if we can permutate the candidates for blamed
+ // requirement; there may be no candidates if the module
+ // associated with the requirement is already resolved.
+ Set<Capability> candidates = copyConflict.get(req);
+ if ((candidates != null) && (candidates.size() > 1))
+ {
+ mutated.add(req);
+ Iterator it = candidates.iterator();
+ it.next();
+ it.remove();
+ // Continue with the next uses constraint.
+ break;
+ }
+ }
+ }
+ }
+
+ if (rethrow != null)
+ {
+ // If we couldn't permutate the uses constraints,
+ // then try to permutate the import.
+// TODO: FELIX3 - Maybe we will push too many permutations this way, since
+// we will push one on each level as we move down.
+ Requirement req = entry.getValue().m_reqs.get(0);
+ if (!mutated.contains(req))
+ {
+ Set<Capability> candidates = candidateMap.get(req);
+ if (candidates.size() > 1)
+ {
+ Map<Requirement, Set<Capability>> importPerm =
+ copyCandidateMap(candidateMap);
+ candidates = importPerm.get(req);
+ Iterator it = candidates.iterator();
+ it.next();
+ it.remove();
+System.out.println("+++ ADDING IMPORT PERM " + req);
+ m_importPermutations.add(importPerm);
+ }
+ }
+
+ if (mutated.size() > 0)
+ {
+System.out.println("+++ ADDING CONFLICT PERM " + mutated);
+ m_usesPermutations.add(copyConflict);
+ }
+
+ throw rethrow;
+ }
+ }
+
+ resultCache.put(module, Boolean.TRUE);
+
+ // Now check the consistency of all modules on which the
+ // current module depends.
+ for (Module m : checkModules)
+ {
+ checkPackageSpaceConsistency(m, candidateMap, modulePkgMap, capDepSet, resultCache);
+ }
+ }
+
+ private void removeInvalidateCandidate(
+ Module invalid, Map<Capability, Set<Requirement>> capDepSet,
+ Map<Requirement, Set<Capability>> candidateMap)
+ {
+System.out.println("+++ REMOVING INVALID CANDIDATE: " + invalid + ":" + invalid.getSymbolicName());
+ Set<Module> invalidated = new HashSet();
+
+ for (Requirement req : invalid.getRequirements())
+ {
+ candidateMap.remove(req);
+ }
+
+ boolean wasRequired = false;
+
+ for (Capability cap : invalid.getCapabilities())
+ {
+ Set<Requirement> reqs = capDepSet.remove(cap);
+ if (reqs == null)
+ {
+ continue;
+ }
+ wasRequired = true;
+ for (Requirement req : reqs)
+ {
+ Set<Capability> candidates = candidateMap.get(req);
+ if (candidates != null)
+ {
+ candidates.remove(cap);
+ if (candidates.size() == 0)
+ {
+ candidateMap.remove(req);
+ invalidated.add(req.getModule());
+ }
+ }
+ else
+ {
+ System.out.println("+++ INVALIDATED REQ WITH NULL CAPS: " + req);
+ }
+ }
+ }
+
+ if (!wasRequired)
+ {
+ throw new ResolveException(
+ "Unable to resolve module", invalid, null);
+ }
+
+ for (Module m : invalidated)
+ {
+ removeInvalidateCandidate(m, capDepSet, candidateMap);
+ }
+ }
+
+ private static void calculateExportedPackages(
+ Module module, 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);
+ }
+
+ Packages packages = modulePkgMap.get(module);
+ if (packages != null)
+ {
+ return;
+ }
+ packages = new Packages();
List<Capability> caps = module.getCapabilities();
@@ -843,7 +1328,7 @@
{
packages.m_exportedPkgs.put(
(String) caps.get(i).getAttribute(Capability.PACKAGE_ATTR).getValue(),
- new Blame(incomingReqs, caps.get(i)));
+ new Blame(caps.get(i), null));
}
}
}
@@ -873,420 +1358,6 @@
return false;
}
- private void mergeCandidatePackages(
- Module current, List<Requirement> outgoingReqs,
- Capability candCap, 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);
- }
-
- if (candCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
- {
- mergeCandidatePackage(
- current, false, new Blame(outgoingReqs, candCap), modulePkgMap, candidateMap);
- }
- else if (candCap.getNamespace().equals(Capability.MODULE_NAMESPACE))
- {
- // Get the candidate's package space to determine which packages
- // will be visible to the current module.
- Packages candPkgs = modulePkgMap.get(candCap.getModule());
-
- // 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(
- current,
- true,
- new Blame(outgoingReqs, entry.getValue().m_cap),
- modulePkgMap,
- candidateMap);
- }
-
- // 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))
- {
- mergeCandidatePackages(
- current,
- outgoingReqs,
- candidateMap.get(req).iterator().next(),
- modulePkgMap,
- candidateMap);
- }
- }
- }
- }
- }
-
- private void mergeCandidatePackage(
- Module current, boolean requires,
- 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);
- }
-
-// 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))
- {
-//System.out.println("+++ MERGING " + candBlame.m_cap + " INTO " + current);
- String pkgName = (String)
- candBlame.m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue();
-
- // Since this capability represents a package, it will become
- // a hard constraint on the module's package space, so we need
- // to make sure it doesn't conflict with any other hard constraints
- // or any other uses constraints.
-
- //
- // First, check to see if the capability conflicts with
- // any existing hard constraints.
- //
-
- Packages currentPkgs = modulePkgMap.get(current);
- Blame currentExportedBlame = currentPkgs.m_exportedPkgs.get(pkgName);
- Blame currentImportedBlame = currentPkgs.m_importedPkgs.get(pkgName);
- List<Blame> currentRequiredBlames = currentPkgs.m_requiredPkgs.get(pkgName);
-
- // We don't need to worry about an import conflicting with a required
- // bundle's export, since imported package wires are terminal the
- // bundle will never see the exported package from the required bundle.
-// TODO: FELIX3 - See scenario 21, this seems odd.
- if (!requires &&
- (currentImportedBlame != null) && !currentImportedBlame.m_cap.equals(candBlame.m_cap))
-// if (!requires &&
-// (((currentExportedBlame != null) && !currentExportedBlame.m_cap.equals(candBlame.m_cap))
-// || ((currentImportedBlame != null) && !currentImportedBlame.m_cap.equals(candBlame.m_cap))))
-// || ((currentRequiredBlames != null) && !currentRequiredBlames.contains(candBlame))))
- {
- // Permutate the candidate map and throw a resolve exception.
- // NOTE: This method ALWAYS throws an exception.
- permutateCandidates(
- current,
- pkgName,
- currentImportedBlame,
- candBlame,
- candidateMap);
- }
-
- //
- // Second, check to see if the capability conflicts with
- // any existing uses constraints
- //
-
- Packages currentPkgsCopy = currentPkgs;
-
- if (!current.isResolved())
- {
- List<Blame> currentUsedBlames = currentPkgs.m_usedPkgs.get(pkgName);
- checkExistingUsesConstraints(
- current, pkgName, currentUsedBlames, candBlame, modulePkgMap, candidateMap);
-
- //
- // Last, check to see if any uses constraints implied by the
- // candidate conflict with any of the existing hard constraints.
- //
-
- // For now, create a copy of the module's package space and
- // add the current candidate to the imported packages.
- currentPkgsCopy = new Packages(currentPkgs);
- }
-
- if (requires)
- {
- if (currentRequiredBlames == null)
- {
- currentRequiredBlames = new ArrayList<Blame>();
- currentPkgsCopy.m_requiredPkgs.put(pkgName, currentRequiredBlames);
- }
-// TODO: FELIX3 - This is potentially modifying the original, we need to modify a copy.
- currentRequiredBlames.add(candBlame);
- }
- else
- {
- currentPkgsCopy.m_importedPkgs.put(pkgName, candBlame);
- }
-
- // Verify and merge the candidate's transitive uses constraints.
- verifyAndMergeUses(
- current,
- currentPkgsCopy,
- candBlame,
- modulePkgMap,
- candidateMap,
- new HashMap<String, List<Module>>());
-
- // If we are here, then there were no conflict, so we should update
- // the module's package space.
- if (!current.isResolved())
- {
- currentPkgs.m_exportedPkgs.putAll(currentPkgsCopy.m_exportedPkgs);
- currentPkgs.m_importedPkgs.putAll(currentPkgsCopy.m_importedPkgs);
- currentPkgs.m_requiredPkgs.putAll(currentPkgsCopy.m_requiredPkgs);
- currentPkgs.m_usedPkgs.putAll(currentPkgsCopy.m_usedPkgs);
- }
-//dumpModulePkgs(current, currentPkgs);
- }
- }
-
- private void checkExistingUsesConstraints(
- Module current, String pkgName, List<Blame> currentUsedBlames,
- 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));
- if (!isCompatible(currentUsedBlames.get(i).m_cap, candBlame.m_cap, modulePkgMap))
- {
- // Permutate the candidate map and throw a resolve exception.
- // NOTE: This method ALWAYS throws an exception.
- permutateCandidates(
- current,
- pkgName,
- currentUsedBlames.get(i),
- candBlame,
- candidateMap);
- }
- }
- }
-
-// TODO: FELIX3 - We end up with duplicates in uses constraints,
-// see scenario 2 for an example.
- private void verifyAndMergeUses(
- Module current, Packages currentPkgs,
- Blame candBlame, Map<Module, Packages> modulePkgMap,
- Map<Requirement, Set<Capability>> candidateMap,
- Map<String, List<Module>> 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);
- }
-
- // Check for cycles.
- String pkgName = (String)
- candBlame.m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue();
- List<Module> list = cycleMap.get(pkgName);
- if ((list != null) && list.contains(current))
- {
- return;
- }
- list = (list == null) ? new ArrayList<Module>() : list;
- list.add(current);
- cycleMap.put(pkgName, list);
-
-//System.out.println("+++ VERIFYING USES " + current + " FOR " + candBlame);
- for (Capability candSourceCap : getPackageSources(
- candBlame.m_cap, modulePkgMap, new ArrayList<Capability>(), new HashSet<Capability>()))
- {
- for (String usedPkgName : candSourceCap.getUses())
- {
- Blame currentExportedBlame = currentPkgs.m_exportedPkgs.get(usedPkgName);
- Blame currentImportedBlame = currentPkgs.m_importedPkgs.get(usedPkgName);
-// TODO: FELIX3 - What do we do with required packages?
- List<Blame> currentRequiredBlames = currentPkgs.m_requiredPkgs.get(usedPkgName);
-
- Packages candSourcePkgs = modulePkgMap.get(candSourceCap.getModule());
-//System.out.println("+++ candSourceCap " + candSourceCap);
-//System.out.println("+++ candSourceCap.getModule() " + candSourceCap.getModule() + " (" + candSourceCap.getModule().isResolved() + ")");
-//System.out.println("+++ candSourcePkgs " + candSourcePkgs);
-//System.out.println("+++ candSourcePkgs.m_exportedPkgs " + candSourcePkgs.m_exportedPkgs);
- Blame candSourceBlame = candSourcePkgs.m_exportedPkgs.get(usedPkgName);
- candSourceBlame = (candSourceBlame != null)
- ? candSourceBlame
- : candSourcePkgs.m_importedPkgs.get(usedPkgName);
-// sourceCap = (sourceCap != null)
-// ? sourceCap
-// : sourcePkgs.m_requiredPkgs.get(usedPkgName);
-
- // If the candidate doesn't actually have a constraint for
- // the used package, then just ignore it since this is likely
- // an error in its metadata.
- if (candSourceBlame == null)
- {
- return;
- }
-
- // If there is no current mapping for this package, then
- // we can just return.
- if ((currentExportedBlame == null)
- && (currentImportedBlame == null)
- && (currentRequiredBlames == null))
- {
- List<Blame> usedCaps = currentPkgs.m_usedPkgs.get(usedPkgName);
- if (usedCaps == null)
- {
- usedCaps = new ArrayList<Blame>();
- currentPkgs.m_usedPkgs.put(usedPkgName, usedCaps);
- }
-//System.out.println("+++ MERGING CB " + candBlame + " SB " + candSourceBlame);
-// usedCaps.add(new Blame(candBlame.m_reqs, sourceBlame.m_cap));
- usedCaps.add(candSourceBlame);
-// return;
- }
- else if (!current.isResolved())
- {
- if ((currentExportedBlame != null)
- && !isCompatible(currentExportedBlame.m_cap, candSourceBlame.m_cap, modulePkgMap))
- {
- throw new ResolveException(
- "Constraint violation for package '" + usedPkgName
- + "' when resolving module " + current
- + " between existing constraint "
- + currentExportedBlame
- + " and candidate constraint "
- + candSourceBlame, null, null);
- }
- else if ((currentImportedBlame != null)
- && !isCompatible(currentImportedBlame.m_cap, candSourceBlame.m_cap, modulePkgMap))
- {
- permutateCandidates(
- current, usedPkgName, currentImportedBlame,
- candSourceBlame, candidateMap);
- }
- }
-
- verifyAndMergeUses(current, currentPkgs, candSourceBlame,
- modulePkgMap, candidateMap, cycleMap);
- }
- }
- }
-
- private void permutateCandidates(
- Module current, String pkgName, Blame currentBlame, Blame candBlame,
- 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.
-
- // When we detect a conflict, we need to permutate the candidate map
- // we when we try again, we'll select different candidates. To achieve
- // this, we create a different permutation for each requirement in
- // the set of blames requirements by eliminating the first candidate
- // (i.e., the selected candidate) for each. We need to create an separate
- // permutation for each blamed requirement to ensure we try all possible
- // combination. This is a form of back tracking, since we eliminate each
- // requirement's selected candidate, which will force them to choose
- // another in the new permutation.
- //
- // The blamed requirement may be null if the bundle itself is exports
- // the package imposing the uses constraint.
- if ((currentBlame.m_reqs != null) && (currentBlame.m_reqs.size() != 0))
- {
- // Permutate the candidate map for each blamed requirement.
- for (int reqIdx = 0; reqIdx < currentBlame.m_reqs.size(); reqIdx++)
- {
- // Verify whether we have more than one candidate to create
- // a permutation.
- Set<Capability> candidates = candidateMap.get(currentBlame.m_reqs.get(reqIdx));
- if (candidates.size() > 1)
- {
- // Create a copy of the current candidate map and then remove
- // the first (i.e., selected) candidate for the current
- // blamed requirement.
- Map<Requirement, Set<Capability>> copy = copyCandidateMap(candidateMap);
- Set<Capability> candCopy = copy.get(currentBlame.m_reqs.get(reqIdx));
- Iterator it = candCopy.iterator();
- it.next();
- it.remove();
-
- // Check if the created permutation is a subset of a previously
- // created permutation. If so, then we don't need to record it
- // since it will be generated again when the superset permutation
- // is processed.
- boolean isSubset = false;
- for (int permIdx = 0; !isSubset && (permIdx < m_candidatePermutations.size()); permIdx++)
- {
- if (isSubsetPermutation(m_candidatePermutations.get(permIdx), copy))
- {
- isSubset = true;
- }
- }
- if (!isSubset)
- {
-//System.out.println("+++ SETTING "
-// + currentBlame.m_reqs.get(reqIdx)
-// + " CANDS FROM " + candidates + " TO " + candCopy);
- m_candidatePermutations.add(0, copy);
- }
- }
- }
- }
-
- throw new ResolveException(
- "Constraint violation for package '"
- + pkgName + "' when resolving module "
- + current + " between existing constraint "
- + currentBlame + " and candidate constraint "
- + candBlame, null, null);
- }
-
- 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());
- if (copyCands == null)
- {
- return false;
- }
- if (!entry.getValue().containsAll(copyCands))
- {
- return false;
- }
- }
- return true;
- }
-
private static boolean isCompatible(
Capability currentCap, Capability candCap, Map<Module, Packages> modulePkgMap)
{
@@ -1300,6 +1371,11 @@
if ((currentCap != null) && (candCap != null))
{
+ if (currentCap.equals(candCap))
+ {
+ return true;
+ }
+
List<Capability> currentSources =
getPackageSources(
currentCap,
@@ -1489,7 +1565,7 @@
// We need an unique requirement here or else subsequent
// dynamic imports for the same dynamic requirement will
// conflict with previous ones.
- new RequirementImpl(Capability.PACKAGE_NAMESPACE, new ArrayList(0), attrs),
+ new RequirementImpl(module, Capability.PACKAGE_NAMESPACE, new ArrayList(0), attrs),
entry.getValue().m_cap.getModule(),
entry.getValue().m_cap));
}
@@ -1552,22 +1628,22 @@
private static class Blame
{
- public final List<Requirement> m_reqs;
public final Capability m_cap;
+ public final List<Requirement> m_reqs;
- public Blame(List<Requirement> reqs, Capability cap)
+ public Blame(Capability cap, List<Requirement> reqs)
{
- m_reqs = reqs;
m_cap = cap;
+ m_reqs = reqs;
}
public String toString()
{
return m_cap.getModule()
+ "." + m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue()
- + ((m_reqs.size() == 0)
+ + (((m_reqs == null) || (m_reqs.size() == 0))
? " NO BLAME"
- : " BLAMED ON " + m_reqs.get(m_reqs.size() - 1));
+ : " BLAMED ON " + m_reqs);
}
public boolean equals(Object o)
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index dcffea3..8fc4a0c 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -125,7 +125,7 @@
// Parse Fragment-Host.
//
- List<Requirement> hostReqs = parseFragmentHost(m_logger, m_headerMap);
+ List<Requirement> hostReqs = parseFragmentHost(m_logger, owner, m_headerMap);
//
// Parse Require-Bundle
@@ -134,7 +134,7 @@
List<ParsedHeaderClause> requireClauses =
parseStandardHeader((String) headerMap.get(Constants.REQUIRE_BUNDLE));
requireClauses = normalizeRequireClauses(m_logger, requireClauses, getManifestVersion());
- List<Requirement> requireReqs = convertRequires(requireClauses);
+ List<Requirement> requireReqs = convertRequires(requireClauses, owner);
//
// Parse Import-Package.
@@ -143,7 +143,7 @@
List<ParsedHeaderClause> importClauses =
parseStandardHeader((String) headerMap.get(Constants.IMPORT_PACKAGE));
importClauses = normalizeImportClauses(m_logger, importClauses, getManifestVersion());
- List<Requirement> importReqs = convertImports(importClauses);
+ List<Requirement> importReqs = convertImports(importClauses, owner);
//
// Parse DynamicImport-Package.
@@ -152,7 +152,7 @@
List<ParsedHeaderClause> dynamicClauses =
parseStandardHeader((String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
dynamicClauses = normalizeDynamicImportClauses(m_logger, dynamicClauses, getManifestVersion());
- m_dynamicRequirements = convertImports(dynamicClauses);
+ m_dynamicRequirements = convertImports(dynamicClauses, owner);
//
// Parse Export-Package.
@@ -173,7 +173,7 @@
{
List<ParsedHeaderClause> implicitClauses =
calculateImplicitImports(exportCaps, importClauses);
- importReqs.addAll(convertImports(implicitClauses));
+ importReqs.addAll(convertImports(implicitClauses, owner));
List<ParsedHeaderClause> allImportClauses =
new ArrayList<ParsedHeaderClause>(implicitClauses.size() + importClauses.size());
@@ -376,7 +376,8 @@
return clauses;
}
- private static List<Requirement> convertImports(List<ParsedHeaderClause> clauses)
+ private static List<Requirement> convertImports(
+ List<ParsedHeaderClause> clauses, Module owner)
{
// Now convert generic header clauses into requirements.
List reqList = new ArrayList();
@@ -397,6 +398,7 @@
// Create package requirement and add to requirement list.
reqList.add(
new RequirementImpl(
+ owner,
Capability.PACKAGE_NAMESPACE,
clauses.get(clauseIdx).m_dirs,
newAttrs));
@@ -1113,7 +1115,8 @@
return null;
}
- private static List<Requirement> parseFragmentHost(Logger logger, Map headerMap)
+ private static List<Requirement> parseFragmentHost(
+ Logger logger, Module owner, Map headerMap)
throws BundleException
{
List<Requirement> reqs = new ArrayList();
@@ -1164,7 +1167,8 @@
clauses.get(0).m_paths.get(0), false));
newAttrs.addAll(attrs);
- reqs.add(new RequirementImpl(Capability.HOST_NAMESPACE,
+ reqs.add(new RequirementImpl(
+ owner, Capability.HOST_NAMESPACE,
clauses.get(0).m_dirs,
newAttrs));
}
@@ -1262,7 +1266,8 @@
return clauses;
}
- private static List<Requirement> convertRequires(List<ParsedHeaderClause> clauses)
+ private static List<Requirement> convertRequires(
+ List<ParsedHeaderClause> clauses, Module owner)
{
List<Requirement> reqList = new ArrayList();
for (int clauseIdx = 0; clauseIdx < clauses.size(); clauseIdx++)
@@ -1283,6 +1288,7 @@
// Create package requirement and add to requirement list.
reqList.add(
new RequirementImpl(
+ owner,
Capability.MODULE_NAMESPACE,
clauses.get(clauseIdx).m_dirs,
newAttrs));
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/RequirementImpl.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/RequirementImpl.java
index e091803..9cbc942 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/RequirementImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/RequirementImpl.java
@@ -25,11 +25,13 @@
import org.apache.felix.framework.capabilityset.Directive;
import org.apache.felix.framework.capabilityset.Requirement;
import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.resolver.Module;
import org.apache.felix.framework.util.VersionRange;
import org.osgi.framework.Constants;
public class RequirementImpl implements Requirement
{
+ private final Module m_module;
private final String m_namespace;
private final SimpleFilter m_filter;
private final boolean m_optional;
@@ -37,8 +39,10 @@
private final List<Directive> m_dirsConst;
public RequirementImpl(
- String namespace, List<Directive> dirs, List<Attribute> attrs)
+ Module module, String namespace,
+ List<Directive> dirs, List<Attribute> attrs)
{
+ m_module = module;
m_namespace = namespace;
m_dirs = dirs;
m_dirsConst = Collections.unmodifiableList(m_dirs);
@@ -56,6 +60,11 @@
m_optional = optional;
}
+ public Module getModule()
+ {
+ return m_module;
+ }
+
public String getNamespace()
{
return m_namespace;
@@ -90,7 +99,7 @@
public String toString()
{
- return m_namespace + "; " + getFilter().toString();
+ return "[" + m_module + "] " + m_namespace + "; " + getFilter().toString();
}
private static SimpleFilter convertToFilter(List<Attribute> attrs)