Apply patch FELIX-4493 to add support for "on demand" resource
resolving.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1589839 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 3bffc19..0555ce1 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
@@ -19,6 +19,7 @@
package org.apache.felix.resolver;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -44,7 +45,6 @@
{
public static final int MANDATORY = 0;
public static final int OPTIONAL = 1;
- public static final int ON_DEMAND = 2;
private final Set<Resource> m_mandatoryResources;
// Maps a capability to requirements that match it.
@@ -60,6 +60,8 @@
// Flag to signal if fragments are present in the candidate map.
private boolean m_fragmentsPresent = false;
+ private final Map<Resource, Boolean> m_validOnDemandResources;
+
/**
* Private copy constructor used by the copy() method.
* @param dependentMap the capability dependency map.
@@ -72,7 +74,8 @@
Map<Capability, Set<Requirement>> dependentMap,
Map<Requirement, List<Capability>> candidateMap,
Map<Resource, WrappedResource> wrappedHosts, Map<Resource, Object> populateResultCache,
- boolean fragmentsPresent)
+ boolean fragmentsPresent,
+ Map<Resource, Boolean> onDemandResources)
{
m_mandatoryResources = mandatoryResources;
m_dependentMap = dependentMap;
@@ -80,18 +83,20 @@
m_allWrappedHosts = wrappedHosts;
m_populateResultCache = populateResultCache;
m_fragmentsPresent = fragmentsPresent;
+ m_validOnDemandResources = onDemandResources;
}
/**
* Constructs an empty Candidates object.
**/
- public Candidates()
+ public Candidates(Map<Resource, Boolean> validOnDemandResources)
{
m_mandatoryResources = new HashSet<Resource>();
m_dependentMap = new HashMap<Capability, Set<Requirement>>();
m_candidateMap = new HashMap<Requirement, List<Capability>>();
m_allWrappedHosts = new HashMap<Resource, WrappedResource>();
m_populateResultCache = new HashMap<Resource, Object>();
+ m_validOnDemandResources = validOnDemandResources;
}
/**
@@ -137,28 +142,22 @@
return;
}
- // Always attempt to populate mandatory or optional revisions.
- // However, for on-demand fragments only populate if their host
- // is already populated.
- if ((resolution != ON_DEMAND)
- || (isFragment && populateFragmentOndemand(rc, resource)))
+
+ if (resolution == MANDATORY)
{
+ m_mandatoryResources.add(resource);
+ }
+ try
+ {
+ // Try to populate candidates for the optional revision.
+ populateResource(rc, resource);
+ }
+ catch (ResolutionException ex)
+ {
+ // Only throw an exception if resolution is mandatory.
if (resolution == MANDATORY)
{
- m_mandatoryResources.add(resource);
- }
- try
- {
- // Try to populate candidates for the optional revision.
- populateResource(rc, resource);
- }
- catch (ResolutionException ex)
- {
- // Only throw an exception if resolution is mandatory.
- if (resolution == MANDATORY)
- {
- throw ex;
- }
+ throw ex;
}
}
}
@@ -304,68 +303,28 @@
{
// Record that the revision was successfully populated.
m_populateResultCache.put(resource, Boolean.TRUE);
-
// Merge local candidate map into global candidate map.
if (localCandidateMap.size() > 0)
{
add(localCandidateMap);
}
- }
- }
-
- private boolean populateFragmentOndemand(ResolveContext rc, Resource resource)
- throws ResolutionException
- {
- // Create a modifiable list of the revision's requirements.
- List<Requirement> remainingReqs =
- new ArrayList(resource.getRequirements(null));
- // Find the host requirement.
- Requirement hostReq = null;
- for (Iterator<Requirement> it = remainingReqs.iterator();
- it.hasNext(); )
- {
- Requirement r = it.next();
- if (r.getNamespace().equals(HostNamespace.HOST_NAMESPACE))
- {
- hostReq = r;
- it.remove();
- break;
+ 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);
+ }
+ }
}
}
- // Get candidates hosts and keep any that have been populated.
- List<Capability> hosts = rc.findProviders(hostReq);
- for (Iterator<Capability> it = hosts.iterator(); it.hasNext(); )
- {
- Capability host = it.next();
- if (!isPopulated(host.getResource()))
- {
- it.remove();
- }
- }
- // If there aren't any populated hosts, then we can just
- // return since this fragment isn't needed.
- if (hosts.isEmpty())
- {
- return false;
- }
-
- // If there are populated host candidates, then prepopulate
- // the result cache with the work we've done so far.
- // Record cycle count, but start at -1 since it will
- // be incremented again in populate().
- Integer cycleCount = new Integer(-1);
- // Create a local map for populating candidates first, just in case
- // the revision is not resolvable.
- Map<Requirement, List<Capability>> localCandidateMap =
- new HashMap<Requirement, List<Capability>>();
- // Add the discovered host candidates to the local candidate map.
- localCandidateMap.put(hostReq, hosts);
- // 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,
- new Object[] { cycleCount, localCandidateMap, remainingReqs });
- return true;
}
public void populateDynamic(
@@ -1004,7 +963,7 @@
return new Candidates(
m_mandatoryResources, dependentMap, candidateMap,
- m_allWrappedHosts, m_populateResultCache, m_fragmentsPresent);
+ m_allWrappedHosts, m_populateResultCache, m_fragmentsPresent, m_validOnDemandResources);
}
public void dump(ResolveContext rc)
diff --git a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
index 56e6df3..c1eba6c 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
@@ -122,10 +122,9 @@
// Make copies of arguments in case we want to modify them.
Collection<Resource> mandatoryResources = new ArrayList(rc.getMandatoryResources());
Collection<Resource> optionalResources = new ArrayList(rc.getOptionalResources());
-// TODO: RFC-112 - Need impl-specific type.
-// Collection<Resource> ondemandFragments = (rc instanceof ResolveContextImpl)
-// ? ((ResolveContextImpl) rc).getOndemandResources() : Collections.EMPTY_LIST;
- Collection<Resource> ondemandFragments = Collections.EMPTY_LIST;
+ // keeps track of valid on demand fragments that we have seen.
+ // a null value or TRUE indicate it is valid
+ Map<Resource, Boolean> validOnDemandResources = new HashMap<Resource, Boolean>(0);
boolean retry;
do
@@ -134,7 +133,7 @@
try
{
// Create object to hold all candidates.
- Candidates allCandidates = new Candidates();
+ Candidates allCandidates = new Candidates(validOnDemandResources);
// Populate mandatory resources; since these are mandatory
// resources, failure throws a resolve exception.
@@ -163,17 +162,6 @@
}
}
- // Populate ondemand fragments; since these are optional
- // resources, failure does not throw a resolve exception.
- for (Resource resource : ondemandFragments)
- {
- boolean isFragment = Util.isFragment(resource);
- if (isFragment)
- {
- allCandidates.populate(rc, resource, Candidates.ON_DEMAND);
- }
- }
-
// Merge any fragments into hosts.
allCandidates.prepare(rc);
@@ -302,7 +290,16 @@
{
if (faultyResources != null) {
Set<Resource> resourceKeys = faultyResources.keySet();
- retry = (optionalResources.removeAll(resourceKeys) || ondemandFragments.removeAll(resourceKeys));
+ retry = (optionalResources.removeAll(resourceKeys));
+ for (Resource faultyResource : resourceKeys) {
+ Boolean valid = validOnDemandResources.get(faultyResource);
+ if (valid != null && valid.booleanValue()) {
+ // This was an ondemand resource.
+ // Invalidate it and try again.
+ validOnDemandResources.put(faultyResource, Boolean.FALSE);
+ retry = true;
+ }
+ }
// log all the resolution exceptions for the uses constraint violations
for (Map.Entry<Resource, ResolutionException> usesError : faultyResources.entrySet()) {
m_logger.logUsesConstraintViolation(usesError.getKey(), usesError.getValue());
@@ -379,8 +376,6 @@
* @param host the hosting resource
* @param dynamicReq the dynamic requirement
* @param matches a list of matching capabilities
- * @param ondemandFragments collection of on demand fragments that will
- * attach to any host that is a candidate
* @return The new resources and wires required to satisfy the specified
* dynamic requirement. The returned map is the property of the caller and
* can be modified by the caller.
@@ -388,7 +383,7 @@
*/
public Map<Resource, List<Wire>> resolve(
ResolveContext rc, Resource host, Requirement dynamicReq,
- List<Capability> matches, Collection<Resource> ondemandFragments)
+ List<Capability> matches)
throws ResolutionException
{
ResolveSession session = new ResolveSession(rc);
@@ -413,15 +408,11 @@
}
}
- // Make copy of args in case we want to modify them.
- ondemandFragments = new ArrayList<Resource>(ondemandFragments);
- // Create all candidates pre-populated with the single candidate set
- // for the resolving dynamic import of the host.
- Candidates allCandidates = new Candidates();
- allCandidates.populateDynamic(rc, host, dynamicReq, matches);
+
Map<Resource, Packages> resourcePkgMap = new HashMap<Resource, Packages>();
+ Map<Resource, Boolean> onDemandResources = new HashMap<Resource, Boolean>();
boolean retry;
do
@@ -430,15 +421,10 @@
try
{
- // Try to populate optional fragments.
- for (Resource r : ondemandFragments)
- {
- if (Util.isFragment(r))
- {
- allCandidates.populate(rc, r, Candidates.ON_DEMAND);
- }
- }
-
+ // Create all candidates pre-populated with the single candidate set
+ // for the resolving dynamic import of the host.
+ Candidates allCandidates = new Candidates(onDemandResources);
+ allCandidates.populateDynamic(rc, host, dynamicReq, matches);
// Merge any fragments into hosts.
allCandidates.prepare(rc);
@@ -508,15 +494,13 @@
((WrappedRequirement) faultyReq)
.getDeclaredRequirement().getResource();
}
- // Try to ignore the faulty resource if it is not mandatory.
- if (ondemandFragments.remove(faultyResource))
- {
- retry = true;
- }
- else
- {
+ Boolean valid = onDemandResources.get(faultyResource);
+ if (valid != null && valid.booleanValue()) {
+ onDemandResources.put(faultyResource, Boolean.FALSE);
+ retry = true;
+ } else {
throw rethrow;
- }
+ }
}
// If there is no exception to rethrow, then this was a clean
// resolve, so populate the wire map.