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);