Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Refactor internal resolver APIs to align with upcoming OSGi resolver …

…spec. (FELIX-3394)

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1300618 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
commit f9e138368407d0fff7305e63d7f7e8ef585cbec6 1 parent 955c921
Richard S. Hall authored
Showing with 1,259 additions and 836 deletions.
  1. +3 −3 framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
  2. +1 −1  framework/src/main/java/org/apache/felix/framework/Felix.java
  3. +111 −0 framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java
  4. +529 −551 framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
  5. +125 −68 framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
  6. +15 −111 framework/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java
  7. +52 −0 framework/src/main/java/org/apache/felix/framework/resolver/ResolveContext.java
  8. +2 −16 framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
  9. +76 −77 framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
  10. +157 −0 framework/src/main/java/org/apache/felix/framework/resolver/ShadowList.java
  11. +60 −0 framework/src/main/java/org/apache/felix/framework/resolver/SimpleHostedCapability.java
  12. +119 −0 framework/src/main/java/org/apache/felix/framework/resolver/WrappedCapability.java
  13. +3 −3 ...k/src/main/java/org/apache/felix/framework/resolver/{HostedRequirement.java → WrappedRequirement.java}
  14. +6 −6 ...ork/src/main/java/org/apache/felix/framework/resolver/{HostBundleRevision.java → WrappedRevision.java}
6 framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
View
@@ -1928,7 +1928,7 @@ protected Class findClass(String name) throws ClassNotFoundException
{
try
{
- m_revision.getSecureAction()
+ BundleRevisionImpl.getSecureAction()
.invokeWeavingHook(wh, wci);
}
catch (Throwable th)
@@ -2531,7 +2531,7 @@ private static String diagnoseClassLoadError(
BundleRevision.PACKAGE_NAMESPACE, (Object) pkgName);
BundleRequirementImpl req = new BundleRequirementImpl(
revision, BundleRevision.PACKAGE_NAMESPACE, dirs, attrs);
- Set<BundleCapability> exporters = resolver.getCandidates(req, false);
+ List<BundleCapability> exporters = resolver.findProviders(req, false);
BundleRevision provider = null;
try
@@ -2570,7 +2570,7 @@ private static String diagnoseClassLoadError(
BundleRevision.PACKAGE_NAMESPACE, (Object) pkgName);
BundleRequirementImpl req = new BundleRequirementImpl(
revision, BundleRevision.PACKAGE_NAMESPACE, dirs, attrs);
- Set<BundleCapability> exports = resolver.getCandidates(req, false);
+ List<BundleCapability> exports = resolver.findProviders(req, false);
if (exports.size() > 0)
{
boolean classpath = false;
2  framework/src/main/java/org/apache/felix/framework/Felix.java
View
@@ -3564,7 +3564,7 @@ Bundle getBundle(Class clazz)
BundleRevision.PACKAGE_NAMESPACE,
Collections.EMPTY_MAP,
attrs);
- Set<BundleCapability> exports = m_resolver.getCandidates(req, false);
+ List<BundleCapability> exports = m_resolver.findProviders(req, false);
// We only want resolved capabilities.
for (Iterator<BundleCapability> it = exports.iterator(); it.hasNext(); )
111 framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java
View
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.felix.framework.resolver.CandidateComparator;
+import org.apache.felix.framework.resolver.HostedCapability;
+import org.apache.felix.framework.resolver.ResolveContext;
+import org.apache.felix.framework.resolver.ResolveException;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+
+/**
+ *
+ * @author rickhall
+ */
+public class ResolveContextImpl extends ResolveContext
+{
+ private final StatefulResolver m_state;
+ private final Map<BundleRevision, BundleWiring> m_wirings;
+ private final Collection<BundleRevision> m_mandatory;
+ private final Collection<BundleRevision> m_optional;
+ private final Collection<BundleRevision> m_ondemand;
+
+ ResolveContextImpl(
+ StatefulResolver state, Map<BundleRevision, BundleWiring> wirings,
+ Collection<BundleRevision> mandatory, Collection<BundleRevision> optional,
+ Collection<BundleRevision> ondemand)
+ {
+ m_state = state;
+ m_wirings = wirings;
+ m_mandatory = mandatory;
+ m_optional = optional;
+ m_ondemand = ondemand;
+ }
+
+ @Override
+ public Collection<BundleRevision> getMandatoryRevisions()
+ {
+ return new ArrayList<BundleRevision>(m_mandatory);
+ }
+
+ @Override
+ public Collection<BundleRevision> getOptionalRevisions()
+ {
+ return new ArrayList<BundleRevision>(m_optional);
+ }
+
+ public Collection<BundleRevision> getOndemandRevisions()
+ {
+ return new ArrayList<BundleRevision>(m_ondemand);
+ }
+
+ public List<BundleCapability> findProviders(BundleRequirement br, boolean obeyMandatory)
+ {
+ return m_state.findProviders(br, obeyMandatory);
+ }
+
+ public int insertHostedCapability(List<BundleCapability> caps, HostedCapability hc)
+ {
+ int idx = Collections.binarySearch(caps, hc, new CandidateComparator());
+ if (idx < 0)
+ {
+ idx = Math.abs(idx + 1);
+ }
+ caps.add(idx, hc);
+ return idx;
+ }
+
+ public boolean isEffective(BundleRequirement br)
+ {
+ return m_state.isEffective(br);
+ }
+
+ public Map<BundleRevision, BundleWiring> getWirings()
+ {
+ return m_wirings;
+ }
+
+ public void checkExecutionEnvironment(BundleRevision rev) throws ResolveException
+ {
+ m_state.checkExecutionEnvironment(rev);
+ }
+
+ public void checkNativeLibraries(BundleRevision rev) throws ResolveException
+ {
+ m_state.checkNativeLibraries(rev);
+ }
+}
1,080 framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
View
@@ -28,9 +28,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.SortedSet;
import java.util.StringTokenizer;
-import java.util.TreeSet;
import org.apache.felix.framework.capabilityset.CapabilitySet;
import org.apache.felix.framework.capabilityset.SimpleFilter;
import org.apache.felix.framework.resolver.CandidateComparator;
@@ -64,33 +62,265 @@
private final Logger m_logger;
private final Felix m_felix;
private final Resolver m_resolver;
- private final ResolverStateImpl m_resolverState;
private final List<ResolverHook> m_hooks = new ArrayList<ResolverHook>();
private boolean m_isResolving = false;
private Collection<BundleRevision> m_whitelist = null;
+ // Set of all revisions.
+ private final Set<BundleRevision> m_revisions;
+ // Set of all fragments.
+ private final Set<BundleRevision> m_fragments;
+ // Capability sets.
+ private final Map<String, CapabilitySet> m_capSets;
+ // Maps singleton symbolic names to list of bundle revisions sorted by version.
+ private final Map<String, List<BundleRevision>> m_singletons;
+ // Selected singleton bundle revisions.
+ private final Set<BundleRevision> m_selectedSingletons;
+ // Execution environment.
+ private final String m_fwkExecEnvStr;
+ // Parsed framework environments
+ private final Set<String> m_fwkExecEnvSet;
+
StatefulResolver(Felix felix)
{
m_felix = felix;
m_logger = m_felix.getLogger();
m_resolver = new ResolverImpl(m_logger);
- m_resolverState = new ResolverStateImpl(
- (String) m_felix.getConfig().get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT));
+
+ m_revisions = new HashSet<BundleRevision>();
+ m_fragments = new HashSet<BundleRevision>();
+ m_capSets = new HashMap<String, CapabilitySet>();
+ m_singletons = new HashMap<String, List<BundleRevision>>();
+ m_selectedSingletons = new HashSet<BundleRevision>();
+
+ String fwkExecEnvStr =
+ (String) m_felix.getConfig().get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
+ m_fwkExecEnvStr = (fwkExecEnvStr != null) ? fwkExecEnvStr.trim() : null;
+ m_fwkExecEnvSet = parseExecutionEnvironments(fwkExecEnvStr);
+
+ List<String> indices = new ArrayList<String>();
+ indices.add(BundleRevision.BUNDLE_NAMESPACE);
+ m_capSets.put(BundleRevision.BUNDLE_NAMESPACE, new CapabilitySet(indices, true));
+
+ indices = new ArrayList<String>();
+ indices.add(BundleRevision.PACKAGE_NAMESPACE);
+ m_capSets.put(BundleRevision.PACKAGE_NAMESPACE, new CapabilitySet(indices, true));
+
+ indices = new ArrayList<String>();
+ indices.add(BundleRevision.HOST_NAMESPACE);
+ m_capSets.put(BundleRevision.HOST_NAMESPACE, new CapabilitySet(indices, true));
}
- void addRevision(BundleRevision br)
+ synchronized void addRevision(BundleRevision br)
{
- m_resolverState.addRevision(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);
+
+ m_revisions.add(br);
+
+ // Add singletons to the singleton map.
+ boolean isSingleton = Util.isSingleton(br);
+ if (isSingleton)
+ {
+ // Index the new singleton.
+ addToSingletonMap(m_singletons, br);
+ }
+
+ // We always need to index non-singleton bundle capabilities, but
+ // singleton bundles only need to be index if they are resolved.
+ // Unresolved singleton capabilities are only indexed before a
+ // resolve operation when singleton selection is performed.
+ if (!isSingleton || (br.getWiring() != null))
+ {
+ if (Util.isFragment(br))
+ {
+ m_fragments.add(br);
+ }
+ indexCapabilities(br);
+ }
}
- void removeRevision(BundleRevision br)
+ synchronized void removeRevision(BundleRevision br)
{
- m_resolverState.removeRevision(br);
+ if (m_revisions.remove(br))
+ {
+ m_fragments.remove(br);
+ deindexCapabilities(br);
+
+ // If this module is a singleton, then remove it from the
+ // singleton map.
+ List<BundleRevision> revisions = m_singletons.get(br.getSymbolicName());
+ if (revisions != null)
+ {
+ revisions.remove(br);
+ if (revisions.isEmpty())
+ {
+ m_singletons.remove(br.getSymbolicName());
+ }
+ }
+ }
}
- Set<BundleCapability> getCandidates(BundleRequirementImpl req, boolean obeyMandatory)
+ boolean isEffective(BundleRequirement req)
{
- return m_resolverState.getCandidates(req, obeyMandatory);
+ String effective = req.getDirectives().get(Constants.EFFECTIVE_DIRECTIVE);
+ return ((effective == null) || effective.equals(Constants.EFFECTIVE_RESOLVE));
+ }
+
+ synchronized List<BundleCapability> findProviders(
+ BundleRequirement req, boolean obeyMandatory)
+ {
+ BundleRevisionImpl reqRevision = (BundleRevisionImpl) req.getRevision();
+ List<BundleCapability> result = new ArrayList<BundleCapability>();
+
+ CapabilitySet capSet = m_capSets.get(req.getNamespace());
+ if (capSet != null)
+ {
+ // Get the requirement's filter; if this is our own impl we
+ // have a shortcut to get the already parsed filter, otherwise
+ // we must parse it from the directive.
+ SimpleFilter sf = null;
+ if (req instanceof BundleRequirementImpl)
+ {
+ sf = ((BundleRequirementImpl) req).getFilter();
+ }
+ else
+ {
+ String filter = req.getDirectives().get(Constants.FILTER_DIRECTIVE);
+ if (filter == null)
+ {
+ sf = new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+ }
+ else
+ {
+ sf = SimpleFilter.parse(filter);
+ }
+ }
+
+ // Find the matching candidates.
+ Set<BundleCapability> matches = capSet.match(sf, obeyMandatory);
+ // Filter matching candidates.
+ for (BundleCapability cap : matches)
+ {
+ // Filter according to security.
+ if (filteredBySecurity(req, cap))
+ {
+ continue;
+ }
+ // Filter already resolved hosts, since we don't support
+ // dynamic attachment of fragments.
+ if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE)
+ && (cap.getRevision().getWiring() != null))
+ {
+ continue;
+ }
+
+ result.add(cap);
+ }
+ }
+
+ // If we have resolver hooks, then we may need to filter our results
+ // based on a whitelist and/or fine-grained candidate filtering.
+ if (!result.isEmpty() && !m_hooks.isEmpty())
+ {
+ // It we have a whitelist, then first filter out candidates
+ // from disallowed revisions.
+ if (m_whitelist != null)
+ {
+ for (Iterator<BundleCapability> it = result.iterator(); it.hasNext(); )
+ {
+ if (!m_whitelist.contains(it.next().getRevision()))
+ {
+ it.remove();
+ }
+ }
+ }
+
+ // Now give the hooks a chance to do fine-grained filtering.
+ ShrinkableCollection<BundleCapability> shrinkable =
+ new ShrinkableCollection<BundleCapability>(result);
+ for (ResolverHook hook : m_hooks)
+ {
+ try
+ {
+ Felix.m_secureAction
+ .invokeResolverHookMatches(hook, req, shrinkable);
+ }
+ catch (Throwable th)
+ {
+ m_logger.log(Logger.LOG_WARNING, "Resolver hook exception.", th);
+ }
+ }
+ }
+
+ Collections.sort(result, new CandidateComparator());
+
+ return result;
+ }
+
+ private boolean filteredBySecurity(BundleRequirement req, BundleCapability cap)
+ {
+ if (System.getSecurityManager() != null)
+ {
+ BundleRevisionImpl reqRevision = (BundleRevisionImpl) req.getRevision();
+
+ if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+ {
+ if (!((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
+ new PackagePermission((String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
+ PackagePermission.EXPORTONLY)) ||
+ !((reqRevision == null) ||
+ ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
+ new PackagePermission((String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
+ cap.getRevision().getBundle(),PackagePermission.IMPORT))
+ ))
+ {
+ if (reqRevision != cap.getRevision())
+ {
+ return true;
+ }
+ }
+ }
+ else if (req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
+ { if (!((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
+ new BundlePermission(cap.getRevision().getSymbolicName(), BundlePermission.PROVIDE)) ||
+ !((reqRevision == null) ||
+ ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
+ new BundlePermission(reqRevision.getSymbolicName(), BundlePermission.REQUIRE))
+ ))
+ {
+ return true;
+ }
+ }
+ else if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
+ {
+ if (!((BundleProtectionDomain) reqRevision.getProtectionDomain())
+ .impliesDirect(new BundlePermission(
+ reqRevision.getSymbolicName(),
+ BundlePermission.FRAGMENT))
+ || !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain())
+ .impliesDirect(new BundlePermission(
+ cap.getRevision().getSymbolicName(),
+ BundlePermission.HOST)))
+ {
+ return true;
+ }
+ }
+ else if (!req.getNamespace().equals("osgi.ee"))
+ {
+ if (!((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
+ new CapabilityPermission(req.getNamespace(), CapabilityPermission.PROVIDE))
+ ||
+ !((reqRevision == null) || ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
+ new CapabilityPermission(req.getNamespace(), cap.getAttributes(), cap.getRevision().getBundle(), CapabilityPermission.REQUIRE))))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
}
void resolve(
@@ -129,7 +359,7 @@ void resolve(
prepareResolverHooks(mandatory, optional);
// Select any singletons in the resolver state.
- m_resolverState.selectSingletons();
+ selectSingletons();
// Extensions are resolved differently.
for (Iterator<BundleRevision> it = mandatory.iterator(); it.hasNext(); )
@@ -140,7 +370,7 @@ void resolve(
{
it.remove();
}
- else if (Util.isSingleton(br) && !m_resolverState.isSelectedSingleton(br))
+ else if (Util.isSingleton(br) && !isSelectedSingleton(br))
{
throw new ResolveException("Singleton conflict.", br, null);
}
@@ -153,7 +383,7 @@ else if (Util.isSingleton(br) && !m_resolverState.isSelectedSingleton(br))
{
it.remove();
}
- else if (Util.isSingleton(br) && !m_resolverState.isSelectedSingleton(br))
+ else if (Util.isSingleton(br) && !isSelectedSingleton(br))
{
it.remove();
}
@@ -166,10 +396,12 @@ else if (Util.isSingleton(br) && !m_resolverState.isSelectedSingleton(br))
{
// Resolve the revision.
wireMap = m_resolver.resolve(
- m_resolverState,
- mandatory,
- optional,
- m_resolverState.getFragments());
+ new ResolveContextImpl(
+ this,
+ getWirings(),
+ mandatory,
+ optional,
+ getFragments()));
}
catch (ResolveException ex)
{
@@ -249,7 +481,7 @@ BundleRevision resolve(BundleRevision revision, String pkgName)
Collections.singleton(revision), Collections.EMPTY_SET);
// Select any singletons in the resolver state.
- m_resolverState.selectSingletons();
+ selectSingletons();
// Catch any resolve exception to rethrow later because
// we may need to call end() on resolver hooks.
@@ -257,8 +489,13 @@ BundleRevision resolve(BundleRevision revision, String pkgName)
try
{
wireMap = m_resolver.resolve(
- m_resolverState, revision, pkgName,
- m_resolverState.getFragments());
+ new ResolveContextImpl(
+ this,
+ getWirings(),
+ Collections.EMPTY_LIST,
+ Collections.EMPTY_LIST,
+ getFragments()),
+ revision, pkgName);
}
catch (ResolveException ex)
{
@@ -373,9 +610,7 @@ BundleRevision resolve(BundleRevision revision, String pkgName)
}
// Ask hooks to indicate which revisions should not be resolved.
- m_whitelist =
- new ShrinkableCollection<BundleRevision>(
- m_resolverState.getUnresolvedRevisions());
+ m_whitelist = new ShrinkableCollection<BundleRevision>(getUnresolvedRevisions());
int originalSize = m_whitelist.size();
for (ResolverHook hook : m_hooks)
{
@@ -518,7 +753,7 @@ boolean isAllowedDynamicImport(BundleRevision revision, String pkgName)
BundleRevision.PACKAGE_NAMESPACE,
Collections.EMPTY_MAP,
attrs);
- Set<BundleCapability> candidates = m_resolverState.getCandidates(req, false);
+ List<BundleCapability> candidates = findProviders(req, false);
return !candidates.isEmpty();
}
@@ -713,7 +948,7 @@ else if (rw.getCapability().getNamespace()
// Reindex the revision's capabilities since its resolved
// capabilities could be different than its declared ones
// (e.g., due to substitutable exports).
- m_resolverState.addRevision(revision);
+ addRevision(revision);
// Update the state of the revision's bundle to resolved as well.
markBundleResolved(revision);
@@ -855,636 +1090,379 @@ private void fireResolvedEvents(Map<BundleRevision, List<ResolverWire>> wireMap)
return pkgs;
}
- class ResolverStateImpl implements Resolver.ResolverState
+ private synchronized void indexCapabilities(BundleRevision br)
{
- // Set of all revisions.
- private final Set<BundleRevision> m_revisions;
- // Set of all fragments.
- private final Set<BundleRevision> m_fragments;
- // Capability sets.
- private final Map<String, CapabilitySet> m_capSets;
- // Maps singleton symbolic names to list of bundle revisions sorted by version.
- private final Map<String, List<BundleRevision>> m_singletons;
- // Selected singleton bundle revisions.
- private final Set<BundleRevision> m_selectedSingletons;
- // Execution environment.
- private final String m_fwkExecEnvStr;
- // Parsed framework environments
- private final Set<String> m_fwkExecEnvSet;
-
-// void dump()
-// {
-// for (Entry<String, CapabilitySet> entry : m_capSets.entrySet())
-// {
-// System.out.println("+++ START CAPSET " + entry.getKey());
-// entry.getValue().dump();
-// System.out.println("+++ END CAPSET " + entry.getKey());
-// }
-// }
-
- ResolverStateImpl(String fwkExecEnvStr)
+ List<BundleCapability> caps =
+ (Util.isFragment(br) || (br.getWiring() == null))
+ ? br.getDeclaredCapabilities(null)
+ : br.getWiring().getCapabilities(null);
+ if (caps != null)
{
- m_revisions = new HashSet<BundleRevision>();
- m_fragments = new HashSet<BundleRevision>();
- m_capSets = new HashMap<String, CapabilitySet>();
- m_singletons = new HashMap<String, List<BundleRevision>>();
- m_selectedSingletons = new HashSet<BundleRevision>();
-
- m_fwkExecEnvStr = (fwkExecEnvStr != null) ? fwkExecEnvStr.trim() : null;
- m_fwkExecEnvSet = parseExecutionEnvironments(fwkExecEnvStr);
-
- List<String> indices = new ArrayList<String>();
- indices.add(BundleRevision.BUNDLE_NAMESPACE);
- m_capSets.put(BundleRevision.BUNDLE_NAMESPACE, new CapabilitySet(indices, true));
-
- indices = new ArrayList<String>();
- indices.add(BundleRevision.PACKAGE_NAMESPACE);
- m_capSets.put(BundleRevision.PACKAGE_NAMESPACE, new CapabilitySet(indices, true));
-
- indices = new ArrayList<String>();
- indices.add(BundleRevision.HOST_NAMESPACE);
- m_capSets.put(BundleRevision.HOST_NAMESPACE, new CapabilitySet(indices, true));
- }
-
- synchronized Set<BundleRevision> getUnresolvedRevisions()
- {
- Set<BundleRevision> unresolved = new HashSet<BundleRevision>();
- for (BundleRevision revision : m_revisions)
+ for (BundleCapability cap : caps)
{
- if (revision.getWiring() == 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.
+ // It will be the resolver's responsibility to find all
+ // attached hosts for fragments.
+ if (cap.getRevision() == br)
{
- unresolved.add(revision);
+ CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+ if (capSet == null)
+ {
+ capSet = new CapabilitySet(null, true);
+ m_capSets.put(cap.getNamespace(), capSet);
+ }
+ capSet.addCapability(cap);
}
}
- return unresolved;
}
+ }
- synchronized void addRevision(BundleRevision br)
+ private synchronized void deindexCapabilities(BundleRevision br)
+ {
+ // We only need be concerned with declared capabilities here,
+ // because resolved capabilities will be a subset, since fragment
+ // capabilities are not considered to be part of the host.
+ List<BundleCapability> caps = br.getDeclaredCapabilities(null);
+ if (caps != null)
{
- // Always attempt to remove the revision, since
- // this method can be used for re-indexing a revision
- // after it has been resolved.
- removeRevision(br);
-
- m_revisions.add(br);
-
- // Add singletons to the singleton map.
- boolean isSingleton = Util.isSingleton(br);
- if (isSingleton)
+ for (BundleCapability cap : caps)
{
- // Index the new singleton.
- addToSingletonMap(m_singletons, br);
- }
-
- // We always need to index non-singleton bundle capabilities, but
- // singleton bundles only need to be index if they are resolved.
- // Unresolved singleton capabilities are only indexed before a
- // resolve operation when singleton selection is performed.
- if (!isSingleton || (br.getWiring() != null))
- {
- if (Util.isFragment(br))
+ CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+ if (capSet != null)
{
- m_fragments.add(br);
+ capSet.removeCapability(cap);
}
- indexCapabilities(br);
}
}
+ }
- private synchronized void indexCapabilities(BundleRevision br)
- {
- List<BundleCapability> caps =
- (Util.isFragment(br) || (br.getWiring() == null))
- ? br.getDeclaredCapabilities(null)
- : br.getWiring().getCapabilities(null);
- if (caps != null)
- {
- for (BundleCapability cap : caps)
- {
- // 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.
- // It will be the resolver's responsibility to find all
- // attached hosts for fragments.
- if (cap.getRevision() == br)
- {
- CapabilitySet capSet = m_capSets.get(cap.getNamespace());
- if (capSet == null)
- {
- capSet = new CapabilitySet(null, true);
- m_capSets.put(cap.getNamespace(), capSet);
- }
- capSet.addCapability(cap);
- }
- }
- }
- }
+ private synchronized boolean isSelectedSingleton(BundleRevision br)
+ {
+ return m_selectedSingletons.contains(br);
+ }
- private synchronized void deindexCapabilities(BundleRevision br)
+ private synchronized void selectSingletons()
+ throws BundleException
+ {
+ // First deindex any unresolved singletons to make sure
+ // there aren't any available from previous resolves.
+ // Also remove them from the fragment list, for the same
+ // reason.
+ m_selectedSingletons.clear();
+ for (Entry<String, List<BundleRevision>> entry : m_singletons.entrySet())
{
- // We only need be concerned with declared capabilities here,
- // because resolved capabilities will be a subset, since fragment
- // capabilities are not considered to be part of the host.
- List<BundleCapability> caps = br.getDeclaredCapabilities(null);
- if (caps != null)
+ for (BundleRevision singleton : entry.getValue())
{
- for (BundleCapability cap : caps)
+ if (singleton.getWiring() == null)
{
- CapabilitySet capSet = m_capSets.get(cap.getNamespace());
- if (capSet != null)
- {
- capSet.removeCapability(cap);
- }
+ deindexCapabilities(singleton);
+ m_fragments.remove(singleton);
}
}
}
- synchronized void removeRevision(BundleRevision br)
+ // If no resolver hooks, then use default singleton selection
+ // algorithm, otherwise defer to the resolver hooks.
+ if (m_hooks.isEmpty())
{
- if (m_revisions.remove(br))
- {
- m_fragments.remove(br);
- deindexCapabilities(br);
-
- // If this module is a singleton, then remove it from the
- // singleton map.
- List<BundleRevision> revisions = m_singletons.get(br.getSymbolicName());
- if (revisions != null)
- {
- revisions.remove(br);
- if (revisions.isEmpty())
- {
- m_singletons.remove(br.getSymbolicName());
- }
- }
- }
+ selectDefaultSingletons();
}
-
- synchronized Set<BundleRevision> getFragments()
+ else
{
- Set<BundleRevision> fragments = new HashSet(m_fragments);
- // Filter out any fragments that are not the current revision.
- for (Iterator<BundleRevision> it = fragments.iterator(); it.hasNext(); )
- {
- BundleRevision fragment = it.next();
- BundleRevision currentFragmentRevision =
- fragment.getBundle().adapt(BundleRevision.class);
- if (fragment != currentFragmentRevision)
- {
- it.remove();
- }
- }
- return fragments;
+ selectSingletonsUsingHooks();
}
+ }
- synchronized boolean isSelectedSingleton(BundleRevision br)
+ /*
+ * Selects the singleton with the highest version from groupings
+ * based on the symbolic name. No selection is made if the group
+ * already has a resolved singleton.
+ */
+ private void selectDefaultSingletons()
+ {
+ // Now select the singletons available for this resolve operation.
+ for (Entry<String, List<BundleRevision>> entry : m_singletons.entrySet())
{
- return m_selectedSingletons.contains(br);
+ selectSingleton(entry.getValue());
}
+ }
- synchronized void selectSingletons()
- throws BundleException
+ /*
+ * Groups singletons based on resolver hook filtering and then selects
+ * the singleton from each group with the highest version that is in
+ * the resolver hook whitelist. No selection is made if a group already
+ * has a resolved singleton in it.
+ */
+ private void selectSingletonsUsingHooks()
+ throws BundleException
+ {
+ // Convert singleton bundle revision map into a map using
+ // bundle capabilities instead, since this is what the resolver
+ // hooks require.
+ Map<BundleCapability, Collection<BundleCapability>> allCollisions
+ = new HashMap<BundleCapability, Collection<BundleCapability>>();
+ for (Entry<String, List<BundleRevision>> entry : m_singletons.entrySet())
{
- // First deindex any unresolved singletons to make sure
- // there aren't any available from previous resolves.
- // Also remove them from the fragment list, for the same
- // reason.
- m_selectedSingletons.clear();
- for (Entry<String, List<BundleRevision>> entry : m_singletons.entrySet())
+ Collection<BundleCapability> bundleCaps =
+ new ArrayList<BundleCapability>();
+ for (BundleRevision br : entry.getValue())
{
- for (BundleRevision singleton : entry.getValue())
+ List<BundleCapability> caps =
+ br.getDeclaredCapabilities(BundleRevision.BUNDLE_NAMESPACE);
+ if (!caps.isEmpty())
{
- if (singleton.getWiring() == null)
- {
- deindexCapabilities(singleton);
- m_fragments.remove(singleton);
- }
+ bundleCaps.add(caps.get(0));
}
}
- // If no resolver hooks, then use default singleton selection
- // algorithm, otherwise defer to the resolver hooks.
- if (m_hooks.isEmpty())
- {
- selectDefaultSingletons();
- }
- else
- {
- selectSingletonsUsingHooks();
- }
- }
-
- /*
- * Selects the singleton with the highest version from groupings
- * based on the symbolic name. No selection is made if the group
- * already has a resolved singleton.
- */
- private void selectDefaultSingletons()
- {
- // Now select the singletons available for this resolve operation.
- for (Entry<String, List<BundleRevision>> entry : m_singletons.entrySet())
+ for (BundleCapability bc : bundleCaps)
{
- selectSingleton(entry.getValue());
+ Collection<BundleCapability> capCopy =
+ new ShrinkableCollection<BundleCapability>(
+ new ArrayList<BundleCapability>(bundleCaps));
+ capCopy.remove(bc);
+ allCollisions.put(bc, capCopy);
}
}
- /*
- * Groups singletons based on resolver hook filtering and then selects
- * the singleton from each group with the highest version that is in
- * the resolver hook whitelist. No selection is made if a group already
- * has a resolved singleton in it.
- */
- private void selectSingletonsUsingHooks()
- throws BundleException
+ // Invoke hooks to allow them to filter singleton collisions.
+ for (ResolverHook hook : m_hooks)
{
- // Convert singleton bundle revision map into a map using
- // bundle capabilities instead, since this is what the resolver
- // hooks require.
- Map<BundleCapability, Collection<BundleCapability>> allCollisions
- = new HashMap<BundleCapability, Collection<BundleCapability>>();
- for (Entry<String, List<BundleRevision>> entry : m_singletons.entrySet())
+ for (Entry<BundleCapability, Collection<BundleCapability>> entry
+ : allCollisions.entrySet())
{
- Collection<BundleCapability> bundleCaps =
- new ArrayList<BundleCapability>();
- for (BundleRevision br : entry.getValue())
- {
- List<BundleCapability> caps =
- br.getDeclaredCapabilities(BundleRevision.BUNDLE_NAMESPACE);
- if (!caps.isEmpty())
- {
- bundleCaps.add(caps.get(0));
- }
- }
-
- for (BundleCapability bc : bundleCaps)
+ try
{
- Collection<BundleCapability> capCopy =
- new ShrinkableCollection<BundleCapability>(
- new ArrayList<BundleCapability>(bundleCaps));
- capCopy.remove(bc);
- allCollisions.put(bc, capCopy);
+ Felix.m_secureAction
+ .invokeResolverHookSingleton(hook, entry.getKey(), entry.getValue());
}
- }
-
- // Invoke hooks to allow them to filter singleton collisions.
- for (ResolverHook hook : m_hooks)
- {
- for (Entry<BundleCapability, Collection<BundleCapability>> entry
- : allCollisions.entrySet())
+ catch (Throwable ex)
{
- try
- {
- Felix.m_secureAction
- .invokeResolverHookSingleton(hook, entry.getKey(), entry.getValue());
- }
- catch (Throwable ex)
- {
- throw new BundleException(
- "Resolver hook exception: " + ex.getMessage(),
- BundleException.REJECTED_BY_HOOK,
- ex);
- }
+ throw new BundleException(
+ "Resolver hook exception: " + ex.getMessage(),
+ BundleException.REJECTED_BY_HOOK,
+ ex);
}
}
+ }
- // Create groups according to how the resolver hooks filtered the
- // collisions.
- List<List<BundleRevision>> groups = new ArrayList<List<BundleRevision>>();
- while (!allCollisions.isEmpty())
- {
- BundleCapability target = allCollisions.entrySet().iterator().next().getKey();
- groups.add(groupSingletons(allCollisions, target, new ArrayList<BundleRevision>()));
- }
+ // Create groups according to how the resolver hooks filtered the
+ // collisions.
+ List<List<BundleRevision>> groups = new ArrayList<List<BundleRevision>>();
+ while (!allCollisions.isEmpty())
+ {
+ BundleCapability target = allCollisions.entrySet().iterator().next().getKey();
+ groups.add(groupSingletons(allCollisions, target, new ArrayList<BundleRevision>()));
+ }
- // Now select the singletons available for this resolve operation.
- for (List<BundleRevision> group : groups)
- {
- selectSingleton(group);
- }
+ // Now select the singletons available for this resolve operation.
+ for (List<BundleRevision> group : groups)
+ {
+ selectSingleton(group);
}
+ }
- private List<BundleRevision> groupSingletons(
- Map<BundleCapability, Collection<BundleCapability>> allCollisions,
- BundleCapability target, List<BundleRevision> group)
+ private List<BundleRevision> groupSingletons(
+ Map<BundleCapability, Collection<BundleCapability>> allCollisions,
+ BundleCapability target, List<BundleRevision> group)
+ {
+ if (!group.contains(target.getRevision()))
{
- if (!group.contains(target.getRevision()))
- {
- // Add the target since it is implicitly part of the group.
- group.add(target.getRevision());
+ // Add the target since it is implicitly part of the group.
+ group.add(target.getRevision());
- // Recursively add the revisions of any singleton's in the
- // target's collisions.
- Collection<BundleCapability> collisions = allCollisions.remove(target);
- for (BundleCapability collision : collisions)
- {
- groupSingletons(allCollisions, collision, group);
- }
+ // Recursively add the revisions of any singleton's in the
+ // target's collisions.
+ Collection<BundleCapability> collisions = allCollisions.remove(target);
+ for (BundleCapability collision : collisions)
+ {
+ groupSingletons(allCollisions, collision, group);
+ }
- // Need to check the values of other collisions for this target
- // and add those to the target's group too, since collisions are
- // treated as two-way relationships. Repeat until there are no
- // collision groups left that contain the target capability.
- boolean repeat;
- do
+ // Need to check the values of other collisions for this target
+ // and add those to the target's group too, since collisions are
+ // treated as two-way relationships. Repeat until there are no
+ // collision groups left that contain the target capability.
+ boolean repeat;
+ do
+ {
+ repeat = false;
+ for (Entry<BundleCapability, Collection<BundleCapability>> entry:
+ allCollisions.entrySet())
{
- repeat = false;
- for (Entry<BundleCapability, Collection<BundleCapability>> entry:
- allCollisions.entrySet())
+ if (entry.getValue().contains(target))
{
- if (entry.getValue().contains(target))
- {
- repeat = true;
- groupSingletons(allCollisions, entry.getKey(), group);
- break;
- }
+ repeat = true;
+ groupSingletons(allCollisions, entry.getKey(), group);
+ break;
}
}
- while (repeat);
}
- return group;
+ while (repeat);
}
+ return group;
+ }
- /*
- * Selects the highest bundle revision from the group that is
- * in the resolver hook whitelist (if there are hooks). No
- * selection is made if there is an already resolved singleton
- * in the group, since it is already indexed.
- */
- private void selectSingleton(List<BundleRevision> singletons)
+ /*
+ * Selects the highest bundle revision from the group that is
+ * in the resolver hook whitelist (if there are hooks). No
+ * selection is made if there is an already resolved singleton
+ * in the group, since it is already indexed.
+ */
+ private void selectSingleton(List<BundleRevision> singletons)
+ {
+ BundleRevision selected = null;
+ for (BundleRevision singleton : singletons)
{
- BundleRevision selected = null;
- for (BundleRevision singleton : singletons)
+ // If a singleton is already resolved,
+ // then there is nothing to do.
+ if (singleton.getWiring() != null)
{
- // If a singleton is already resolved,
- // then there is nothing to do.
- if (singleton.getWiring() != null)
- {
- selected = null;
- break;
- }
- // If this singleton is not in the whitelist, then it cannot
- // be selected. If it is, in can only be selected if it has
- // a higher version than the currently selected singleton, if
- // there is one.
- if (((m_whitelist == null) || m_whitelist.contains(singleton))
- && ((selected == null)
- || (selected.getVersion().compareTo(singleton.getVersion()) > 0)))
- {
- selected = singleton;
- }
+ selected = null;
+ break;
}
- if (selected != null)
+ // If this singleton is not in the whitelist, then it cannot
+ // be selected. If it is, in can only be selected if it has
+ // a higher version than the currently selected singleton, if
+ // there is one.
+ if (((m_whitelist == null) || m_whitelist.contains(singleton))
+ && ((selected == null)
+ || (selected.getVersion().compareTo(singleton.getVersion()) > 0)))
{
- // Record the selected singleton.
- m_selectedSingletons.add(selected);
- // Index its capabilities.
- indexCapabilities(selected);
- // If the selected singleton is a fragment, then
- // add it to the list of fragments.
- if (Util.isFragment(selected))
- {
- m_fragments.add(selected);
- }
+ selected = singleton;
}
}
-
- //
- // ResolverState methods.
- //
-
- public boolean isEffective(BundleRequirement req)
+ if (selected != null)
{
- String effective = req.getDirectives().get(Constants.EFFECTIVE_DIRECTIVE);
- return ((effective == null) || effective.equals(Constants.EFFECTIVE_RESOLVE));
+ // Record the selected singleton.
+ m_selectedSingletons.add(selected);
+ // Index its capabilities.
+ indexCapabilities(selected);
+ // If the selected singleton is a fragment, then
+ // add it to the list of fragments.
+ if (Util.isFragment(selected))
+ {
+ m_fragments.add(selected);
+ }
}
+ }
- public synchronized SortedSet<BundleCapability> getCandidates(
- BundleRequirement req, boolean obeyMandatory)
+ private synchronized Set<BundleRevision> getFragments()
+ {
+ Set<BundleRevision> fragments = new HashSet(m_fragments);
+ // Filter out any fragments that are not the current revision.
+ for (Iterator<BundleRevision> it = fragments.iterator(); it.hasNext(); )
{
- BundleRevisionImpl reqRevision = (BundleRevisionImpl) req.getRevision();
- SortedSet<BundleCapability> result =
- new TreeSet<BundleCapability>(new CandidateComparator());
-
- CapabilitySet capSet = m_capSets.get(req.getNamespace());
- if (capSet != null)
+ BundleRevision fragment = it.next();
+ BundleRevision currentFragmentRevision =
+ fragment.getBundle().adapt(BundleRevision.class);
+ if (fragment != currentFragmentRevision)
{
- // Get the requirement's filter; if this is our own impl we
- // have a shortcut to get the already parsed filter, otherwise
- // we must parse it from the directive.
- SimpleFilter sf = null;
- if (req instanceof BundleRequirementImpl)
- {
- sf = ((BundleRequirementImpl) req).getFilter();
- }
- else
- {
- String filter = req.getDirectives().get(Constants.FILTER_DIRECTIVE);
- if (filter == null)
- {
- sf = new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
- }
- else
- {
- sf = SimpleFilter.parse(filter);
- }
- }
-
- // Find the matching candidates.
- Set<BundleCapability> matches = capSet.match(sf, obeyMandatory);
- // Filter matching candidates.
- for (BundleCapability cap : matches)
- {
- // Filter according to security.
- if (filteredBySecurity(req, cap))
- {
- continue;
- }
- // Filter already resolved hosts, since we don't support
- // dynamic attachment of fragments.
- if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE)
- && (cap.getRevision().getWiring() != null))
- {
- continue;
- }
-
- result.add(cap);
- }
+ it.remove();
}
+ }
+ return fragments;
+ }
- // If we have resolver hooks, then we may need to filter our results
- // based on a whitelist and/or fine-grained candidate filtering.
- if (!result.isEmpty() && !m_hooks.isEmpty())
+ void checkExecutionEnvironment(BundleRevision revision) throws ResolveException
+ {
+ String bundleExecEnvStr = (String)
+ ((BundleRevisionImpl) revision).getHeaders().get(
+ Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
+ if (bundleExecEnvStr != null)
+ {
+ bundleExecEnvStr = bundleExecEnvStr.trim();
+
+ // If the bundle has specified an execution environment and the
+ // framework has an execution environment specified, then we must
+ // check for a match.
+ if (!bundleExecEnvStr.equals("")
+ && (m_fwkExecEnvStr != null)
+ && (m_fwkExecEnvStr.length() > 0))
{
- // It we have a whitelist, then first filter out candidates
- // from disallowed revisions.
- if (m_whitelist != null)
+ StringTokenizer tokens = new StringTokenizer(bundleExecEnvStr, ",");
+ boolean found = false;
+ while (tokens.hasMoreTokens() && !found)
{
- for (Iterator<BundleCapability> it = result.iterator(); it.hasNext(); )
+ if (m_fwkExecEnvSet.contains(tokens.nextToken().trim()))
{
- if (!m_whitelist.contains(it.next().getRevision()))
- {
- it.remove();
- }
+ found = true;
}
}
-
- // Now give the hooks a chance to do fine-grained filtering.
- ShrinkableCollection<BundleCapability> shrinkable =
- new ShrinkableCollection<BundleCapability>(result);
- for (ResolverHook hook : m_hooks)
+ if (!found)
{
- try
- {
- Felix.m_secureAction
- .invokeResolverHookMatches(hook, req, shrinkable);
- }
- catch (Throwable th)
- {
- m_logger.log(Logger.LOG_WARNING, "Resolver hook exception.", th);
- }
+ throw new ResolveException(
+ "Execution environment not supported: "
+ + bundleExecEnvStr, revision, null);
}
}
-
- return result;
}
+ }
- private boolean filteredBySecurity(BundleRequirement req, BundleCapability cap)
+ void checkNativeLibraries(BundleRevision revision) throws ResolveException
+ {
+ // Next, try to resolve any native code, since the revision is
+ // not resolvable if its native code cannot be loaded.
+ List<R4Library> libs = ((BundleRevisionImpl) revision).getDeclaredNativeLibraries();
+ if (libs != null)
{
- if (System.getSecurityManager() != null)
+ String msg = null;
+ // Verify that all native libraries exist in advance; this will
+ // throw an exception if the native library does not exist.
+ for (int libIdx = 0; (msg == null) && (libIdx < libs.size()); libIdx++)
{
- BundleRevisionImpl reqRevision = (BundleRevisionImpl) req.getRevision();
-
- if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
- {
- if (!((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
- new PackagePermission((String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
- PackagePermission.EXPORTONLY)) ||
- !((reqRevision == null) ||
- ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
- new PackagePermission((String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
- cap.getRevision().getBundle(),PackagePermission.IMPORT))
- ))
- {
- if (reqRevision != cap.getRevision())
- {
- return true;
- }
- }
- }
- else if (req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
- { if (!((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
- new BundlePermission(cap.getRevision().getSymbolicName(), BundlePermission.PROVIDE)) ||
- !((reqRevision == null) ||
- ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
- new BundlePermission(reqRevision.getSymbolicName(), BundlePermission.REQUIRE))
- ))
- {
- return true;
- }
- }
- else if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
+ String entryName = libs.get(libIdx).getEntryName();
+ if (entryName != null)
{
- if (!((BundleProtectionDomain) reqRevision.getProtectionDomain())
- .impliesDirect(new BundlePermission(
- reqRevision.getSymbolicName(),
- BundlePermission.FRAGMENT))
- || !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain())
- .impliesDirect(new BundlePermission(
- cap.getRevision().getSymbolicName(),
- BundlePermission.HOST)))
+ if (!((BundleRevisionImpl) revision).getContent().hasEntry(entryName))
{
- return true;
- }
- }
- else if (!req.getNamespace().equals("osgi.ee"))
- {
- if (!((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
- new CapabilityPermission(req.getNamespace(), CapabilityPermission.PROVIDE))
- ||
- !((reqRevision == null) || ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
- new CapabilityPermission(req.getNamespace(), cap.getAttributes(), cap.getRevision().getBundle(), CapabilityPermission.REQUIRE))))
- {
- return true;
+ msg = "Native library does not exist: " + entryName;
}
}
}
- return false;
+ // If we have a zero-length native library array, then
+ // this means no native library class could be selected
+ // so we should fail to resolve.
+ if (libs.isEmpty())
+ {
+ msg = "No matching native libraries found.";
+ }
+ if (msg != null)
+ {
+ throw new ResolveException(msg, revision, null);
+ }
}
+ }
- public void checkExecutionEnvironment(BundleRevision revision) throws ResolveException
+ private synchronized Set<BundleRevision> getUnresolvedRevisions()
+ {
+ Set<BundleRevision> unresolved = new HashSet<BundleRevision>();
+ for (BundleRevision revision : m_revisions)
{
- String bundleExecEnvStr = (String)
- ((BundleRevisionImpl) revision).getHeaders().get(
- Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
- if (bundleExecEnvStr != null)
+ if (revision.getWiring() == null)
{
- bundleExecEnvStr = bundleExecEnvStr.trim();
-
- // If the bundle has specified an execution environment and the
- // framework has an execution environment specified, then we must
- // check for a match.
- if (!bundleExecEnvStr.equals("")
- && (m_fwkExecEnvStr != null)
- && (m_fwkExecEnvStr.length() > 0))
- {
- StringTokenizer tokens = new StringTokenizer(bundleExecEnvStr, ",");
- boolean found = false;
- while (tokens.hasMoreTokens() && !found)
- {
- if (m_fwkExecEnvSet.contains(tokens.nextToken().trim()))
- {
- found = true;
- }
- }
- if (!found)
- {
- throw new ResolveException(
- "Execution environment not supported: "
- + bundleExecEnvStr, revision, null);
- }
- }
+ unresolved.add(revision);
}
}
+ return unresolved;
+ }
- public void checkNativeLibraries(BundleRevision revision) throws ResolveException
+ private synchronized Map<BundleRevision, BundleWiring> getWirings()
+ {
+ Map<BundleRevision, BundleWiring> wirings = new HashMap<BundleRevision, BundleWiring>();
+
+ for (BundleRevision revision : m_revisions)
{
- // Next, try to resolve any native code, since the revision is
- // not resolvable if its native code cannot be loaded.
- List<R4Library> libs = ((BundleRevisionImpl) revision).getDeclaredNativeLibraries();
- if (libs != null)
+ if (revision.getWiring() != null)
{
- String msg = null;
- // Verify that all native libraries exist in advance; this will
- // throw an exception if the native library does not exist.
- for (int libIdx = 0; (msg == null) && (libIdx < libs.size()); libIdx++)
- {
- String entryName = libs.get(libIdx).getEntryName();
- if (entryName != null)
- {
- if (!((BundleRevisionImpl) revision).getContent().hasEntry(entryName))
- {
- msg = "Native library does not exist: " + entryName;
- }
- }
- }
- // If we have a zero-length native library array, then
- // this means no native library class could be selected
- // so we should fail to resolve.
- if (libs.isEmpty())
- {
- msg = "No matching native libraries found.";
- }
- if (msg != null)
- {
- throw new ResolveException(msg, revision, null);
- }
+ wirings.put(revision, revision.getWiring());
}
}
+ return wirings;
}
- //
- // Utility methods.
- //
-
/**
* Updates the framework wide execution environment string and a cached Set of
* execution environment tokens from the comma delimited list specified by the
193 framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
View
@@ -27,11 +27,8 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.SortedSet;
import java.util.TreeMap;
-import java.util.TreeSet;
-import org.apache.felix.framework.BundleRevisionImpl;
-import org.apache.felix.framework.resolver.Resolver.ResolverState;
+import org.apache.felix.framework.ResolveContextImpl;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.wiring.BundleCapabilityImpl;
@@ -50,15 +47,14 @@
public static final int OPTIONAL = 1;
public static final int ON_DEMAND = 2;
- // Set of all mandatory bundle revisions.
private final Set<BundleRevision> m_mandatoryRevisions;
// Maps a capability to requirements that match it.
private final Map<BundleCapability, Set<BundleRequirement>> m_dependentMap;
// Maps a requirement to the capability it matches.
- private final Map<BundleRequirement, SortedSet<BundleCapability>> m_candidateMap;
+ private final Map<BundleRequirement, List<BundleCapability>> m_candidateMap;
// Maps a bundle revision to its associated wrapped revision; this only happens
// when a revision being resolved has fragments to attach to it.
- private final Map<BundleRevision, HostBundleRevision> m_allWrappedHosts;
+ private final Map<BundleRevision, WrappedRevision> m_allWrappedHosts;
// Map used when populating candidates to hold intermediate and final results.
private final Map<BundleRevision, Object> m_populateResultCache;
@@ -75,8 +71,8 @@
private Candidates(
Set<BundleRevision> mandatoryRevisions,
Map<BundleCapability, Set<BundleRequirement>> dependentMap,
- Map<BundleRequirement, SortedSet<BundleCapability>> candidateMap,
- Map<BundleRevision, HostBundleRevision> wrappedHosts, Map<BundleRevision, Object> populateResultCache,
+ Map<BundleRequirement, List<BundleCapability>> candidateMap,
+ Map<BundleRevision, WrappedRevision> wrappedHosts, Map<BundleRevision, Object> populateResultCache,
boolean fragmentsPresent)
{
m_mandatoryRevisions = mandatoryRevisions;
@@ -94,8 +90,8 @@ public Candidates()
{
m_mandatoryRevisions = new HashSet<BundleRevision>();
m_dependentMap = new HashMap<BundleCapability, Set<BundleRequirement>>();
- m_candidateMap = new HashMap<BundleRequirement, SortedSet<BundleCapability>>();
- m_allWrappedHosts = new HashMap<BundleRevision, HostBundleRevision>();
+ m_candidateMap = new HashMap<BundleRequirement, List<BundleCapability>>();
+ m_allWrappedHosts = new HashMap<BundleRevision, WrappedRevision>();
m_populateResultCache = new HashMap<BundleRevision, Object>();
}
@@ -116,7 +112,7 @@ public Candidates()
* @param resolution indicates the resolution type.
*/
public final void populate(
- ResolverState state, BundleRevision revision, int resolution)
+ ResolveContext rc, BundleRevision revision, int resolution)
{
// Get the current result cache value, to make sure the revision
// hasn't already been populated.
@@ -146,7 +142,7 @@ else if (cacheValue instanceof Boolean)
// However, for on-demand fragments only populate if their host
// is already populated.
if ((resolution != ON_DEMAND)
- || (isFragment && populateFragmentOndemand(state, revision)))
+ || (isFragment && populateFragmentOndemand(rc, revision)))
{
if (resolution == MANDATORY)
{
@@ -155,7 +151,7 @@ else if (cacheValue instanceof Boolean)
try
{
// Try to populate candidates for the optional revision.
- populateRevision(state, revision);
+ populateRevision(rc, revision);
}
catch (ResolveException ex)
{
@@ -174,7 +170,7 @@ else if (cacheValue instanceof Boolean)
* @param revision the revision whose candidates should be populated.
*/
// TODO: FELIX3 - Modify to not be recursive.
- private void populateRevision(ResolverState state, BundleRevision revision)
+ private void populateRevision(ResolveContext rc, BundleRevision revision)
{
// Determine if we've already calculated this revision's candidates.
// The result cache will have one of three values:
@@ -196,7 +192,7 @@ private void populateRevision(ResolverState state, BundleRevision revision)
// Keeps track of the candidates we've already calculated for the
// current revision's requirements.
- Map<BundleRequirement, SortedSet<BundleCapability>> localCandidateMap = null;
+ Map<BundleRequirement, List<BundleCapability>> localCandidateMap = null;
// Keeps track of the current revision's requirements for which we
// haven't yet found candidates.
@@ -234,10 +230,10 @@ else if (cacheValue != null)
if ((remainingReqs == null) && (localCandidateMap == null))
{
// Verify that any required execution environment is satisfied.
- state.checkExecutionEnvironment(revision);
+ ((ResolveContextImpl) rc).checkExecutionEnvironment(revision);
// Verify that any native libraries match the current platform.
- state.checkNativeLibraries(revision);
+ ((ResolveContextImpl) rc).checkNativeLibraries(revision);
// Record cycle count.
cycleCount = new Integer(0);
@@ -263,7 +259,7 @@ else if (cacheValue != null)
// Ignore non-effective and dynamic requirements.
String resolution = req.getDirectives().get(Constants.RESOLUTION_DIRECTIVE);
- if (!state.isEffective(req)
+ if (!rc.isEffective(req)
|| ((resolution != null)
&& resolution.equals(FelixConstants.RESOLUTION_DYNAMIC)))
{
@@ -272,9 +268,8 @@ else if (cacheValue != null)
// Process the candidates, removing any candidates that
// cannot resolve.
- SortedSet<BundleCapability> candidates =
- state.getCandidates((BundleRequirementImpl) req, true);
- ResolveException rethrow = processCandidates(state, revision, candidates);
+ List<BundleCapability> candidates = rc.findProviders(req, true);
+ ResolveException rethrow = processCandidates(rc, revision, candidates);
// First, due to cycles, makes sure we haven't already failed in
// a deeper recursion.
@@ -324,7 +319,7 @@ else if (cycleCount.intValue() == 0)
}
}
- private boolean populateFragmentOndemand(ResolverState state, BundleRevision revision)
+ private boolean populateFragmentOndemand(ResolveContext rc, BundleRevision revision)
throws ResolveException
{
// Create a modifiable list of the revision's requirements.
@@ -344,8 +339,7 @@ private boolean populateFragmentOndemand(ResolverState state, BundleRevision rev
}
}
// Get candidates hosts and keep any that have been populated.
- SortedSet<BundleCapability> hosts =
- state.getCandidates((BundleRequirementImpl) hostReq, false);
+ List<BundleCapability> hosts = rc.findProviders(hostReq, false);
for (Iterator<BundleCapability> it = hosts.iterator(); it.hasNext(); )
{
BundleCapability host = it.next();
@@ -364,16 +358,16 @@ private boolean populateFragmentOndemand(ResolverState state, BundleRevision rev
// 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);
+ ((ResolveContextImpl) rc).checkExecutionEnvironment(revision);
// Verify that any native libraries match the current platform.
- state.checkNativeLibraries(revision);
+ ((ResolveContextImpl) rc).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>>();
+ Map<BundleRequirement, List<BundleCapability>> localCandidateMap =
+ new HashMap<BundleRequirement, List<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
@@ -385,8 +379,8 @@ private boolean populateFragmentOndemand(ResolverState state, BundleRevision rev
}
public void populateDynamic(
- ResolverState state, BundleRevision revision,
- BundleRequirement req, SortedSet<BundleCapability> candidates)
+ ResolveContext rc, BundleRevision revision,
+ BundleRequirement req, List<BundleCapability> candidates)
{
// Record the revision associated with the dynamic require
// as a mandatory revision.
@@ -397,7 +391,7 @@ public void populateDynamic(
// Process the candidates, removing any candidates that
// cannot resolve.
- ResolveException rethrow = processCandidates(state, revision, candidates);
+ ResolveException rethrow = processCandidates(rc, revision, candidates);
if (candidates.isEmpty())
{
@@ -423,9 +417,9 @@ public void populateDynamic(
* @return a resolve exception to be re-thrown, if any, or null.
*/
private ResolveException processCandidates(
- ResolverState state,
+ ResolveContext rc,
BundleRevision revision,
- SortedSet<BundleCapability> candidates)
+ List<BundleCapability> candidates)
{
// Get satisfying candidates and populate their candidates if necessary.
ResolveException rethrow = null;
@@ -466,7 +460,7 @@ private ResolveException processCandidates(
{
try
{
- populateRevision(state, candCap.getRevision());
+ populateRevision(rc, candCap.getRevision());
}
catch (ResolveException ex)
{
@@ -505,8 +499,19 @@ private ResolveException processCandidates(
{
// Note that we can just add this as a candidate
// directly, since we know it is already resolved.
- candidates.add(
- new HostedCapability(
+ // NOTE: We are synthesizing a hosted capability here,
+ // but we are not using a ShadowList like we do when
+ // we synthesizing capabilities for unresolved hosts.
+ // It is not necessary to use the ShadowList here since
+ // the host is resolved, because in that case we can
+ // calculate the proper package space by traversing
+ // the wiring. In the unresolved case, this isn't possible
+ // so we need to use the ShadowList so we can keep
+ // a reference to a synthesized resource with attached
+ // fragments so we can correctly calculate its package
+ // space.
+ rc.insertHostedCapability(candidates,
+ new WrappedCapability(
wire.getCapability().getRevision(),
(BundleCapabilityImpl) fragCand));
}
@@ -540,7 +545,7 @@ public ResolveException getResolveException(BundleRevision revision)
* @param req the requirement to add.
* @param candidates the candidates matching the requirement.
**/
- private void add(BundleRequirement req, SortedSet<BundleCapability> candidates)
+ private void add(BundleRequirement req, List<BundleCapability> candidates)
{
if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
{
@@ -557,9 +562,9 @@ private void add(BundleRequirement req, SortedSet<BundleCapability> candidates)
* be further modified by the caller.
* @param candidates the bulk requirements and candidates to add.
**/
- private void add(Map<BundleRequirement, SortedSet<BundleCapability>> candidates)
+ private void add(Map<BundleRequirement, List<BundleCapability>> candidates)
{
- for (Entry<BundleRequirement, SortedSet<BundleCapability>> entry : candidates.entrySet())
+ for (Entry<BundleRequirement, List<BundleCapability>> entry : candidates.entrySet())
{
add(entry.getKey(), entry.getValue());
}
@@ -583,7 +588,7 @@ public BundleRevision getWrappedHost(BundleRevision m)
* @param req the requirement whose candidates are desired.
* @return the matching candidates or null.
**/
- public SortedSet<BundleCapability> getCandidates(BundleRequirement req)
+ public List<BundleCapability> getCandidates(BundleRequirement req)
{
return m_candidateMap.get(req);
}
@@ -604,7 +609,7 @@ public BundleRevision getWrappedHost(BundleRevision m)
* @throws ResolveException if the removal of any unselected fragments result
* in the root module being unable to resolve.
**/
- public void prepare()
+ public void prepare(ResolveContext rc)
{
// Maps a host capability to a map containing its potential fragments;
// the fragment map maps a fragment symbolic name to a map that maps
@@ -632,7 +637,7 @@ public void prepare()
// with host's attached fragment capabilities.
// Steps 1 and 2
- List<HostBundleRevision> hostRevisions = new ArrayList<HostBundleRevision>();
+ List<WrappedRevision> hostRevisions = new ArrayList<WrappedRevision>();
List<BundleRevision> unselectedFragments = new ArrayList<BundleRevision>();
for (Entry<BundleCapability, Map<String, Map<Version, List<BundleRequirement>>>>
hostEntry : hostFragments.entrySet())
@@ -667,7 +672,7 @@ public void prepare()
else
{
m_dependentMap.get(hostCap).remove(hostReq);
- SortedSet<BundleCapability> hosts = m_candidateMap.get(hostReq);
+ List<BundleCapability> hosts = m_candidateMap.get(hostReq);
hosts.remove(hostCap);
if (hosts.isEmpty())
{
@@ -679,8 +684,8 @@ public void prepare()
}
// Step 2
- HostBundleRevision wrappedHost =
- new HostBundleRevision(hostCap.getRevision(), selectedFragments);
+ WrappedRevision wrappedHost =
+ new WrappedRevision(hostCap.getRevision(), selectedFragments);
hostRevisions.add(wrappedHost);
m_allWrappedHosts.put(hostCap.getRevision(), wrappedHost);
}
@@ -694,7 +699,7 @@ public void prepare()
}
// Step 4
- for (HostBundleRevision hostRevision : hostRevisions)
+ for (WrappedRevision hostRevision : hostRevisions)
{
// Replaces capabilities from fragments with the capabilities
// from the merged host.
@@ -704,8 +709,7 @@ public void prepare()
// really be attached to the original host, not the wrapper.
if (!c.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
{
- BundleCapability origCap =
- ((HostedCapability) c).getOriginalCapability();
+ BundleCapability origCap = ((HostedCapability) c).getDeclaredCapability();
// Note that you might think we could remove the original cap
// from the dependent map, but you can't since it may come from
// a fragment that is attached to multiple hosts, so each host
@@ -717,9 +721,63 @@ public void prepare()
m_dependentMap.put(c, dependents);
for (BundleRequirement r : dependents)
{
- Set<BundleCapability> cands = m_candidateMap.get(r);
- cands.remove(origCap);
- cands.add(c);
+ // We have synthesized hosted capabilities for all
+ // fragments that have been attached to hosts by
+ // wrapping the host bundle and their attached
+ // fragments. We need to use the ResolveContext to
+ // determine the proper priority order for hosted
+ // capabilities since the order may depend on the
+ // declaring host/fragment combination. However,
+ // internally we completely wrap the host revision
+ // and make all capabilities/requirements point back
+ // to the wrapped host not the declaring host. The
+ // ResolveContext expects HostedCapabilities to point
+ // to the declaring revision, so we need two separate
+ // candidate lists: one for the ResolveContext with
+ // HostedCapabilities pointing back to the declaring
+ // host and one for the resolver with HostedCapabilities
+ // pointing back to the wrapped host. We ask the
+ // ResolveContext to insert its appropriate HostedCapability
+ // into its list, then we mirror the insert into a
+ // shadow list with the resolver's HostedCapability.
+ // We only need to ask the ResolveContext to find
+ // the insert position for fragment caps since these
+ // were synthesized and we don't know their priority.
+ // However, in the resolver's candidate list we need
+ // to replace all caps with the wrapped caps, no
+ // matter if they come from the host or fragment,
+ // since we are completing replacing the declaring
+ // host and fragments with the wrapped host.
+ List<BundleCapability> cands = m_candidateMap.get(r);
+ if (!(cands instanceof ShadowList))
+ {
+ ShadowList<BundleCapability> shadow =
+ new ShadowList<BundleCapability>(cands);
+ m_candidateMap.put(r, shadow);
+ cands = shadow;
+ }
+
+ // If the original capability is from a fragment, then
+ // ask the ResolveContext to insert it and update the
+ // shadow copy of the list accordingly.
+ if (!origCap.getRevision().equals(hostRevision.getHost()))
+ {
+ List<BundleCapability> original = ((ShadowList) cands).getOriginal();
+ int removeIdx = original.indexOf(origCap);
+ original.remove(removeIdx);
+ int insertIdx = rc.insertHostedCapability(
+ original,
+ new SimpleHostedCapability(hostRevision.getHost(), origCap));
+ cands.remove(removeIdx);
+ cands.add(insertIdx, c);
+ }
+ // If the original capability is from the host, then
+ // we just need to replace it in the shadow list.
+ else
+ {
+ int idx = cands.indexOf(origCap);
+ cands.set(idx, c);
+ }
}
}
}
@@ -729,11 +787,11 @@ public void prepare()
for (BundleRequirement r : hostRevision.getDeclaredRequirements(null))
{
BundleRequirement origReq =
- ((HostedRequirement) r).getOriginalRequirement();
- SortedSet<BundleCapability> cands = m_candidateMap.get(origReq);
+ ((WrappedRequirement) r).getOriginalRequirement();
+ List<BundleCapability> cands = m_candidateMap.get(origReq);
if (cands != null)
{
- m_candidateMap.put(r, new TreeSet<BundleCapability>(cands));
+ m_candidateMap.put(r, new ArrayList<BundleCapability>(cands));
for (BundleCapability cand : cands)
{
Set<BundleRequirement> dependents = m_dependentMap.get(cand);
@@ -766,11 +824,10 @@ public void prepare()
Map<BundleCapability, Map<String, Map<Version, List<BundleRequirement>>>>
hostFragments = new HashMap<BundleCapability,
Map<String, Map<Version, List<BundleRequirement>>>>();
- for (Entry<BundleRequirement, SortedSet<BundleCapability>> entry
- : m_candidateMap.entrySet())
+ for (Entry<BundleRequirement, List<BundleCapability>> entry : m_candidateMap.entrySet())
{
BundleRequirement req = entry.getKey();
- SortedSet<BundleCapability> caps = entry.getValue();
+ List<BundleCapability> caps = entry.getValue();
for (BundleCapability cap : caps)
{
// Record the requirement as dependent on the capability.
@@ -871,7 +928,7 @@ private void remove(BundleRequirement req)
{
boolean isFragment = req.getNamespace().equals(BundleRevision.HOST_NAMESPACE);
- SortedSet<BundleCapability> candidates = m_candidateMap.remove(req);
+ List<BundleCapability> candidates = m_candidateMap.remove(req);
if (candidates != null)
{
for (BundleCapability cap : candidates)
@@ -902,7 +959,7 @@ private void remove(BundleCapability c, Set<BundleRevision> unresolvedRevisions)
{
for (BundleRequirement r : dependents)
{
- SortedSet<BundleCapability> candidates = m_candidateMap.get(r);
+ List<BundleCapability> candidates = m_candidateMap.get(r);
candidates.remove(c);
if (candidates.isEmpty())
{
@@ -935,13 +992,13 @@ public Candidates copy()
dependentMap.put(entry.getKey(), dependents);
}
- Map<BundleRequirement, SortedSet<BundleCapability>> candidateMap =
- new HashMap<BundleRequirement, SortedSet<BundleCapability>>();
- for (Entry<BundleRequirement, SortedSet<BundleCapability>> entry
+ Map<BundleRequirement, List<BundleCapability>> candidateMap =
+ new HashMap<BundleRequirement, List<BundleCapability>>();
+ for (Entry<BundleRequirement, List<BundleCapability>> entry
: m_candidateMap.entrySet())
{
- SortedSet<BundleCapability> candidates =
- new TreeSet<BundleCapability>(entry.getValue());
+ List<BundleCapability> candidates =
+ new ArrayList<BundleCapability>(entry.getValue());
candidateMap.put(entry.getKey(), candidates);
}
@@ -954,7 +1011,7 @@ public void dump()
{
// Create set of all revisions from requirements.
Set<BundleRevision> revisions = new HashSet<BundleRevision>();
- for (Entry<BundleRequirement, SortedSet<BundleCapability>> entry
+ for (Entry<BundleRequirement, List<BundleCapability>> entry
: m_candidateMap.entrySet())
{
revisions.add(entry.getKey().getRevision());
@@ -970,7 +1027,7 @@ public void dump()
: br.getDeclaredRequirements(null);
for (BundleRequirement req : reqs)
{
- Set<BundleCapability> candidates = m_candidateMap.get(req);
+ List<BundleCapability> candidates = m_candidateMap.get(req);
if ((candidates != null) && (candidates.size() > 0))
{
System.out.println(" " + req + ": " + candidates);
@@ -981,7 +1038,7 @@ public void dump()
: Util.getDynamicRequirements(br.getDeclaredRequirements(null));
for (BundleRequirement req : reqs)
{
- Set<BundleCapability> candidates = m_candidateMap.get(req);
+ List<BundleCapability> candidates = m_candidateMap.get(req);
if ((candidates != null) && (candidates.size() > 0))
{
System.out.println(" " + req + ": " + candidates);
126 framework/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java
View
@@ -1,122 +1,26 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.felix.framework.resolver;
-import java.util.List;
-import java.util.Map;
-import org.apache.felix.framework.wiring.BundleCapabilityImpl;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
-public class HostedCapability extends BundleCapabilityImpl
-{
- private final BundleRevision m_host;
- private final BundleCapabilityImpl m_cap;
-
- public HostedCapability(BundleRevision host, BundleCapabilityImpl cap)
- {
- super(host, cap.getNamespace(), cap.getDirectives(), cap.getAttributes());
- m_host = host;
- m_cap = cap;
- }
-
- @Override
- public boolean equals(Object obj)
- {
- if (obj == null)
- {
- return false;
- }
- if (getClass() != obj.getClass())
- {
- return false;
- }
- final HostedCapability other = (HostedCapability) obj;
- if (m_host != other.m_host && (m_host == null || !m_host.equals(other.m_host)))
- {
- return false;
- }
- if (m_cap != other.m_cap && (m_cap == null || !m_cap.equals(other.m_cap)))
- {
- return false;
- }
- return true;
- }
-
- @Override
- public int hashCode()
- {
- int hash = 7;
- hash = 37 * hash + (m_host != null ? m_host.hashCode() : 0);
- hash = 37 * hash + (m_cap != null ? m_cap.hashCode() : 0);
- return hash;
- }
-
- public BundleCapabilityImpl getOriginalCapability()
- {
- return m_cap;
- }
-
- @Override
- public BundleRevision getRevision()
- {
- return m_host;
- }
-
- @Override
- public String getNamespace()
- {
- return m_cap.getNamespace();
- }
-
- @Override
- public Map<String, String> getDirectives()
- {
- return m_cap.getDirectives();
- }
-
- @Override
- public Map<String, Object> getAttributes()
- {
- return m_cap.getAttributes();
- }
+public interface HostedCapability extends BundleCapability {
- @Override
- public List<String> getUses()
- {
- return m_cap.getUses();
- }
+ BundleRevision getRevision();
- @Override
- public String toString()
- {
- if (m_host == null)
- {
- return getAttributes().toString();
- }
- if (getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
- {
- return "[" + m_host + "] "
- + getNamespace()
- + "; "
- + getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
- }
- return "[" + m_host + "] " + getNamespace() + "; " + getAttributes();
- }
-}
+ BundleCapability getDeclaredCapability();
+}
52 framework/src/main/java/org/apache/felix/framework/resolver/ResolveContext.java
View
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+
+public abstract class ResolveContext
+{
+ public Collection<BundleRevision> getMandatoryRevisions()
+ {
+ return emptyCollection();
+ }
+
+ public Collection<BundleRevision> getOptionalRevisions()
+ {
+ return emptyCollection();
+ }
+
+ private static <T> Collection<T> emptyCollection()
+ {
+ return Collections.EMPTY_LIST;
+ }
+
+ public abstract List<BundleCapability> findProviders(BundleRequirement br, boolean obeyMandatory);
+
+ public abstract int insertHostedCapability(List<BundleCapability> caps, HostedCapability hc);
+
+ public abstract boolean isEffective(BundleRequirement br);
+
+ public abstract Map<BundleRevision, BundleWiring> getWirings();
+}
18 framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
View
@@ -28,21 +28,7 @@
public interface Resolver
{
+ Map<BundleRevision, List<ResolverWire>> resolve(ResolveContext rc);
Map<BundleRevision, List<ResolverWire>> resolve(
- ResolverState state,
- Set<BundleRevision> mandatoryRevisions,
- Set<BundleRevision> optionalRevisions,
- Set<BundleRevision> ondemandFragments);
- Map<BundleRevision, List<ResolverWire>> resolve(
- ResolverState state, BundleRevision revision, String pkgName,
- Set<BundleRevision> ondemandFragments);
-
- public static interface ResolverState
- {
- boolean isEffective(BundleRequirement req);
- SortedSet<BundleCapability> getCandidates(
- BundleRequirement req, boolean obeyMandatory);
- void checkExecutionEnvironment(BundleRevision revision) throws ResolveException;
- void checkNativeLibraries(BundleRevision revision) throws ResolveException;
- }
+ ResolveContext rc, BundleRevision revision, String pkgName);
}
153 framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
View
@@ -19,6 +19,7 @@
package org.apache.felix.framework.resolver;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -30,6 +31,7 @@
import java.util.SortedSet;
import org.apache.felix.framework.BundleWiringImpl;
import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.ResolveContextImpl;
import org.apache.felix.framework.capabilityset.CapabilitySet;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.Util;
@@ -57,17 +59,17 @@ public ResolverImpl(Logger logger)
m_logger = logger;
}
- public Map<BundleRevision, List<ResolverWire>> resolve(
- ResolverState state,
- Set<BundleRevision> mandatoryRevisions,
- Set<BundleRevision> optionalRevisions,
- Set<BundleRevision> ondemandFragments)
+ public Map<BundleRevision, List<ResolverWire>> resolve(ResolveContext rc)
{
Map<BundleRevision, List<ResolverWire>> wireMap =
new HashMap<BundleRevision, List<ResolverWire>>();
Map<BundleRevision, Packages> revisionPkgMap =
new HashMap<BundleRevision, Packages>();
+ Collection<BundleRevision> mandatoryRevisions = rc.getMandatoryRevisions();
+ Collection<BundleRevision> optionalRevisions = rc.getOptionalRevisions();
+ Collection<BundleRevision> ondemandFragments = (rc instanceof ResolveContextImpl)
+ ? ((ResolveContextImpl) rc).getOndemandRevisions() : Collections.EMPTY_LIST;
boolean retry;
do
{
@@ -86,7 +88,7 @@ public ResolverImpl(Logger logger)
BundleRevision br = it.next();
if (Util.isFragment(br) || (br.getWiring() == null))
{
- allCandidates.populate(state, br, Candidates.MANDATORY);
+ allCandidates.populate(rc, br, Candidates.MANDATORY);
}
else
{
@@ -101,7 +103,7 @@ public ResolverImpl(Logger logger)
boolean isFragment = Util.isFragment(br);
if (isFragment || (br.getWiring() == null))
{
- allCandidates.populate(state, br, Candidates.OPTIONAL);
+ allCandidates.populate(rc, br, Candidates.OPTIONAL);
}
}
@@ -112,12 +114,12 @@ public ResolverImpl(Logger logger)
boolean isFragment = Util.isFragment(br);
if (isFragment)
{
- allCandidates.populate(state, br, Candidates.ON_DEMAND);
+ allCandidates.populate(rc, br, Candidates.ON_DEMAND);
}
}
// Merge any fragments into hosts.
- allCandidates.prepare();
+ allCandidates.prepare(rc);
// Create a combined list of populated revisions; for
// optional revisions. We do not need to consider ondemand
@@ -207,11 +209,11 @@ public ResolverImpl(Logger logger)
if (rethrow != null)
{
BundleRevision faultyRevision =
- getActualBundleRevision(rethrow.getRevision());
- if (rethrow.getRequirement() instanceof HostedRequirement)
+ getDeclaringBundleRevision(rethrow.getRevision());
+ if (rethrow.getRequirement() instanceof WrappedRequirement)
{
faultyRevision =
- ((HostedRequirement) rethrow.getRequirement())
+ ((WrappedRequirement) rethrow.getRequirement())
.getOriginalRequirement().getRevision();
}
if (optionalRevisions.remove(faultyRevision))
@@ -267,8 +269,7 @@ else if (ondemandFragments.remove(faultyRevision))
}
public Map<BundleRevision, List<ResolverWire>> resolve(
- ResolverState state, BundleRevision revision, String pkgName,
- Set<BundleRevision> ondemandFragments)
+ ResolveContext rc, BundleRevision revision, String pkgName)
{
// We can only create a dynamic import if the following
// conditions are met:
@@ -280,11 +281,16 @@ else if (ondemandFragments.remove(faultyRevision))
// The following call checks all of these conditions and returns
// the associated dynamic import and matching capabilities.
Candidates allCandidates =
- getDynamicImportCandidates(state, revision, pkgName);
+ getDynamicImportCandidates(rc, revision, pkgName);
if (allCandidates != null)
{
- Map<BundleRevision, List<ResolverWire>> wireMap = new HashMap<BundleRevision, List<ResolverWire>>();
- Map<BundleRevision, Packages> revisionPkgMap = new HashMap<BundleRevision, Packages>();
+ Collection<BundleRevision> ondemandFragments = (rc instanceof ResolveContextImpl)
+ ? ((ResolveContextImpl) rc).getOndemandRevisions() : Collections.EMPTY_LIST;
+
+ Map<BundleRevision, List<ResolverWire>> wireMap =
+ new HashMap<BundleRevision, List<ResolverWire>>();
+ Map<BundleRevision, Packages> revisionPkgMap =
+ new HashMap<BundleRevision, Packages>();
boolean retry;
do
@@ -298,12 +304,12 @@ else if (ondemandFragments.remove(faultyRevision))
{
if (Util.isFragment(br))
{
- allCandidates.populate(state, br, Candidates.ON_DEMAND);
+ allCandidates.populate(rc, br, Candidates.ON_DEMAND);
}
}
// Merge any fragments into hosts.
- allCandidates.prepare();
+ allCandidates.prepare(rc);
// Record the initial candidate permutation.
m_usesPermutations.add(allCandidates);
@@ -355,11 +361,11 @@ else if (ondemandFragments.remove(faultyRevision))
if (rethrow != null)
{
BundleRevision faultyRevision =
- getActualBundleRevision(rethrow.getRevision());
- if (rethrow.getRequirement() instanceof HostedRequirement)
+ getDeclaringBundleRevision(rethrow.getRevision());
+ if (rethrow.getRequirement() instanceof WrappedRequirement)
{
faultyRevision =
- ((HostedRequirement) rethrow.getRequirement())
+ ((WrappedRequirement) rethrow.getRequirement())
.getOriginalRequirement().getRevision();
}
if (ondemandFragments.remove(faultyRevision))
@@ -394,7 +400,7 @@ else if (ondemandFragments.remove(faultyRevision))
}
private static Candidates getDynamicImportCandidates(
- ResolverState state, BundleRevision revision, String pkgName)
+ ResolveContext rc, BundleRevision revision, String pkgName)
{
// Unresolved revisions cannot dynamically import, nor can the default
// package be dynamically imported.
@@ -438,7 +444,7 @@ private static Candidates getDynamicImportCandidates(
BundleRevision.PACKAGE_NAMESPACE,