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 2011/07/18 20:43:23 UTC

svn commit: r1148001 - in /felix/trunk/framework/src/main/java/org/apache/felix/framework: StatefulResolver.java resolver/Candidates.java

Author: rickhall
Date: Mon Jul 18 18:43:23 2011
New Revision: 1148001

URL: http://svn.apache.org/viewvc?rev=1148001&view=rev
Log:
Need to deal with the fact that hosted fragment capabilities are no longer
wrapped. This impacts indexing of capabilities and also impacts finding
candidates. For the former, we only index fragment capabilities and ignore
them from the host. For the latter, when we come across a candidate from
a fragment we also insert synthesized candidates for any hosts to which
the fragment is attached. (FELIX-2950)

Modified:
    felix/trunk/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java?rev=1148001&r1=1148000&r2=1148001&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java Mon Jul 18 18:43:23 2011
@@ -842,9 +842,6 @@ class StatefulResolver
                                 rte.getMessage(), ex2);
                             throw rte;
                         }
-
-                        // Reindex host with no fragments.
-                        m_resolverState.addRevision(revision);
                     }
 
                     ResolveException re = new ResolveException(
@@ -870,12 +867,9 @@ class StatefulResolver
                 // Update resolver state to remove substituted capabilities.
                 if (!Util.isFragment(revision))
                 {
-                    // Update resolver state by reindexing host with attached
-                    // fragments and removing any substituted exports.
-// TODO: OSGi R4.3 - We could avoid reindexing for fragments if we check it
-//       the revision has fragments or not.
+                    // Reindex the revision's capabilities since its resolved
+                    // capabilities could be different than its declared ones.
                     m_resolverState.addRevision(revision);
-                    m_resolverState.removeSubstitutedCapabilities(revision);
                 }
 
                 // Update the state of the revision's bundle to resolved as well.
@@ -1079,6 +1073,14 @@ class StatefulResolver
 
         synchronized void addRevision(BundleRevision br)
         {
+            // Always attempt to remove the revision, since
+            // this method can be used for re-indexing a revision
+            // after it has been resolved.
+            removeRevision(br);
+
+            // Add the revision and index its declared or resolved
+            // capabilities depending on whether it is resolved or
+            // not.
             m_revisions.add(br);
             List<BundleCapability> caps = (br.getWiring() == null)
                 ? br.getDeclaredCapabilities(null)
@@ -1087,13 +1089,19 @@ class StatefulResolver
             {
                 for (BundleCapability cap : caps)
                 {
-                    CapabilitySet capSet = m_capSets.get(cap.getNamespace());
-                    if (capSet == null)
+                    // If the capability is from a different revision, then
+                    // don't index it since it is a capability from a fragment.
+                    // In that case, the fragment capability is still indexed.
+                    if (cap.getRevision() == br)
                     {
-                        capSet = new CapabilitySet(null, true);
-                        m_capSets.put(cap.getNamespace(), capSet);
+                        CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+                        if (capSet == null)
+                        {
+                            capSet = new CapabilitySet(null, true);
+                            m_capSets.put(cap.getNamespace(), capSet);
+                        }
+                        capSet.addCapability(cap);
                     }
-                    capSet.addCapability(cap);
                 }
             }
 
@@ -1105,25 +1113,27 @@ class StatefulResolver
 
         synchronized void removeRevision(BundleRevision br)
         {
-            m_revisions.remove(br);
-            List<BundleCapability> caps = (br.getWiring() == null)
-                ? br.getDeclaredCapabilities(null)
-                : br.getWiring().getCapabilities(null);
-            if (caps != null)
+            if (m_revisions.remove(br))
             {
-                for (BundleCapability cap : caps)
+                // We only need be concerned with declared capabilities here,
+                // because resolved capabilities will be a subset.
+                List<BundleCapability> caps = br.getDeclaredCapabilities(null);
+                if (caps != null)
                 {
-                    CapabilitySet capSet = m_capSets.get(cap.getNamespace());
-                    if (capSet != null)
+                    for (BundleCapability cap : caps)
                     {
-                        capSet.removeCapability(cap);
+                        CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+                        if (capSet != null)
+                        {
+                            capSet.removeCapability(cap);
+                        }
                     }
                 }
-            }
 
-            if (Util.isFragment(br))
-            {
-                m_fragments.remove(br);
+                if (Util.isFragment(br))
+                {
+                    m_fragments.remove(br);
+                }
             }
         }
 
@@ -1132,36 +1142,6 @@ class StatefulResolver
             return new HashSet(m_fragments);
         }
 
-// TODO: OSGi R4.3 - This will need to be changed once BundleWiring.getCapabilities()
-//       is correctly implemented, since it already has to remove substituted caps.
-        synchronized void removeSubstitutedCapabilities(BundleRevision br)
-        {
-            if (br.getWiring() != null)
-            {
-                // Loop through the revision's package wires and determine if any
-                // of them overlap any of the packages exported by the revision.
-                // If so, then the framework must have chosen to have the revision
-                // import rather than export the package, so we need to remove the
-                // corresponding package capability from the package capability set.
-                for (BundleWire w : br.getWiring().getRequiredWires(null))
-                {
-                    if (w.getCapability().getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
-                    {
-                        for (BundleCapability cap : br.getWiring().getCapabilities(null))
-                        {
-                            if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
-                                && w.getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
-                                    .equals(cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)))
-                            {
-                                m_capSets.get(BundleRevision.PACKAGE_NAMESPACE).removeCapability(cap);
-                                break;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
         //
         // ResolverState methods.
         //

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java?rev=1148001&r1=1148000&r2=1148001&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java Mon Jul 18 18:43:23 2011
@@ -33,12 +33,15 @@ import java.util.TreeSet;
 import org.apache.felix.framework.BundleRevisionImpl;
 import org.apache.felix.framework.resolver.Resolver.ResolverState;
 import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
 import org.apache.felix.framework.wiring.BundleRequirementImpl;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
 import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRequirement;
 import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
 
 class Candidates
 {
@@ -207,11 +210,22 @@ class Candidates
             ResolveException rethrow = null;
             SortedSet<BundleCapability> candidates =
                 state.getCandidates((BundleRequirementImpl) req, true);
+            Set<BundleCapability> fragmentCands = new HashSet();
             for (Iterator<BundleCapability> itCandCap = candidates.iterator();
                 itCandCap.hasNext(); )
             {
                 BundleCapability candCap = itCandCap.next();
 
+                boolean isFragment = Util.isFragment(candCap.getRevision());
+
+                // If the capability is from a fragment, then record it
+                // because we have to insert associated host capabilities
+                // if the fragment is already attached to any hosts.
+                if (isFragment)
+                {
+                    fragmentCands.add(candCap);
+                }
+
                 // If the candidate revision is a fragment, then always attempt
                 // to populate candidates for its dependency, since it must be
                 // attached to a host to be used. Otherwise, if the candidate
@@ -224,7 +238,7 @@ class Candidates
                 // since we effectively chain exception messages for each level
                 // of recursion; thus, any avoided recursion results in fewer
                 // exceptions to chain when an error does occur.
-                if (Util.isFragment(candCap.getRevision())
+                if (isFragment
                     || ((candCap.getRevision().getWiring() == null)
                         && !candCap.getRevision().equals(revision)))
                 {
@@ -245,6 +259,40 @@ class Candidates
                 }
             }
 
+            // If any of the candidates for the requirement were from a fragment,
+            // then also insert synthesized hosted capabilities for any other host
+            // to which the fragment is attached since they are all effectively
+            // unique capabilities.
+            if (!fragmentCands.isEmpty())
+            {
+                for (BundleCapability fragCand : fragmentCands)
+                {
+                    // Only necessary for resolved fragments.
+                    BundleWiring wiring = fragCand.getRevision().getWiring();
+                    if (wiring != null)
+                    {
+                        // Fragments only have host wire, so each wire represents
+                        // an attached host.
+                        for (BundleWire wire : wiring.getRequiredWires(null))
+                        {
+                            // If the capability is a package, then make sure the
+                            // host actually provides it in its resolved capabilities,
+                            // since it may be a substitutable export.
+                            if (!fragCand.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                                || wire.getProviderWiring().getCapabilities(null).contains(fragCand))
+                            {
+                                // Note that we can just add this as a candidate
+                                // directly, since we know it is already resolved.
+                                candidates.add(
+                                    new HostedCapability(
+                                        wire.getCapability().getRevision(),
+                                        (BundleCapabilityImpl) fragCand));
+                            }
+                        }
+                    }
+                }
+            }
+
             // If there are no candidates for the current requirement
             // and it is not optional, then create, cache, and throw
             // a resolve exception.
@@ -296,6 +344,20 @@ class Candidates
     public final boolean populate(
         ResolverState state, BundleRevision revision, boolean isGreedyAttach)
     {
+        // Get the current result cache value, to make sure the revision
+        // hasn't already been populated.
+        Object cacheValue = m_populateResultCache.get(revision);
+        // Has been unsuccessfully populated.
+        if (cacheValue instanceof ResolveException)
+        {
+            return false;
+        }
+        // Has been successfully populated.
+        else if (cacheValue instanceof Boolean)
+        {
+            return true;
+        }
+
         // We will always attempt to populate fragments, since this is necessary
         // for greedy attaching of fragment. However, we'll only attempt to
         // populate optional non-fragment revisions if they aren't already
@@ -316,76 +378,70 @@ class Candidates
             // lookup again in populate().
             if (isGreedyAttach && isFragment)
             {
-                // Get the current result cache value, to make sure the revision
-                // hasn't already been populated.
-                Object cacheValue = m_populateResultCache.get(revision);
-                if (cacheValue == null)
-                {
-                    // Create a modifiable list of the revision's requirements.
-                    List<BundleRequirement> remainingReqs =
-                        new ArrayList(revision.getDeclaredRequirements(null));
-
-                    // Find the host requirement.
-                    BundleRequirement hostReq = null;
-                    for (Iterator<BundleRequirement> it = remainingReqs.iterator();
-                        it.hasNext(); )
-                    {
-                        BundleRequirement r = it.next();
-                        if (r.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
-                        {
-                            hostReq = r;
-                            it.remove();
-                            break;
-                        }
-                    }
-
-                    // Get candidates hosts and keep any that have been populated.
-                    SortedSet<BundleCapability> hosts =
-                        state.getCandidates((BundleRequirementImpl) hostReq, false);
-                    for (Iterator<BundleCapability> it = hosts.iterator(); it.hasNext(); )
+                // Create a modifiable list of the revision's requirements.
+                List<BundleRequirement> remainingReqs =
+                    new ArrayList(revision.getDeclaredRequirements(null));
+
+                // Find the host requirement.
+                BundleRequirement hostReq = null;
+                for (Iterator<BundleRequirement> it = remainingReqs.iterator();
+                    it.hasNext(); )
+                {
+                    BundleRequirement r = it.next();
+                    if (r.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
                     {
-                        BundleCapability host = it.next();
-                        if (!isPopulated(host.getRevision()))
-                        {
-                            it.remove();
-                        }
+                        hostReq = r;
+                        it.remove();
+                        break;
                     }
+                }
 
-                    // If there aren't any populated hosts, then we can just
-                    // return since this fragment isn't needed.
-                    if (hosts.isEmpty())
+                // Get candidates hosts and keep any that have been populated.
+                SortedSet<BundleCapability> hosts =
+                    state.getCandidates((BundleRequirementImpl) hostReq, false);
+                for (Iterator<BundleCapability> it = hosts.iterator(); it.hasNext(); )
+                {
+                    BundleCapability host = it.next();
+                    if (!isPopulated(host.getRevision()))
                     {
-                        return false;
+                        it.remove();
                     }
+                }
 
-                    // If there are populates host candidates, then finish up
-                    // some other checks and prepopulate the result cache with
-                    // the work we've done so far.
-
-                    // Verify that any required execution environment is satisfied.
-                    state.checkExecutionEnvironment(revision);
-
-                    // Verify that any native libraries match the current platform.
-                    state.checkNativeLibraries(revision);
-
-                    // 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<BundleRequirement, SortedSet<BundleCapability>> localCandidateMap =
-                        new HashMap<BundleRequirement, SortedSet<BundleCapability>>();
-
-                    // 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(revision,
-                        new Object[] { cycleCount, localCandidateMap, remainingReqs });
+                // 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 populates host candidates, then finish up
+                // some other checks and prepopulate the result cache with
+                // the work we've done so far.
+
+                // Verify that any required execution environment is satisfied.
+                state.checkExecutionEnvironment(revision);
+
+                // Verify that any native libraries match the current platform.
+                state.checkNativeLibraries(revision);
+
+                // 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<BundleRequirement, SortedSet<BundleCapability>> localCandidateMap =
+                    new HashMap<BundleRequirement, SortedSet<BundleCapability>>();
+
+                // 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(revision,
+                    new Object[] { cycleCount, localCandidateMap, remainingReqs });
             }
 
             // Try to populate candidates for the optional revision.