The case of dynamically importing from a fragment was not correctly
handled when we previously refactored the code so that we don't
synthesize capabilities for attached fragments on the host. I refactored
this code to avoid duplication and to ensure that the normal path
and the dynamic import path are resolved the same way. (FELIX-2950)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1172688 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java b/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
index f26181b..1bf0512 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
@@ -160,7 +160,7 @@
// However, for on-demand fragments only populate if their host
// is already populated.
if ((resolution != ON_DEMAND)
- || (isFragment && populateFragment(state, revision)))
+ || (isFragment && populateFragmentOndemand(state, revision)))
{
if (resolution == MANDATORY)
{
@@ -284,96 +284,11 @@
continue;
}
- // Get satisfying candidates and populate their candidates if necessary.
- ResolveException rethrow = null;
+ // Process the candidates, removing any candidates that
+ // cannot resolve.
SortedSet<BundleCapability> candidates =
state.getCandidates((BundleRequirementImpl) req, true);
- Set<BundleCapability> fragmentCands = null;
- 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)
- {
- if (fragmentCands == null)
- {
- fragmentCands = new HashSet<BundleCapability>();
- }
- 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
- // revision is not already resolved and is not the current version
- // we are trying to populate, then populate the candidates for
- // its dependencies as well.
- // NOTE: Technically, we don't have to check to see if the
- // candidate revision is equal to the current revision, but this
- // saves us from recursing and also simplifies exceptions messages
- // 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 (isFragment
- || ((candCap.getRevision().getWiring() == null)
- && !candCap.getRevision().equals(revision)))
- {
- try
- {
- populateRevision(state, candCap.getRevision());
- }
- catch (ResolveException ex)
- {
- if (rethrow == null)
- {
- rethrow = ex;
- }
- // Remove the candidate since we weren't able to
- // populate its candidates.
- itCandCap.remove();
- }
- }
- }
-
- // 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 != null)
- {
- 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));
- }
- }
- }
- }
- }
+ ResolveException rethrow = processCandidates(state, revision, candidates);
// If there are no candidates for the current requirement
// and it is not optional, then create, cache, and throw
@@ -420,7 +335,7 @@
}
}
- private boolean populateFragment(ResolverState state, BundleRevision revision)
+ private boolean populateFragmentOndemand(ResolverState state, BundleRevision revision)
throws ResolveException
{
// Create a modifiable list of the revision's requirements.
@@ -480,19 +395,6 @@
return true;
}
- public boolean isPopulated(BundleRevision revision)
- {
- Object value = m_populateResultCache.get(revision);
- return ((value != null) && (value instanceof Boolean));
- }
-
- public ResolveException getResolveException(BundleRevision revision)
- {
- Object value = m_populateResultCache.get(revision);
- return ((value != null) && (value instanceof ResolveException))
- ? (ResolveException) value : null;
- }
-
public void populateDynamic(
ResolverState state, BundleRevision revision,
BundleRequirement req, SortedSet<BundleCapability> candidates)
@@ -504,28 +406,9 @@
// Add the dynamic imports candidates.
add(req, candidates);
- // Populate the candidates for the dynamic import.
- ResolveException rethrow = null;
- for (Iterator<BundleCapability> itCandCap = candidates.iterator();
- itCandCap.hasNext(); )
- {
- BundleCapability candCap = itCandCap.next();
- if (candCap.getRevision().getWiring() == null)
- {
- try
- {
- populateRevision(state, candCap.getRevision());
- }
- catch (ResolveException ex)
- {
- if (rethrow == null)
- {
- rethrow = ex;
- }
- itCandCap.remove();
- }
- }
- }
+ // Process the candidates, removing any candidates that
+ // cannot resolve.
+ ResolveException rethrow = processCandidates(state, revision, candidates);
if (candidates.isEmpty())
{
@@ -540,6 +423,127 @@
}
/**
+ * 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 state the resolver state.
+ * @param revision the revision being resolved.
+ * @param candidates the candidates to process.
+ * @return a resolve exception to be re-thrown, if any, or null.
+ */
+ private ResolveException processCandidates(
+ ResolverState state,
+ BundleRevision revision,
+ SortedSet<BundleCapability> candidates)
+ {
+ // Get satisfying candidates and populate their candidates if necessary.
+ ResolveException rethrow = null;
+ Set<BundleCapability> fragmentCands = null;
+ 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)
+ {
+ if (fragmentCands == null)
+ {
+ fragmentCands = new HashSet<BundleCapability>();
+ }
+ 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
+ // revision is not already resolved and is not the current version
+ // we are trying to populate, then populate the candidates for
+ // its dependencies as well.
+ // NOTE: Technically, we don't have to check to see if the
+ // candidate revision is equal to the current revision, but this
+ // saves us from recursing and also simplifies exceptions messages
+ // 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 (isFragment
+ || ((candCap.getRevision().getWiring() == null)
+ && !candCap.getRevision().equals(revision)))
+ {
+ try
+ {
+ populateRevision(state, candCap.getRevision());
+ }
+ catch (ResolveException ex)
+ {
+ if (rethrow == null)
+ {
+ rethrow = ex;
+ }
+ // Remove the candidate since we weren't able to
+ // populate its candidates.
+ itCandCap.remove();
+ }
+ }
+ }
+
+ // 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 != null)
+ {
+ 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));
+ }
+ }
+ }
+ }
+ }
+
+ return rethrow;
+ }
+
+ public boolean isPopulated(BundleRevision revision)
+ {
+ Object value = m_populateResultCache.get(revision);
+ return ((value != null) && (value instanceof Boolean));
+ }
+
+ public ResolveException getResolveException(BundleRevision revision)
+ {
+ Object value = m_populateResultCache.get(revision);
+ return ((value != null) && (value instanceof ResolveException))
+ ? (ResolveException) value : null;
+ }
+
+ /**
* Adds a requirement and its matching candidates to the internal data
* structure. This method assumes it owns the data being passed in and
* does not make a copy. It takes the data and processes, such as calculating
@@ -614,37 +618,11 @@
**/
public void prepare()
{
- boolean init = false;
-
if (m_fragmentsPresent)
{
populateDependents();
- init = true;
}
- // If the root is a singleton, then prefer it over any other singleton.
-// TODO: OSGi R4.3/SINGLETON - How do we prefer the root as a singleton?
-/*
- if ((m_root != null) && isSingleton(m_root))
- {
- BundleRevision singleton = singletons.get(m_root.getSymbolicName());
- singletons.put(m_root.getSymbolicName(), m_root);
- if ((singleton != null) && !singleton.equals(m_root))
- {
- if (singleton.getWiring() != null)
- {
- throw new ResolveException(
- "Cannot resolve singleton "
- + m_root
- + " because "
- + singleton
- + " singleton is already resolved.",
- m_root, null);
- }
- removeRevision(singleton);
- }
- }
-*/
// This method performs the following steps:
// 1. Select the fragments to attach to a given host.
// 2. Wrap hosts and attach fragments.