[FELIX-4942] Slightly refactor Candidates.populate() to remove the recursion.
[FELIX-4942] Populate the dependent map at the same time.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1690731 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/resolver/src/main/java/org/apache/felix/resolver/Candidates.java b/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
index 8f56fdd..578706f 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
@@ -24,6 +24,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -50,8 +51,17 @@
class Candidates
{
- public static final int MANDATORY = 0;
- public static final int OPTIONAL = 1;
+ static class PopulateResult {
+ boolean success;
+ ResolutionError error;
+ List<Requirement> remaining;
+ Map<Requirement, List<Capability>> candidates;
+
+ @Override
+ public String toString() {
+ return success ? "true" : error != null ? error.getMessage() : "???";
+ }
+ }
private final Set<Resource> m_mandatoryResources;
// Maps a capability to requirements that match it.
@@ -62,10 +72,7 @@
// when a revision being resolved has fragments to attach to it.
private final Map<Resource, WrappedResource> m_allWrappedHosts;
// Map used when populating candidates to hold intermediate and final results.
- private final OpenHashMap<Resource, Object> m_populateResultCache;
-
- // Flag to signal if fragments are present in the candidate map.
- private boolean m_fragmentsPresent = false;
+ private final OpenHashMap<Resource, PopulateResult> m_populateResultCache;
private final Map<Resource, Boolean> m_validOnDemandResources;
@@ -81,8 +88,7 @@
OpenHashMapSet<Capability, Requirement> dependentMap,
OpenHashMapList<Requirement, Capability> candidateMap,
Map<Resource, WrappedResource> wrappedHosts,
- OpenHashMap<Resource, Object> populateResultCache,
- boolean fragmentsPresent,
+ OpenHashMap<Resource, PopulateResult> populateResultCache,
Map<Resource, Boolean> onDemandResources,
Map<Capability, Requirement> substitutableMap,
OpenHashMapSet<Requirement, Capability> delta)
@@ -92,7 +98,6 @@
m_candidateMap = candidateMap;
m_allWrappedHosts = wrappedHosts;
m_populateResultCache = populateResultCache;
- m_fragmentsPresent = fragmentsPresent;
m_validOnDemandResources = onDemandResources;
m_subtitutableMap = substitutableMap;
m_delta = delta;
@@ -107,7 +112,7 @@
m_dependentMap = new OpenHashMapSet<Capability, Requirement>();
m_candidateMap = new OpenHashMapList<Requirement, Capability>();
m_allWrappedHosts = new HashMap<Resource, WrappedResource>();
- m_populateResultCache = new OpenHashMap<Resource, Object>();
+ m_populateResultCache = new OpenHashMap<Resource, PopulateResult>();
m_validOnDemandResources = validOnDemandResources;
m_subtitutableMap = new OpenHashMap<Capability, Requirement>();
m_delta = new OpenHashMapSet<Requirement, Capability>(3);
@@ -123,259 +128,139 @@
* original Candidates permutation.
* @return the delta
*/
- public Object getDelta() {
+ public Object getDelta()
+ {
return m_delta;
}
- /**
- * Populates candidates for the specified revision. How a revision is
- * resolved depends on its resolution type as follows:
- * <ul>
- * <li><tt>MANDATORY</tt> - must resolve and failure to do so throws an
- * exception.</li>
- * <li><tt>OPTIONAL</tt> - attempt to resolve, but no exception is thrown if
- * the resolve fails.</li>
- * <li><tt>ON_DEMAND</tt> - only resolve on demand; this only applies to
- * fragments and will only resolve a fragment if its host is already
- * selected as a candidate.</li>
- * </ul>
- *
- * @param rc the resolve context used for populating the candidates.
- * @param resource the resource whose candidates should be populated.
- * @param resolution indicates the resolution type.
- */
- public final ResolutionError populate(
- ResolveContext rc, Resource resource, int resolution)
+ public void addMandatoryResources(Collection<Resource> resources)
{
- // Get the current result cache value, to make sure the revision
- // hasn't already been populated.
- Object cacheValue = m_populateResultCache.get(resource);
- // Has been unsuccessfully populated.
- if (cacheValue instanceof ResolutionError)
- {
- return null;
- }
- // Has been successfully populated.
- else if (cacheValue instanceof Boolean)
- {
- return null;
- }
-
- // We will always attempt to populate fragments, since this is necessary
- // for ondemand attaching of fragment. However, we'll only attempt to
- // populate optional non-fragment revisions if they aren't already
- // resolved.
- boolean isFragment = Util.isFragment(resource);
- if (!isFragment && rc.getWirings().containsKey(resource))
- {
- return null;
- }
-
- if (resolution == MANDATORY)
- {
- m_mandatoryResources.add(resource);
- }
- // Try to populate candidates for the optional revision.
- ResolutionError error = populateResource(rc, resource);
- // Only throw an exception if resolution is mandatory.
- if (error != null && resolution == MANDATORY)
- {
- return error;
- }
- return null;
+ m_mandatoryResources.addAll(resources);
}
- /**
- * Populates candidates for the specified revision.
- *
- * @param rc the resolver state used for populating the candidates.
- * @param resource the revision whose candidates should be populated.
- */
-// TODO: FELIX3 - Modify to not be recursive.
- @SuppressWarnings("unchecked")
- private ResolutionError populateResource(ResolveContext rc, Resource resource)
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public ResolutionError populate(ResolveContext rc, Collection<Resource> resources)
{
- // Determine if we've already calculated this revision's candidates.
- // The result cache will have one of three values:
- // 1. A resolve exception if we've already attempted to populate the
- // revision's candidates but were unsuccessful.
- // 2. Boolean.TRUE indicating we've already attempted to populate the
- // revision's candidates and were successful.
- // 3. An array containing the cycle count, current map of candidates
- // for already processed requirements, and a list of remaining
- // requirements whose candidates still need to be calculated.
- // For case 1, rethrow the exception. For case 2, simply return immediately.
- // For case 3, this means we have a cycle so we should continue to populate
- // the candidates where we left off and not record any results globally
- // until we've popped completely out of the cycle.
-
- // Keeps track of the number of times we've reentered this method
- // for the current revision.
- Integer cycleCount = null;
-
- // Keeps track of the candidates we've already calculated for the
- // current revision's requirements.
- Map<Requirement, List<Capability>> localCandidateMap = null;
-
- // Keeps track of the current revision's requirements for which we
- // haven't yet found candidates.
- List<Requirement> remainingReqs = null;
-
- // Get the cache value for the current revision.
- Object cacheValue = m_populateResultCache.get(resource);
-
- // This is case 1.
- if (cacheValue instanceof ResolutionError)
+ Set<Resource> toRemove = new HashSet<Resource>();
+ LinkedList<Resource> toPopulate = new LinkedList<Resource>(resources);
+ while (!toPopulate.isEmpty())
{
- return (ResolutionError) cacheValue;
- }
- // This is case 2.
- else if (cacheValue instanceof Boolean)
- {
- return null;
- }
- // This is case 3.
- else if (cacheValue != null)
- {
- // Increment and get the cycle count.
- cycleCount = (Integer) (((Object[]) cacheValue)[0] = (Integer) ((Object[]) cacheValue)[0] + 1);
- // Get the already populated candidates.
- localCandidateMap = (Map) ((Object[]) cacheValue)[1];
- // Get the remaining requirements.
- remainingReqs = (List) ((Object[]) cacheValue)[2];
- }
-
- // If there is no cache value for the current revision, then this is
- // the first time we are attempting to populate its candidates, so
- // do some one-time checks and initialization.
- if ((remainingReqs == null) && (localCandidateMap == null))
- {
- // Record cycle count.
- cycleCount = 0;
-
- // Create a local map for populating candidates first, just in case
- // the revision is not resolvable.
- localCandidateMap = new HashMap<Requirement, List<Capability>>();
-
- // Create a modifiable list of the revision's requirements.
- remainingReqs = new ArrayList<Requirement>(resource.getRequirements(null));
-
- // Add these value to the result cache so we know we are
- // in the middle of populating candidates for the current
- // revision.
- m_populateResultCache.put(resource,
- cacheValue = new Object[] { cycleCount, localCandidateMap, remainingReqs });
- }
-
- // If we have requirements remaining, then find candidates for them.
- while (!remainingReqs.isEmpty())
- {
- Requirement req = remainingReqs.remove(0);
-
- // Ignore non-effective and dynamic requirements.
- String resolution = req.getDirectives()
- .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE);
- if (!rc.isEffective(req)
- || ((resolution != null)
- && resolution.equals(PackageNamespace.RESOLUTION_DYNAMIC)))
+ Resource resource = toPopulate.getFirst();
+ // Get cached result
+ PopulateResult result = m_populateResultCache.get(resource);
+ if (result == null)
+ {
+ result = new PopulateResult();
+ result.candidates = new OpenHashMap<Requirement, List<Capability>>();
+ result.remaining = new ArrayList<Requirement>(resource.getRequirements(null));
+ m_populateResultCache.put(resource, result);
+ }
+ if (result.success || result.error != null)
+ {
+ toPopulate.removeFirst();
+ continue;
+ }
+ if (result.remaining.isEmpty())
+ {
+ toPopulate.removeFirst();
+ result.success = true;
+ addCandidates(result.candidates);
+ result.candidates = null;
+ result.remaining = null;
+ if ((rc instanceof FelixResolveContext) && !Util.isFragment(resource))
+ {
+ Collection<Resource> ondemandFragments = ((FelixResolveContext) rc).getOndemandResources(resource);
+ for (Resource fragment : ondemandFragments)
+ {
+ Boolean valid = m_validOnDemandResources.get(fragment);
+ if (valid == null)
+ {
+ // Mark this resource as a valid on demand resource
+ m_validOnDemandResources.put(fragment, Boolean.TRUE);
+ valid = Boolean.TRUE;
+ }
+ if (valid)
+ {
+ // This resource is a valid on demand resource;
+ // populate it now, consider it optional
+ toPopulate.addFirst(fragment);
+ }
+ }
+ }
+ continue;
+ }
+ // We have a requirement to process
+ Requirement requirement = result.remaining.remove(0);
+ if (!isEffective(rc, requirement))
{
continue;
}
-
- // Process the candidates, removing any candidates that
- // cannot resolve.
- List<Capability> candidates = rc.findProviders(req);
- ResolutionError rethrow = processCandidates(rc, resource, candidates);
-
- // First, due to cycles, makes sure we haven't already failed in
- // a deeper recursion.
- Object result = m_populateResultCache.get(resource);
- if (result instanceof ResolutionError)
- {
- return (ResolutionError) result;
- }
- // Next, if are no candidates remaining and the requirement is not
- // not optional, then record and throw a resolve exception.
- else if (candidates.isEmpty() && !Util.isOptional(req))
+ List<Capability> candidates = rc.findProviders(requirement);
+ LinkedList<Resource> newToPopulate = new LinkedList<Resource>();
+ ResolutionError thrown = processCandidates(rc, newToPopulate, requirement, candidates);
+ if (candidates.isEmpty() && !Util.isOptional(requirement))
{
if (Util.isFragment(resource) && rc.getWirings().containsKey(resource))
{
// This is a fragment that is already resolved and there is no unresolved hosts to attach it to.
- m_populateResultCache.put(resource, Boolean.TRUE);
- return null;
+ result.success = true;
}
- rethrow = new MissingRequirementError(req, rethrow);
- m_populateResultCache.put(resource, rethrow);
- return rethrow;
+ else
+ {
+ result.error = new MissingRequirementError(requirement, thrown);
+ toRemove.add(resource);
+ }
+ toPopulate.removeFirst();
}
- // Otherwise, if we actually have candidates for the requirement, then
- // add them to the local candidate map.
- else if (candidates.size() > 0)
+ else
{
- localCandidateMap.put(req, candidates);
+ if (!candidates.isEmpty())
+ {
+ result.candidates.put(requirement, candidates);
+ }
+ if (!newToPopulate.isEmpty())
+ {
+ toPopulate.addAll(0, newToPopulate);
+ }
}
}
- // If we are exiting from a cycle then decrement
- // cycle counter, otherwise record the result.
- if (cycleCount > 0)
+ while (!toRemove.isEmpty())
{
- ((Object[]) cacheValue)[0] = cycleCount - 1;
- }
- else if (cycleCount == 0)
- {
- // Record that the revision was successfully populated.
- m_populateResultCache.put(resource, Boolean.TRUE);
- // FELIX-4825: Verify candidate map in case of cycles and optional requirements
- for (Iterator<Map.Entry<Requirement, List<Capability>>> it = localCandidateMap.entrySet().iterator(); it.hasNext();)
- {
- Map.Entry<Requirement, List<Capability>> entry = it.next();
- for (Iterator<Capability> it2 = entry.getValue().iterator(); it2.hasNext();)
- {
- if (m_populateResultCache.get(it2.next().getResource()) instanceof ResolutionError)
- {
- it2.remove();
- }
- }
- if (entry.getValue().isEmpty())
- {
- it.remove();
- }
- }
- // Merge local candidate map into global candidate map.
- if (localCandidateMap.size() > 0)
- {
- add(localCandidateMap);
- }
- if ((rc instanceof FelixResolveContext) && !Util.isFragment(resource))
- {
- Collection<Resource> ondemandFragments = ((FelixResolveContext) rc).getOndemandResources(resource);
- for (Resource fragment : ondemandFragments)
- {
- Boolean valid = m_validOnDemandResources.get(fragment);
- if (valid == null)
- {
- // Mark this resource as a valid on demand resource
- m_validOnDemandResources.put(fragment, Boolean.TRUE);
- valid = Boolean.TRUE;
- }
- if (valid)
- {
- // This resource is a valid on demand resource;
- // populate it now, consider it optional
- populate(rc, fragment, OPTIONAL);
- }
- }
- }
+ Iterator<Resource> iterator = toRemove.iterator();
+ Resource resource = iterator.next();
+ iterator.remove();
+ remove(resource, toRemove);
}
return null;
}
+ private boolean isEffective(ResolveContext rc, Requirement req) {
+ if (!rc.isEffective(req)) {
+ return false;
+ }
+ String res = req.getDirectives().get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE);
+ return !PackageNamespace.RESOLUTION_DYNAMIC.equals(res);
+ }
+
+ private boolean isMandatory(ResolveContext rc, Requirement requirement) {
+ // The requirement is optional
+ if (Util.isOptional(requirement)) {
+ return false;
+ }
+ // This is a fragment that is already resolved and there is no unresolved hosts to attach it to
+ Resource resource = requirement.getResource();
+ if (Util.isFragment(resource) && rc.getWirings().containsKey(resource)) {
+ return false;
+ }
+ return true;
+ }
+
private void populateSubstitutables()
{
- for (Map.Entry<Resource, Object> populated : m_populateResultCache.fast())
+ for (Map.Entry<Resource, PopulateResult> populated : m_populateResultCache.fast())
{
- if (populated.getValue() instanceof Boolean)
+ if (populated.getValue().success)
{
populateSubstitutables(populated.getKey());
}
@@ -385,31 +270,33 @@
private void populateSubstitutables(Resource resource)
{
// Collect the package names exported
- List<Capability> packageExports = resource.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE);
- if (packageExports.isEmpty())
- {
- return;
- }
- List<Requirement> packageImports = resource.getRequirements(PackageNamespace.PACKAGE_NAMESPACE);
- if (packageImports.isEmpty())
- {
- return;
- }
OpenHashMap<String, List<Capability>> exportNames = new OpenHashMap<String, List<Capability>>() {
@Override
protected List<Capability> compute(String s) {
return new ArrayList<Capability>(1);
}
};
- for (Capability packageExport : packageExports)
+ for (Capability packageExport : resource.getCapabilities(null))
{
+ if (!PackageNamespace.PACKAGE_NAMESPACE.equals(packageExport.getNamespace()))
+ {
+ continue;
+ }
String packageName = (String) packageExport.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
List<Capability> caps = exportNames.getOrCompute(packageName);
caps.add(packageExport);
}
- // Check if any requirements substitute one of the exported packages
- for (Requirement req : packageImports)
+ if (exportNames.isEmpty())
{
+ return;
+ }
+ // Check if any requirements substitute one of the exported packages
+ for (Requirement req : resource.getRequirements(null))
+ {
+ if (!PackageNamespace.PACKAGE_NAMESPACE.equals(req.getNamespace()))
+ {
+ continue;
+ }
List<Capability> substitutes = m_candidateMap.get(req);
if (substitutes != null && !substitutes.isEmpty())
{
@@ -453,14 +340,6 @@
// Remove any substituted exports from candidates
for (Map.Entry<Capability, Integer> substituteStatus : substituteStatuses.fast())
{
- if (substituteStatus.getValue() == SUBSTITUTED)
- {
- if (m_dependentMap.isEmpty())
- {
- // make sure the dependents are populated
- populateDependents();
- }
- }
// add a permutation that imports a different candidate for the substituted if possible
Requirement substitutedReq = m_subtitutableMap.get(substituteStatus.getKey());
if (substitutedReq != null)
@@ -579,12 +458,15 @@
// Process the candidates, removing any candidates that
// cannot resolve.
- ResolutionError rethrow = processCandidates(rc, resource, candidates);
+ // TODO: verify the two following statements
+ LinkedList<Resource> toPopulate = new LinkedList<Resource>();
+ toPopulate.add(resource);
+ ResolutionError rethrow = processCandidates(rc, toPopulate, req, candidates);
// Add the dynamic imports candidates.
// Make sure this is done after the call to processCandidates since we want to ensure
// fragment candidates are properly hosted before adding the candidates list which makes a copy
- add(req, candidates);
+ addCandidates(req, candidates);
if (candidates.isEmpty())
{
@@ -595,25 +477,16 @@
return rethrow;
}
- m_populateResultCache.put(resource, Boolean.TRUE);
+ PopulateResult result = new PopulateResult();
+ result.success = true;
+ m_populateResultCache.put(resource, result);
return null;
}
- /**
- * This method performs common processing on the given set of candidates.
- * Specifically, it removes any candidates which cannot resolve and it
- * synthesizes candidates for any candidates coming from any attached
- * fragments, since fragment capabilities only appear once, but technically
- * each host represents a unique capability.
- *
- * @param rc the resolver state.
- * @param resource the revision being resolved.
- * @param candidates the candidates to process.
- * @return a resolve exception to be re-thrown, if any, or null.
- */
private ResolutionError processCandidates(
ResolveContext rc,
- Resource resource,
+ LinkedList<Resource> toPopulate,
+ Requirement req,
List<Capability> candidates)
{
// Get satisfying candidates and populate their candidates if necessary.
@@ -651,18 +524,25 @@
// of recursion; thus, any avoided recursion results in fewer
// exceptions to chain when an error does occur.
if ((isFragment || !rc.getWirings().containsKey(candCap.getResource()))
- && !candCap.getResource().equals(resource))
+ && !candCap.getResource().equals(req.getResource()))
{
- ResolutionError error = populateResource(rc, candCap.getResource());
- if (error != null)
+ PopulateResult result = m_populateResultCache.get(candCap.getResource());
+ if (result != null)
{
- if (rethrow == null)
+ if (result.error != null)
{
- rethrow = error;
+ // Remove the candidate since we weren't able to
+ // populate its candidates.
+ itCandCap.remove();
}
- // Remove the candidate since we weren't able to
- // populate its candidates.
- itCandCap.remove();
+ else if (!result.success)
+ {
+ toPopulate.add(candCap.getResource());
+ }
+ }
+ else
+ {
+ toPopulate.add(candCap.getResource());
}
}
}
@@ -728,15 +608,14 @@
public boolean isPopulated(Resource resource)
{
- Object value = m_populateResultCache.get(resource);
- return ((value != null) && (value instanceof Boolean));
+ PopulateResult value = m_populateResultCache.get(resource);
+ return (value != null && value.success);
}
public ResolutionError getResolutionError(Resource resource)
{
- Object value = m_populateResultCache.get(resource);
- return ((value != null) && (value instanceof ResolutionError))
- ? (ResolutionError) value : null;
+ PopulateResult value = m_populateResultCache.get(resource);
+ return value != null ? value.error : null;
}
/**
@@ -749,15 +628,14 @@
* @param req the requirement to add.
* @param candidates the candidates matching the requirement.
*/
- private void add(Requirement req, List<Capability> candidates)
+ private void addCandidates(Requirement req, List<Capability> candidates)
{
- if (req.getNamespace().equals(HostNamespace.HOST_NAMESPACE))
- {
- m_fragmentsPresent = true;
- }
-
// Record the candidates.
m_candidateMap.put(req, new CopyOnWriteList<Capability>(candidates));
+ for (Capability cap : candidates)
+ {
+ m_dependentMap.getOrCompute(cap).add(req);
+ }
}
/**
@@ -767,11 +645,11 @@
*
* @param candidates the bulk requirements and candidates to add.
*/
- private void add(Map<Requirement, List<Capability>> candidates)
+ private void addCandidates(Map<Requirement, List<Capability>> candidates)
{
- for (Entry<Requirement, List<Capability>> entry : candidates.entrySet())
+ for (Map.Entry<Requirement, List<Capability>> entry : candidates.entrySet())
{
- add(entry.getKey(), entry.getValue());
+ addCandidates(entry.getKey(), entry.getValue());
}
}
@@ -855,7 +733,7 @@
* satisfied by the fragment will end up having the two hosts as potential
* candidates, rather than the single fragment.
*
- * @throws org.osgi.service.resolver.ResolutionException if the removal of any unselected fragments
+ * @return ResolutionError if the removal of any unselected fragments
* result in the root module being unable to resolve.
*/
public ResolutionError prepare(ResolveContext rc)
@@ -864,11 +742,8 @@
// the fragment map maps a fragment symbolic name to a map that maps
// a version to a list of fragments requirements matching that symbolic
// name and version.
- Map<Capability, Map<String, Map<Version, List<Requirement>>>> hostFragments = Collections.emptyMap();
- if (m_fragmentsPresent)
- {
- hostFragments = populateDependents();
- }
+ Map<Capability, Map<String, Map<Version, List<Requirement>>>> hostFragments =
+ getHostFragments();
// This method performs the following steps:
// 1. Select the fragments to attach to a given host.
@@ -1072,7 +947,7 @@
// the fragment map maps a fragment symbolic name to a map that maps
// a version to a list of fragments requirements matching that symbolic
// name and version.
- private Map<Capability, Map<String, Map<Version, List<Requirement>>>> populateDependents()
+ private Map<Capability, Map<String, Map<Version, List<Requirement>>>> getHostFragments()
{
Map<Capability, Map<String, Map<Version, List<Requirement>>>> hostFragments =
new HashMap<Capability, Map<String, Map<Version, List<Requirement>>>>();
@@ -1082,10 +957,6 @@
List<Capability> caps = entry.getValue();
for (Capability cap : caps)
{
- // Record the requirement as dependent on the capability.
- CopyOnWriteSet<Requirement> dependents = m_dependentMap.getOrCompute(cap);
- dependents.add(req);
-
// Keep track of hosts and associated fragments.
if (req.getNamespace().equals(HostNamespace.HOST_NAMESPACE))
{
@@ -1133,7 +1004,9 @@
private void removeResource(Resource resource, ResolutionError ex)
{
// Add removal reason to result cache.
- m_populateResultCache.put(resource, ex);
+ PopulateResult result = m_populateResultCache.get(resource);
+ result.success = false;
+ result.error = ex;
// Remove from dependents.
Set<Resource> unresolvedResources = new HashSet<Resource>();
remove(resource, unresolvedResources);
@@ -1214,9 +1087,10 @@
m_candidateMap.remove(r);
if (!Util.isOptional(r))
{
- m_populateResultCache.put(
- r.getResource(),
- new MissingRequirementError(r));
+ PopulateResult result = m_populateResultCache.get(r.getResource());
+ result.success = false;
+ result.error =
+ new MissingRequirementError(r);
unresolvedResources.add(r.getResource());
}
}
@@ -1238,7 +1112,6 @@
m_candidateMap.deepClone(),
m_allWrappedHosts,
m_populateResultCache,
- m_fragmentsPresent,
m_validOnDemandResources,
m_subtitutableMap,
m_delta.deepClone());
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 930e1a5..b73ca51 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
@@ -23,7 +23,6 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -38,6 +37,7 @@
import org.apache.felix.resolver.util.ArrayMap;
import org.apache.felix.resolver.util.OpenHashMap;
+
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
import org.osgi.framework.namespace.HostNamespace;
@@ -168,41 +168,32 @@
// Create object to hold all candidates.
Candidates allCandidates = new Candidates(validOnDemandResources);
+ List<Resource> mandatory = new ArrayList<Resource>();
+ List<Resource> toPopulate = new ArrayList<Resource>();
+
// Populate mandatory resources; since these are mandatory
// resources, failure throws a resolve exception.
- for (Iterator<Resource> it = mandatoryResources.iterator();
- it.hasNext();)
+ for (Resource resource : mandatoryResources)
{
- Resource resource = it.next();
if (Util.isFragment(resource) || (rc.getWirings().get(resource) == null))
{
- ResolutionError error = allCandidates.populate(rc, resource, Candidates.MANDATORY);
- if (error != null)
- {
- throw error.toException();
- }
- }
- else
- {
- it.remove();
+ mandatory.add(resource);
+ toPopulate.add(resource);
}
}
-
// Populate optional resources; since these are optional
// resources, failure does not throw a resolve exception.
for (Resource resource : optionalResources)
{
- boolean isFragment = Util.isFragment(resource);
- if (isFragment || (rc.getWirings().get(resource) == null))
+ if (Util.isFragment(resource) || (rc.getWirings().get(resource) == null))
{
- ResolutionError error = allCandidates.populate(rc, resource, Candidates.OPTIONAL);
- if (error != null)
- {
- throw error.toException();
- }
+ toPopulate.add(resource);
}
}
+ allCandidates.addMandatoryResources(mandatory);
+ allCandidates.populate(rc, toPopulate);
+
// Merge any fragments into hosts.
ResolutionError rethrow = allCandidates.prepare(rc);
if (rethrow != null)
@@ -226,6 +217,7 @@
List<Candidates> usesPermutations = session.getUsesPermutations();
List<Candidates> importPermutations = session.getImportPermutations();
+ List<Candidates> substPermutations = new ArrayList<Candidates>();
// Record the initial candidate permutation.
usesPermutations.add(allCandidates);
@@ -248,15 +240,23 @@
Map<Resource, ResolutionError> faultyResources = null;
do
{
- allCandidates = (usesPermutations.size() > 0)
- ? usesPermutations.remove(0)
- : (importPermutations.size() > 0
- ? importPermutations.remove(0)
- : null);
- if (allCandidates == null)
+ if (!usesPermutations.isEmpty())
+ {
+ allCandidates = usesPermutations.remove(0);
+ }
+ else if (!importPermutations.isEmpty())
+ {
+ allCandidates = importPermutations.remove(0);
+ }
+ else if (!substPermutations.isEmpty())
+ {
+ allCandidates = substPermutations.remove(0);
+ }
+ else
{
break;
}
+
// The delta is used to detect that we have already processed this particular permutation
if (!processedDeltas.add(allCandidates.getDelta()))
{
@@ -272,7 +272,7 @@
//allCandidates.dump();
- rethrow = allCandidates.checkSubstitutes(importPermutations);
+ rethrow = allCandidates.checkSubstitutes( /*substPermutations*/ importPermutations);
if (rethrow != null)
{
continue;
diff --git a/resolver/src/main/java/org/apache/felix/resolver/util/CopyOnWriteList.java b/resolver/src/main/java/org/apache/felix/resolver/util/CopyOnWriteList.java
index 1defe57..4af5dfa 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/util/CopyOnWriteList.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/util/CopyOnWriteList.java
@@ -144,7 +144,28 @@
}
public boolean containsAll(Collection<?> c) {
- throw new UnsupportedOperationException();
+ Object[] elements = data;
+ int len = elements.length;
+ for (Object e : c) {
+ if (indexOf(e, elements, len) < 0)
+ return false;
+ }
+ return true;
+ }
+
+ private static int indexOf(Object o, Object[] d, int len) {
+ if (o == null) {
+ for (int i = len; i-- > 0;) {
+ if (d[i] == null)
+ return i;
+ }
+ } else {
+ for (int i = len; i-- > 0;) {
+ if (o.equals(d[i]))
+ return i;
+ }
+ }
+ return -1;
}
public boolean addAll(Collection<? extends E> c) {
@@ -184,20 +205,7 @@
}
public int indexOf(Object o) {
- if (o == null) {
- Object[] d = data;
- for (int i = d.length; i-- > 0;) {
- if (d[i] == null)
- return i;
- }
- } else {
- Object[] d = data;
- for (int i = d.length; i-- > 0;) {
- if (o.equals(d[i]))
- return i;
- }
- }
- return -1;
+ return indexOf(o, data, data.length);
}
public int lastIndexOf(Object o) {
diff --git a/resolver/src/main/java/org/apache/felix/resolver/util/OpenHashMapList.java b/resolver/src/main/java/org/apache/felix/resolver/util/OpenHashMapList.java
index befcd77..f314ed5 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/util/OpenHashMapList.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/util/OpenHashMapList.java
@@ -28,10 +28,7 @@
super(initialCapacity);
}
-<<<<<<< HEAD
-=======
@SuppressWarnings("unchecked")
->>>>>>> d6bbce6... Speed up collections a bit
public OpenHashMapList<K, V> deepClone() {
OpenHashMapList<K, V> copy = (OpenHashMapList<K, V>) super.clone();
Object[] values = copy.value;