You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2010/02/25 07:02:39 UTC
svn commit: r916139 -
/felix/sandbox/rickhall/framework-proto/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
Author: rickhall
Date: Thu Feb 25 06:02:39 2010
New Revision: 916139
URL: http://svn.apache.org/viewvc?rev=916139&view=rev
Log:
Improve how candidates are populated to avoid clean up for broken cycles.
Modified:
felix/sandbox/rickhall/framework-proto/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
Modified: felix/sandbox/rickhall/framework-proto/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/framework-proto/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java?rev=916139&r1=916138&r2=916139&view=diff
==============================================================================
--- felix/sandbox/rickhall/framework-proto/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java (original)
+++ felix/sandbox/rickhall/framework-proto/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java Thu Feb 25 06:02:39 2010
@@ -378,52 +378,86 @@
// Determine if we've already calculated this module's candidates.
// The result cache will have one of three values:
- // 1. A resolve exception if there are not sufficient candidates
- // to resolve the module.
- // 2. A list of modules involved in a cycle with this module, which
- // is used to clean up failed cycles; this value only exists while
- // candidates are being populated.
- // 3. Boolean.TRUE indicating candidates for all requirements where
- // successfully calculated.
- // For case 1, rethrow the exception. For case 2, record the current
- // instigator module as being in a cycle with the target module and
- // return immediately. For case 3, simply return immediately.
- Object o = resultCache.get(module);
- if (o instanceof ResolveException)
+ // 1. A resolve exception if we've already attempted to populate the
+ // module's candidates but were unsuccessful.
+ // 2. Boolean.TRUE indicating we've already attempted to populate the
+ // module'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 3, simply return immediately.
+ // For case 3, this means we have a cycle so we should continue to populate
+ // the candidate 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 module.
+ Integer cycleCount = null;
+
+ // Keeps track of the candidates we've already calculated for the
+ // current module's requirements.
+ Map<Requirement, Set<Capability>> localCandidateMap = null;
+
+ // Keeps track of the current module's requirements for which we
+ // haven't yet found candidates.
+ List<Requirement> remainingReqs = null;
+
+ // Get the cache value for the current module.
+ Object cacheValue = resultCache.get(module);
+
+ // This is case 1.
+ if (cacheValue instanceof ResolveException)
{
- throw (ResolveException) o;
+ throw (ResolveException) cacheValue;
}
- else if (o instanceof List)
+ // This is case 2.
+ else if (cacheValue instanceof Boolean)
{
- if (!((List) o).contains(instigator))
- {
- ((List) o).add(instigator);
- }
return;
}
- else if (o != null)
+ // This is case 3.
+ else if (cacheValue != null)
{
- return;
+ cycleCount = (Integer) ((Object[]) cacheValue)[0];
+ ((Object[]) cacheValue)[0] = new Integer(cycleCount.intValue() + 1);
+ localCandidateMap = (Map) ((Object[]) cacheValue)[1];
+ remainingReqs = (List) ((Object[]) cacheValue)[2];
}
- // Verify that any required execution environment is satisfied.
- verifyExecutionEnvironment(fwkExecEnvStr, fwkExecEnvSet, module);
+ // If there is no cache value for the current module, 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))
+ {
+ // Verify that any required execution environment is satisfied.
+ verifyExecutionEnvironment(fwkExecEnvStr, fwkExecEnvSet, module);
+
+ // Verify that any native libraries match the current platform.
+ verifyNativeLibraries(module);
- // Verify that any native libraries match the current platform.
- verifyNativeLibraries(module);
+ // Record cycle count.
+ cycleCount = new Integer(0);
- // Add this module to cache map so we know we are in the middle
- // of calculating its candidates.
- resultCache.put(module, new ArrayList());
+ // Store candidates in a local map first, just in case the module
+ // is not resolvable.
+ localCandidateMap = new HashMap();
- // Store candidates in a local map first, just in case the module
- // is not resolvable.
- Map<Requirement, Set<Capability>> localCandidateMap = new HashMap();
+ // Create a modifiable list of the module's requirements.
+ remainingReqs = new ArrayList(module.getRequirements());
- // Find candidates for all requirements for the target module.
- List<Requirement> reqs = module.getRequirements();
- for (Requirement req : reqs)
+ // Add these value to the result cache so we know we are
+ // in the middle of populating candidates for the current
+ // module.
+ resultCache.put(
+ module, new Object[] { cycleCount, localCandidateMap, remainingReqs });
+ }
+
+ // If we have requirements remaining, then find candidates for them.
+ while (remainingReqs.size() > 0)
{
+ Requirement req = remainingReqs.remove(0);
+
+ // Get satisfying candidates and populate their candidates if necessary.
Set<Capability> candidates = state.getCandidates(module, req, true);
for (Iterator<Capability> itCandCap = candidates.iterator(); itCandCap.hasNext(); )
{
@@ -438,106 +472,47 @@
catch (ResolveException ex)
{
System.out.println("RE: Candidate not resolveable: " + ex);
+ // Remove the candidate since we weren't able to
+ // populate its candidates.
itCandCap.remove();
}
}
}
+
+ // If there are no candidates for the current requirement
+ // and it is not optional, then create, cache, and throw
+ // a resolve exception.
if ((candidates.size() == 0) && !req.isOptional())
{
- // Remove invalid candidate and any cycle byproduct resolved modules.
- removeInvalidCandidate(module, candidateMap, resultCache, new ArrayList());
-
ResolveException ex =
new ResolveException("Unable to resolve " + module
+ ": missing requirement " + req, module, req);
resultCache.put(module, ex);
-
throw ex;
}
+ // If we actually have candidates for the requirement, then
+ // add them to the local candidate map.
else if (candidates.size() > 0)
{
localCandidateMap.put(req, candidates);
}
}
- resultCache.put(module, Boolean.TRUE);
-
- // Put candidates for all requirements into the global candidate map.
- if (localCandidateMap.size() > 0)
- {
- candidateMap.putAll(localCandidateMap);
- }
- }
-
- private static void removeInvalidCandidate(
- Module invalidModule, Map<Requirement, Set<Capability>> candidatesMap,
- Map<Module, Object> resultCache, List<Module> invalidModules)
- {
- // Remove the invalid module's candidates from the candidates map,
- // since it should only contain entries for resolved modules.
- for (Requirement req : invalidModule.getRequirements())
+ // If we are exiting from a cycle then decrement
+ // cycle counter, otherwise record the result.
+ if (cycleCount.intValue() > 0)
{
- candidatesMap.remove(req);
+ ((Object[]) cacheValue)[0] = new Integer(cycleCount.intValue() + 1);
}
-
- // Remove the partial result from the result cache.
- Object o = resultCache.remove(invalidModule);
-
- // If the value in the result cache is a list, then there are modules
- // in a cyclical relationship with the invalid module that need to
- // be cleaned up.
- if (o instanceof List)
+ else if (cycleCount.intValue() == 0)
{
- // Loop through each cyclically dependent module.
- List<Module> dependents = (List<Module>) o;
- for (Module dep : dependents)
- {
- // Loop through its requirements to find any with the invalid
- // module as a candidate.
- for (Requirement req : dep.getRequirements())
- {
- Set<Capability> cands = candidatesMap.get(req);
- if (cands != null)
- {
- for (Iterator<Capability> itCandCaps = cands.iterator();
- itCandCaps.hasNext(); )
- {
- // If the candidate capability is from the invalid module,
- // then remove it from the available candidates.
- if (itCandCaps.next().getModule().equals(invalidModule))
- {
- itCandCaps.remove();
+ // Record that the module was successfully populated.
+ resultCache.put(module, Boolean.TRUE);
- // If there are no more candidates available, then
- // remove it from the candidates map.
- if (cands.size() == 0)
- {
- candidatesMap.remove(req);
-
- // If the requirement is not optional, then record
- // the dependent module as invalid.
- if (!req.isOptional() && (dep != invalidModule)
- && !invalidModules.contains(dep))
- {
- invalidModules.add(dep);
- }
- }
- break;
- }
- }
- }
- }
- }
- }
-
- // If more modules were invalidated, then recursively clean them
- // up as well.
- if (!invalidModules.isEmpty())
- {
- while (!invalidModules.isEmpty())
+ // Merge local candidate map into global candidate map.
+ if (localCandidateMap.size() > 0)
{
- Module m = invalidModules.remove(0);
- removeInvalidCandidate(m, candidatesMap, resultCache, invalidModules);
+ candidateMap.putAll(localCandidateMap);
}
}
}
@@ -580,8 +555,8 @@
}
// TODO: FELIX3 - Since we reuse the same dynamic requirement, is it possible
-// that some sort of cycle could cause us to try try to match another
-// set of candidates to the same requirement?
+// that some sort of cycle could cause us to try to match another set
+// of candidates to the same requirement?
if (candidates.size() == 0)
{
candidateMap.remove(dynReq);