Implement most of the functionality for resolver hooks (FELIX-2986) along
with some other changes for R4.3 API (FELIX-2950).
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1136150 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index 964afbc..471ec65 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -162,17 +162,22 @@
}
else
{
- // Dispose of the current revisions.
+ // Get current revision, since we can reuse it.
+ BundleRevisionImpl current = (BundleRevisionImpl) adapt(BundleRevision.class);
+ // Close all existing revisions.
closeRevisions();
+ // Clear all revisions.
+ m_revisions.clear();
- // Now we will purge all old revisions, only keeping the newest one.
+ // Purge all old archive revisions, only keeping the newest one.
m_archive.purge();
- // Lastly, we want to reset our bundle be reinitializing our state
- // and recreating a revision object for the newest revision.
- m_revisions.clear();
- final BundleRevision br = createRevision();
- addRevision(br);
+ // Reset the content of the current bundle revision.
+ current.resetContent(m_archive.getCurrentRevision().getContent());
+ // Re-add the revision to the bundle.
+ addRevision(current);
+
+ // Reset the bundle state.
m_state = Bundle.INSTALLED;
m_stale = false;
m_cachedHeaders.clear();
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleRevisionDependencies.java b/framework/src/main/java/org/apache/felix/framework/BundleRevisionDependencies.java
index ee50cb4..9b4f83a 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleRevisionDependencies.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleRevisionDependencies.java
@@ -157,7 +157,7 @@
// Get exported package name.
String pkgName = (String)
- exportCap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+ exportCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
// Get all importers and requirers for all revisions of the bundle.
// The spec says that require-bundle should be returned with importers.
@@ -171,7 +171,7 @@
{
BundleCapability cap = entry.getKey();
if ((cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
- && cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)
+ && cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
.equals(pkgName))
|| cap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
{
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
index 0341468..2db1452 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
@@ -51,7 +51,6 @@
private final Logger m_logger;
private final Map m_configMap;
private final String m_id;
- private final Content m_content;
private final Map m_headerMap;
private final URLStreamHandler m_streamHandler;
@@ -69,6 +68,7 @@
private final Bundle m_bundle;
+ private Content m_content;
private List<Content> m_contentPath;
private ProtectionDomain m_protectionDomain = null;
private final static SecureAction m_secureAction = new SecureAction();
@@ -362,11 +362,16 @@
// Content access methods.
//
- public Content getContent()
+ public synchronized Content getContent()
{
return m_content;
}
+ synchronized void resetContent(Content content)
+ {
+ m_content = content;
+ }
+
synchronized List<Content> getContentPath()
{
if (m_contentPath == null)
@@ -607,7 +612,7 @@
}
if (index == 0)
{
- return m_content.hasEntry(urlPath);
+ return getContent().hasEntry(urlPath);
}
return getContentPath().get(index - 1).hasEntry(urlPath);
}
@@ -621,7 +626,7 @@
}
if (index == 0)
{
- return m_content.getEntryAsStream(urlPath);
+ return getContent().getEntryAsStream(urlPath);
}
return getContentPath().get(index - 1).getEntryAsStream(urlPath);
}
@@ -634,7 +639,7 @@
}
if (index == 0)
{
- return m_content.getEntryAsURL(urlPath);
+ return getContent().getEntryAsURL(urlPath);
}
return getContentPath().get(index - 1).getEntryAsURL(urlPath);
}
@@ -676,6 +681,7 @@
m_logger.log(Logger.LOG_ERROR, "Error releasing revision: " + ex.getMessage(), ex);
}
m_content.close();
+ m_content = null;
for (int i = 0; (m_contentPath != null) && (i < m_contentPath.size()); i++)
{
m_contentPath.get(i).close();
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
index a1a45cd..bd12416 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -403,7 +403,7 @@
{
m_wires.add(wire);
m_importedPkgs.put(
- (String) wire.getCapability().getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR),
+ (String) wire.getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
wire.getProviderWiring().getRevision());
}
@@ -537,7 +537,7 @@
{
m_resolver.resolve(m_revision);
}
- catch (ResolveException ex)
+ catch (Exception ex)
{
// The spec states that if the bundle cannot be resolved, then
// only the local bundle's resources should be searched. So we
@@ -639,6 +639,10 @@
{
// Ignore this since it is likely normal.
}
+ catch (BundleException ex)
+ {
+ // Ignore this since it is likely the result of a resolver hook.
+ }
if (provider != null)
{
// Delegate to the provider revision.
@@ -886,20 +890,11 @@
}
}
}
- catch (ResolveException ex)
+// TODO: OSGi R4.3 - If we eliminate resolving from this method, then we can
+// simplify this catch, since resolve throws resolve and bundle exceptions.
+ catch (Exception ex)
{
- if (isClass)
- {
- // We do not use the resolve exception as the
- // cause of the exception, since this would
- // potentially leak internal module information.
- throw new ClassNotFoundException(
- name + " not found because "
- + getBundle()
- + " cannot resolve: "
- + ex.getRequirement());
- }
- else
+ if (!isClass && (ex instanceof ResolveException))
{
// The spec states that if the bundle cannot be resolved, then
// only the local bundle's resources should be searched. So we
@@ -909,13 +904,38 @@
{
return url;
}
+ }
- // We need to throw a resource not found exception.
- throw new ResourceNotFoundException(
- name + " not found because "
- + getBundle()
- + " cannot resolve: "
- + ex.getRequirement());
+ if (isClass)
+ {
+ if (!(ex instanceof ClassNotFoundException))
+ {
+ ClassNotFoundException cnfe = new ClassNotFoundException(
+ name
+ + " not found in "
+ + getBundle()
+ + " : "
+ + ex.getMessage());
+ex.printStackTrace();
+ cnfe.initCause(ex);
+ throw cnfe;
+ }
+ throw (ClassNotFoundException) ex;
+ }
+ else
+ {
+ if (!(ex instanceof ResourceNotFoundException))
+ {
+ ResourceNotFoundException rnfe = new ResourceNotFoundException(
+ name
+ + " not found in "
+ + getBundle()
+ + " : "
+ + ex.getMessage());
+ rnfe.initCause(ex);
+ throw rnfe;
+ }
+ throw (ResourceNotFoundException) ex;
}
}
finally
@@ -965,10 +985,14 @@
return result;
}
- // If no class was found, then we must throw an exception
+ // If no class or resource was found, then we must throw an exception
// since the provider of this package did not contain the
// requested class and imported packages are atomic.
- throw new ClassNotFoundException(name);
+ if (isClass)
+ {
+ throw new ClassNotFoundException(name);
+ }
+ throw new ResourceNotFoundException(name);
}
// Check if the package is required.
@@ -1017,6 +1041,10 @@
{
// Ignore this since it is likely normal.
}
+ catch (BundleException ex)
+ {
+ // Ignore this since it is likely the result of a resolver hook.
+ }
// If the dynamic import was successful, then this initial
// time we must directly return the result from dynamically
@@ -1858,7 +1886,7 @@
for (int i = 0; (wires != null) && (i < wires.size()); i++)
{
if (wires.get(i).getCapability().getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE) &&
- wires.get(i).getCapability().getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
+ wires.get(i).getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
{
String exporter = wires.get(i).getProviderWiring().getBundle().toString();
@@ -1949,7 +1977,7 @@
// Try to see if there is an exporter available.
Map<String, String> dirs = Collections.EMPTY_MAP;
Map<String, Object> attrs = new HashMap<String, Object>(1);
- attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+ attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
BundleRequirementImpl req = new BundleRequirementImpl(
revision, BundleRevision.PACKAGE_NAMESPACE, dirs, attrs);
Set<BundleCapability> exporters = resolver.getCandidates(req, false);
@@ -1988,7 +2016,7 @@
// Next, check to see if there are any exporters for the package at all.
Map<String, String> dirs = Collections.EMPTY_MAP;
Map<String, Object> attrs = new HashMap<String, Object>(1);
- attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+ attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
BundleRequirementImpl req = new BundleRequirementImpl(
revision, BundleRevision.PACKAGE_NAMESPACE, dirs, attrs);
Set<BundleCapability> exports = resolver.getCandidates(req, false);
diff --git a/framework/src/main/java/org/apache/felix/framework/ExportedPackageImpl.java b/framework/src/main/java/org/apache/felix/framework/ExportedPackageImpl.java
index 01fab7a..be2b1db 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExportedPackageImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExportedPackageImpl.java
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -42,7 +42,7 @@
m_exportingBundle = exporter;
m_exportingRevision = revision;
m_export = export;
- m_pkgName = (String) m_export.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+ m_pkgName = (String) m_export.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
m_version = (!m_export.getAttributes().containsKey(BundleCapabilityImpl.VERSION_ATTR))
? Version.emptyVersion
: (Version) m_export.getAttributes().get(BundleCapabilityImpl.VERSION_ATTR);
diff --git a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
index 4b6a368..16e6f46 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -447,7 +447,7 @@
// Append exported package information.
exportSB.append(m_capabilities.get(i)
- .getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+ .getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
for (Entry<String, String> entry
: m_capabilities.get(i).getDirectives().entrySet())
{
@@ -460,7 +460,7 @@
for (Entry<String, Object> entry
: m_capabilities.get(i).getAttributes().entrySet())
{
- if (!entry.getKey().equals(BundleCapabilityImpl.PACKAGE_ATTR)
+ if (!entry.getKey().equals(BundleRevision.PACKAGE_NAMESPACE)
&& !entry.getKey().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)
&& !entry.getKey().equals(Constants.BUNDLE_VERSION_ATTRIBUTE))
{
@@ -474,7 +474,7 @@
// Remember exported packages.
exportNames.add(m_capabilities.get(i)
- .getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+ .getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
}
}
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 35269bc..7dfb4ae 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -1531,11 +1531,23 @@
{
return null;
}
-// TODO: OSGi R4.3 - Currently, we try to resolve resource requests in
+// TODO: OSGi R4.3 - Previously, we try to resolve resource requests in
// findClassOrResourceByDelegation() and fall back to local resource
-// searching if it fails. Perhaps we should attempt the resolve here
-// and do the local searching here. This means we could get rid of
-// resolve attempts in findClassOrResourceByDelegation().
+// searching if it fails. Now we must attempt the resolve here since
+// we cannot search by delegation until we are resolved and do the local
+// searching here if we fail. This means we could get rid of resolve
+// attempts in findClassOrResourceByDelegation().
+ try
+ {
+ resolveBundleRevision(bundle.adapt(BundleRevision.class));
+ }
+ catch (Exception ex)
+ {
+ // Ignore.
+ }
+
+ // If the bundle revision isn't resolved, then just search
+ // locally, otherwise delegate.
if (bundle.adapt(BundleRevision.class).getWiring() == null)
{
return ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
@@ -1561,11 +1573,21 @@
{
return null;
}
-// TODO: OSGi R4.3 - Currently, we try to resolve resource requests in
-// findResourcesByDelegation() and fall back to local resource
-// searching if it fails. Perhaps we should attempt the resolve here
-// and do the local searching here. This means we could get rid of
-// resolve attempts in findResourcesByDelegation().
+// TODO: OSGi R4.3 - Previously, we try to resolve resource requests in
+// findClassOrResourceByDelegation() and fall back to local resource
+// searching if it fails. Now we must attempt the resolve here since
+// we cannot search by delegation until we are resolved and do the local
+// searching here if we fail. This means we could get rid of resolve
+// attempts in findClassOrResourceByDelegation().
+ try
+ {
+ resolveBundleRevision(bundle.adapt(BundleRevision.class));
+ }
+ catch (Exception ex)
+ {
+ // Ignore.
+ }
+
if (bundle.adapt(BundleRevision.class).getWiring() == null)
{
return ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
@@ -1726,7 +1748,7 @@
{
try
{
- resolveBundle(bundle);
+ resolveBundleRevision(bundle.adapt(BundleRevision.class));
}
catch (BundleException ex)
{
@@ -1881,7 +1903,7 @@
case Bundle.ACTIVE:
return;
case Bundle.INSTALLED:
- resolveBundle(bundle);
+ resolveBundleRevision(bundle.adapt(BundleRevision.class));
// No break.
case Bundle.RESOLVED:
// Set the bundle's context.
@@ -2037,7 +2059,9 @@
}
// Rethrow all other exceptions as a BundleException.
- throw new BundleException("Activator start error in bundle " + bundle + ".", th);
+ throw new BundleException(
+ "Activator start error in bundle " + bundle + ".",
+ BundleException.ACTIVATOR_ERROR, th);
}
}
finally
@@ -3427,7 +3451,7 @@
{
// First, get all exporters of the package.
Map<String, Object> attrs = new HashMap<String, Object>(1);
- attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+ attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
BundleRequirementImpl req = new BundleRequirementImpl(
null,
BundleRevision.PACKAGE_NAMESPACE,
@@ -3580,9 +3604,9 @@
// BundleWiring.getCapabilities() returns the proper result. We probably
// Won't even need this method.
String pkgName = (String)
- cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+ cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
Map<String, Object> attrs = new HashMap<String, Object>(1);
- attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+ attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
BundleRequirementImpl req =
new BundleRequirementImpl(
null,
@@ -3637,22 +3661,15 @@
try
{
+ // Remember original targets.
+ Collection<Bundle> originalTargets = targets;
+
// Determine set of bundles to be resolved, which is either the
// specified bundles or all bundles if null.
if (targets == null)
{
- targets = new ArrayList<Bundle>();
-
- // Add all unresolved bundles to the list.
- Iterator iter = m_installedBundles[LOCATION_MAP_IDX].values().iterator();
- while (iter.hasNext())
- {
- BundleImpl bundle = (BundleImpl) iter.next();
- if (bundle.getState() == Bundle.INSTALLED)
- {
- targets.add(bundle);
- }
- }
+ // Add all bundles to the list.
+ targets = m_installedBundles[LOCATION_MAP_IDX].values();
}
// Now resolve each target bundle.
@@ -3661,17 +3678,45 @@
// If there are targets, then resolve each one.
if (!targets.isEmpty())
{
+ // Get bundle revisions for bundles in INSTALLED state.
+ Set<BundleRevision> revisions =
+ new HashSet<BundleRevision>(targets.size());
for (Bundle b : targets)
{
- try
+ if (b.getState() != Bundle.UNINSTALLED)
{
- resolveBundle((BundleImpl) b);
+ revisions.add(b.adapt(BundleRevision.class));
}
- catch (BundleException ex)
+ }
+ // If we had to filter any of the original targets, then
+ // the return result will be false regardless.
+ if ((originalTargets != null) && (originalTargets.size() != revisions.size()))
+ {
+ result = false;
+ }
+ try
+ {
+ m_resolver.resolve(revisions);
+ if (result)
{
- result = false;
+ for (BundleRevision br : revisions)
+ {
+ if (br.getWiring() == null)
+ {
+ result = false;
+ break;
+ }
+ }
}
}
+ catch (ResolveException ex)
+ {
+ result = false;
+ }
+ catch (BundleException ex)
+ {
+ result = false;
+ }
}
return result;
@@ -3683,11 +3728,11 @@
}
}
- private void resolveBundle(Bundle bundle) throws BundleException
+ private void resolveBundleRevision(BundleRevision revision) throws BundleException
{
try
{
- m_resolver.resolve(bundle.adapt(BundleRevision.class));
+ m_resolver.resolve(revision);
}
catch (ResolveException ex)
{
@@ -3696,11 +3741,11 @@
Bundle b = ex.getRevision().getBundle();
throw new BundleException(
"Unresolved constraint in bundle "
- + b + ": " + ex.getMessage());
+ + b + ": " + ex.getMessage(), BundleException.RESOLVE_ERROR);
}
else
{
- throw new BundleException(ex.getMessage());
+ throw new BundleException(ex.getMessage(), BundleException.RESOLVE_ERROR);
}
}
}
diff --git a/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java b/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java
deleted file mode 100644
index fc4ce43..0000000
--- a/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * 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.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-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.resolver.CandidateComparator;
-import org.apache.felix.framework.resolver.ResolveException;
-import org.apache.felix.framework.resolver.Resolver;
-import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.util.manifestparser.R4Library;
-import org.apache.felix.framework.wiring.BundleCapabilityImpl;
-import org.apache.felix.framework.wiring.BundleRequirementImpl;
-import org.osgi.framework.BundlePermission;
-import org.osgi.framework.Constants;
-import org.osgi.framework.PackagePermission;
-import org.osgi.framework.wiring.BundleCapability;
-import org.osgi.framework.wiring.BundleRevision;
-import org.osgi.framework.wiring.BundleWire;
-
-class ResolverStateImpl implements Resolver.ResolverState
-{
- private final Logger m_logger;
- // 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;
- // 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(Logger logger, String fwkExecEnvStr)
- {
- m_logger = logger;
- m_revisions = new HashSet<BundleRevision>();
- m_fragments = new HashSet<BundleRevision>();
- m_capSets = new HashMap<String, CapabilitySet>();
-
- m_fwkExecEnvStr = (fwkExecEnvStr != null) ? fwkExecEnvStr.trim() : null;
- m_fwkExecEnvSet = parseExecutionEnvironments(fwkExecEnvStr);
-
- List<String> indices = new ArrayList<String>();
- indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
- m_capSets.put(BundleRevision.BUNDLE_NAMESPACE, new CapabilitySet(indices, true));
-
- indices = new ArrayList<String>();
- indices.add(BundleCapabilityImpl.PACKAGE_ATTR);
- m_capSets.put(BundleRevision.PACKAGE_NAMESPACE, new CapabilitySet(indices, true));
-
- indices = new ArrayList<String>();
- indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
- m_capSets.put(BundleRevision.HOST_NAMESPACE, new CapabilitySet(indices, true));
- }
-
- synchronized void addRevision(BundleRevision br)
- {
- m_revisions.add(br);
- List<BundleCapability> caps = (br.getWiring() == null)
- ? br.getDeclaredCapabilities(null)
- : br.getWiring().getCapabilities(null);
- if (caps != null)
- {
- for (BundleCapability cap : caps)
- {
- CapabilitySet capSet = m_capSets.get(cap.getNamespace());
- if (capSet == null)
- {
- capSet = new CapabilitySet(null, true);
- m_capSets.put(cap.getNamespace(), capSet);
- }
- capSet.addCapability(cap);
- }
- }
-
- if (Util.isFragment(br))
- {
- m_fragments.add(br);
- }
- }
-
- 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)
- {
- for (BundleCapability cap : caps)
- {
- CapabilitySet capSet = m_capSets.get(cap.getNamespace());
- if (capSet != null)
- {
- capSet.removeCapability(cap);
- }
- }
- }
-
- if (Util.isFragment(br))
- {
- m_fragments.remove(br);
- }
- }
-
- synchronized Set<BundleRevision> getFragments()
- {
- 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(BundleCapabilityImpl.PACKAGE_ATTR)
- .equals(cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)))
- {
- m_capSets.get(BundleRevision.PACKAGE_NAMESPACE).removeCapability(cap);
- break;
- }
- }
- }
- }
- }
- }
-
- //
- // ResolverState methods.
- //
-
- public synchronized SortedSet<BundleCapability> getCandidates(
- BundleRequirementImpl req, boolean obeyMandatory)
- {
- BundleRevisionImpl reqRevision = (BundleRevisionImpl) req.getRevision();
- SortedSet<BundleCapability> result =
- new TreeSet<BundleCapability>(new CandidateComparator());
-
- CapabilitySet capSet = m_capSets.get(req.getNamespace());
- if (capSet != null)
- {
- Set<BundleCapability> matches = capSet.match(req.getFilter(), obeyMandatory);
- for (BundleCapability cap : matches)
- {
- if (System.getSecurityManager() != null)
- {
- if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE) && (
- !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
- new PackagePermission((String) cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR),
- PackagePermission.EXPORTONLY)) ||
- !((reqRevision == null) ||
- ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
- new PackagePermission((String) cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR),
- cap.getRevision().getBundle(),PackagePermission.IMPORT))
- )))
- {
- if (reqRevision != cap.getRevision())
- {
- continue;
- }
- }
- else if (req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE) && (
- !((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))
- )))
- {
- continue;
- }
- else if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE) &&
- (!((BundleProtectionDomain) reqRevision.getProtectionDomain())
- .impliesDirect(new BundlePermission(
- reqRevision.getSymbolicName(),
- BundlePermission.FRAGMENT))
- || !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain())
- .impliesDirect(new BundlePermission(
- cap.getRevision().getSymbolicName(),
- BundlePermission.HOST))))
- {
- continue;
- }
- }
-
- if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE)
- && (cap.getRevision().getWiring() != null))
- {
- continue;
- }
-
- result.add(cap);
- }
- }
-
- return result;
- }
-
- public 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))
- {
- 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);
- }
- }
- }
- }
-
- public 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.
-// TODO: OSGi R4.3 - Is it sufficient to just check declared native libs here?
-// List<R4Library> libs = ((BundleWiringImpl) revision.getWiring()).getNativeLibraries();
- List<R4Library> libs = ((BundleRevisionImpl) revision).getDeclaredNativeLibraries();
- if (libs != 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);
- }
- }
- }
-
- //
- // 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
- * system variable 'org.osgi.framework.executionenvironment'.
- * @param fwkExecEnvStr Comma delimited string of provided execution environments
- * @return the parsed set of execution environments
- **/
- private static Set<String> parseExecutionEnvironments(String fwkExecEnvStr)
- {
- Set<String> newSet = new HashSet<String>();
- if (fwkExecEnvStr != null)
- {
- StringTokenizer tokens = new StringTokenizer(fwkExecEnvStr, ",");
- while (tokens.hasMoreTokens())
- {
- newSet.add(tokens.nextToken().trim());
- }
- }
- return newSet;
- }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java b/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
index 2856581..83955d6 100644
--- a/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
+++ b/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
@@ -19,6 +19,7 @@
package org.apache.felix.framework;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -27,17 +28,30 @@
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.resolver.CandidateComparator;
import org.apache.felix.framework.resolver.ResolveException;
import org.apache.felix.framework.resolver.Resolver;
import org.apache.felix.framework.resolver.ResolverImpl;
import org.apache.felix.framework.resolver.ResolverWire;
+import org.apache.felix.framework.util.ShrinkableCollection;
import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.R4Library;
import org.apache.felix.framework.wiring.BundleCapabilityImpl;
import org.apache.felix.framework.wiring.BundleRequirementImpl;
import org.apache.felix.framework.wiring.BundleWireImpl;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundlePermission;
import org.osgi.framework.Constants;
+import org.osgi.framework.PackagePermission;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleRevision;
@@ -48,6 +62,9 @@
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;
StatefulResolver(Felix felix)
{
@@ -72,15 +89,13 @@
return m_resolverState.getCandidates(req, obeyMandatory);
}
- void resolve(BundleRevision rootRevision) throws ResolveException
+ void resolve(BundleRevision rootRevision) throws ResolveException, BundleException
{
// Although there is a race condition to check the bundle state
// then lock it, we do this because we don't want to acquire the
// a lock just to check if the revision is resolved, which itself
// is a safe read. If the revision isn't resolved, we end up double
// check the resolved status later.
-// TODO: OSGi R4.3 - This locking strategy here depends on how we ultimately
-// implement getWiring(), which may change.
if (rootRevision.getWiring() == null)
{
// Acquire global lock.
@@ -91,26 +106,151 @@
"Unable to acquire global lock for resolve.", rootRevision, null);
}
+ // Make sure we are not already resolving, which can be
+ // the case if a resolver hook does something bad.
+ if (m_isResolving)
+ {
+ m_felix.releaseGlobalLock();
+ throw new IllegalStateException("Nested resolve operations not allowed.");
+ }
+ m_isResolving = true;
+
Map<BundleRevision, List<ResolverWire>> wireMap = null;
try
{
- BundleImpl bundle = (BundleImpl) rootRevision.getBundle();
-
// Extensions are resolved differently.
+ BundleImpl bundle = (BundleImpl) rootRevision.getBundle();
if (bundle.isExtension())
{
return;
}
- // Resolve the revision.
- wireMap = m_resolver.resolve(
- m_resolverState, rootRevision, m_resolverState.getFragments());
+ // Get resolver hook factories.
+ Set<ServiceReference<ResolverHookFactory>> hookRefs =
+ m_felix.getHooks(ResolverHookFactory.class);
+ if (!hookRefs.isEmpty())
+ {
+ // Create triggers list.
+ List<BundleRevision> triggers = new ArrayList<BundleRevision>(1);
+ triggers.add(rootRevision);
+ triggers = Collections.unmodifiableList(triggers);
- // Mark all revisions as resolved.
+ // Create resolver hook objects by calling begin() on factory.
+ for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+ {
+ try
+ {
+ ResolverHook hook = m_felix.getService(m_felix, ref).begin(triggers);
+ if (hook != null)
+ {
+ m_hooks.add(hook);
+ }
+ }
+ catch (Throwable ex)
+ {
+ throw new BundleException(
+ "Resolver hook exception: " + ex.getMessage(),
+ BundleException.REJECTED_BY_HOOK,
+ ex);
+ }
+ }
+
+ // Ask hooks to indicate which revisions should not be resolved.
+ m_whitelist =
+ new ShrinkableCollection<BundleRevision>(
+ m_resolverState.getUnresolvedRevisions());
+ int originalSize = m_whitelist.size();
+ for (ResolverHook hook : m_hooks)
+ {
+ try
+ {
+ hook.filterResolvable(m_whitelist);
+ }
+ catch (Throwable ex)
+ {
+ throw new BundleException(
+ "Resolver hook exception: " + ex.getMessage(),
+ BundleException.REJECTED_BY_HOOK,
+ ex);
+ }
+ }
+ // If nothing was removed, then just null the whitelist
+ // as an optimization.
+ if (m_whitelist.size() == originalSize)
+ {
+ m_whitelist = null;
+ }
+
+ // Check to make sure the target revision is allowed to resolve.
+ if ((m_whitelist != null) && !m_whitelist.contains(rootRevision))
+ {
+ throw new ResolveException(
+ "Resolver hook prevented resolution.", rootRevision, null);
+ }
+ }
+
+ // Catch any resolve exception to rethrow later because
+ // we may need to call end() on resolver hooks.
+ ResolveException rethrow = null;
+ try
+ {
+ // Resolve the revision.
+ wireMap = m_resolver.resolve(
+ m_resolverState, rootRevision, m_resolverState.getFragments());
+ }
+ catch (ResolveException ex)
+ {
+ rethrow = ex;
+ }
+
+ // If we have resolver hooks, we must call end() on them.
+ if (!hookRefs.isEmpty())
+ {
+ // Verify that all resolver hook service references are still valid
+ // Call end() on resolver hooks.
+ for (ResolverHook hook : m_hooks)
+ {
+// TODO: OSGi R4.3/RESOLVER HOOK - We likely need to put these hooks into a map
+// to their svc ref since we aren't supposed to call end() on unregistered
+// but currently we call end() on all.
+ hook.end();
+ }
+ // Verify that all hook service references are still valid
+ // and unget all resolver hook factories.
+ boolean invalid = false;
+ for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+ {
+ if (ref.getBundle() == null)
+ {
+ invalid = true;
+ }
+ m_felix.ungetService(m_felix, ref);
+ }
+ if (invalid)
+ {
+ throw new BundleException(
+ "Resolver hook service unregistered during resolve.",
+ BundleException.REJECTED_BY_HOOK);
+ }
+ }
+
+ // If the resolve failed, rethrow the exception.
+ if (rethrow != null)
+ {
+ throw rethrow;
+ }
+
+ // Otherwise, mark all revisions as resolved.
markResolvedRevisions(wireMap);
}
finally
{
+ // Clear resolving flag.
+ m_isResolving = false;
+ // Clear whitelist.
+ m_whitelist = null;
+ // Always clear any hooks.
+ m_hooks.clear();
// Always release the global lock.
m_felix.releaseGlobalLock();
}
@@ -119,8 +259,186 @@
}
}
+// TODO: OSGi R4.3 - Isn't this method just a generalization of the above method?
+// Can't we combine them and perhaps simplify the various resolve() methods
+// here and in Felix.java too?
+ void resolve(Set<BundleRevision> revisions) throws ResolveException, BundleException
+ {
+ // Acquire global lock.
+ boolean locked = m_felix.acquireGlobalLock();
+ if (!locked)
+ {
+ throw new ResolveException(
+ "Unable to acquire global lock for resolve.", null, null);
+ }
+
+ // Make sure we are not already resolving, which can be
+ // the case if a resolver hook does something bad.
+ if (m_isResolving)
+ {
+ m_felix.releaseGlobalLock();
+ throw new IllegalStateException("Nested resolve operations not allowed.");
+ }
+ m_isResolving = true;
+
+ Map<BundleRevision, List<ResolverWire>> wireMap = null;
+ try
+ {
+ // Make our own copy of revisions.
+ revisions = new HashSet<BundleRevision>(revisions);
+
+ // Extensions are resolved differently.
+ for (Iterator<BundleRevision> it = revisions.iterator(); it.hasNext(); )
+ {
+ BundleImpl bundle = (BundleImpl) it.next().getBundle();
+ if (bundle.isExtension())
+ {
+ it.remove();
+ }
+ }
+
+ // Get resolver hook factories.
+ Set<ServiceReference<ResolverHookFactory>> hookRefs =
+ m_felix.getHooks(ResolverHookFactory.class);
+ if (!hookRefs.isEmpty())
+ {
+ // Create triggers list.
+ Collection<BundleRevision> triggers = Collections.unmodifiableSet(revisions);
+
+ // Create resolver hook objects by calling begin() on factory.
+ for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+ {
+ try
+ {
+ ResolverHookFactory factory = m_felix.getService(m_felix, ref);
+ if (factory != null)
+ {
+ ResolverHook hook = factory.begin(triggers);
+ if (hook != null)
+ {
+ m_hooks.add(hook);
+ }
+ }
+ }
+ catch (Throwable ex)
+ {
+ throw new BundleException(
+ "Resolver hook exception: " + ex.getMessage(),
+ BundleException.REJECTED_BY_HOOK,
+ ex);
+ }
+ }
+
+ // Ask hooks to indicate which revisions should not be resolved.
+ m_whitelist =
+ new ShrinkableCollection<BundleRevision>(
+ m_resolverState.getUnresolvedRevisions());
+ int originalSize = m_whitelist.size();
+ for (ResolverHook hook : m_hooks)
+ {
+ try
+ {
+ hook.filterResolvable(m_whitelist);
+ }
+ catch (Throwable ex)
+ {
+ throw new BundleException(
+ "Resolver hook exception: " + ex.getMessage(),
+ BundleException.REJECTED_BY_HOOK,
+ ex);
+ }
+ }
+ // If nothing was removed, then just null the whitelist
+ // as an optimization.
+ if (m_whitelist.size() == originalSize)
+ {
+ m_whitelist = null;
+ }
+
+ // Check to make sure the target revision is allowed to resolve.
+ if (m_whitelist != null)
+ {
+ revisions.retainAll(m_whitelist);
+ if (revisions.isEmpty())
+ {
+ throw new ResolveException(
+ "Resolver hook prevented resolution.", null, null);
+ }
+ }
+ }
+
+ // Catch any resolve exception to rethrow later because
+ // we may need to call end() on resolver hooks.
+ ResolveException rethrow = null;
+ try
+ {
+ // Resolve the revision.
+// TODO: OSGi R4.3 - Shouldn't we still be passing in greedy attach fragments here?
+ wireMap = m_resolver.resolve(
+ m_resolverState, revisions, m_resolverState.getFragments());
+ }
+ catch (ResolveException ex)
+ {
+ rethrow = ex;
+ }
+
+ // If we have resolver hooks, we must call end() on them.
+ if (!hookRefs.isEmpty())
+ {
+ // Verify that all resolver hook service references are still valid
+ // Call end() on resolver hooks.
+ for (ResolverHook hook : m_hooks)
+ {
+// TODO: OSGi R4.3/RESOLVER HOOK - We likely need to put these hooks into a map
+// to their svc ref since we aren't supposed to call end() on unregistered
+// but currently we call end() on all.
+ hook.end();
+ }
+ // Verify that all hook service references are still valid
+ // and unget all resolver hook factories.
+ boolean invalid = false;
+ for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+ {
+ if (ref.getBundle() == null)
+ {
+ invalid = true;
+ }
+ m_felix.ungetService(m_felix, ref);
+ }
+ if (invalid)
+ {
+ throw new BundleException(
+ "Resolver hook service unregistered during resolve.",
+ BundleException.REJECTED_BY_HOOK);
+ }
+ }
+
+ // If the resolve failed, rethrow the exception.
+ if (rethrow != null)
+ {
+ throw rethrow;
+ }
+
+ // Otherwise, mark all revisions as resolved.
+ markResolvedRevisions(wireMap);
+ }
+ finally
+ {
+ // Clear resolving flag.
+ m_isResolving = false;
+ // Clear whitelist.
+ m_whitelist = null;
+ // Always clear any hooks.
+ m_hooks.clear();
+ // Always release the global lock.
+ m_felix.releaseGlobalLock();
+ }
+
+ fireResolvedEvents(wireMap);
+ }
+
BundleRevision resolve(BundleRevision revision, String pkgName)
- throws ResolveException
+ throws ResolveException, BundleException
{
BundleRevision provider = null;
@@ -139,6 +457,14 @@
"Unable to acquire global lock for resolve.", revision, null);
}
+ // Make sure we are not already resolving, which can be
+ // the case if a resolver hook does something bad.
+ if (m_isResolving)
+ {
+ throw new IllegalStateException("Nested resolve operations not allowed.");
+ }
+ m_isResolving = true;
+
Map<BundleRevision, List<ResolverWire>> wireMap = null;
try
{
@@ -150,9 +476,117 @@
.getImportedPackageSource(pkgName);
if (provider == null)
{
- wireMap = m_resolver.resolve(
- m_resolverState, revision, pkgName,
- m_resolverState.getFragments());
+ // Get resolver hook factories.
+ Set<ServiceReference<ResolverHookFactory>> hookRefs =
+ m_felix.getHooks(ResolverHookFactory.class);
+ if (!hookRefs.isEmpty())
+ {
+ // Create triggers list.
+ List<BundleRevision> triggers = new ArrayList<BundleRevision>(1);
+ triggers.add(revision);
+ triggers = Collections.unmodifiableList(triggers);
+
+ // Create resolver hook objects by calling begin() on factory.
+ for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+ {
+ try
+ {
+ ResolverHook hook = m_felix.getService(m_felix, ref).begin(triggers);
+ if (hook != null)
+ {
+ m_hooks.add(hook);
+ }
+ }
+ catch (Throwable ex)
+ {
+ throw new BundleException(
+ "Resolver hook exception: " + ex.getMessage(),
+ BundleException.REJECTED_BY_HOOK,
+ ex);
+ }
+ }
+
+ // Ask hooks to indicate which revisions should not be resolved.
+ m_whitelist =
+ new ShrinkableCollection<BundleRevision>(
+ m_resolverState.getUnresolvedRevisions());
+ int originalSize = m_whitelist.size();
+ for (ResolverHook hook : m_hooks)
+ {
+ try
+ {
+ hook.filterResolvable(m_whitelist);
+ }
+ catch (Throwable ex)
+ {
+ throw new BundleException(
+ "Resolver hook exception: " + ex.getMessage(),
+ BundleException.REJECTED_BY_HOOK,
+ ex);
+ }
+ }
+ // If nothing was removed, then just null the whitelist
+ // as an optimization.
+ if (m_whitelist.size() == originalSize)
+ {
+ m_whitelist = null;
+ }
+
+ // Since this is a dynamic import, the root revision is
+ // already resolved, so we don't need to check it against
+ // the whitelist as we do in other cases.
+ }
+
+ // Catch any resolve exception to rethrow later because
+ // we may need to call end() on resolver hooks.
+ ResolveException rethrow = null;
+ try
+ {
+ wireMap = m_resolver.resolve(
+ m_resolverState, revision, pkgName,
+ m_resolverState.getFragments());
+ }
+ catch (ResolveException ex)
+ {
+ rethrow = ex;
+ }
+
+ // If we have resolver hooks, we must call end() on them.
+ if (!hookRefs.isEmpty())
+ {
+ // Verify that all resolver hook service references are still valid
+ // Call end() on resolver hooks.
+ for (ResolverHook hook : m_hooks)
+ {
+// TODO: OSGi R4.3/RESOLVER HOOK - We likely need to put these hooks into a map
+// to their svc ref since we aren't supposed to call end() on unregistered
+// but currently we call end() on all.
+ hook.end();
+ }
+ // Verify that all hook service references are still valid
+ // and unget all resolver hook factories.
+ boolean invalid = false;
+ for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+ {
+ if (ref.getBundle() == null)
+ {
+ invalid = true;
+ }
+ m_felix.ungetService(m_felix, ref);
+ }
+ if (invalid)
+ {
+ throw new BundleException(
+ "Resolver hook service unregistered during resolve.",
+ BundleException.REJECTED_BY_HOOK);
+ }
+ }
+
+ // If the resolve failed, rethrow the exception.
+ if (rethrow != null)
+ {
+ throw rethrow;
+ }
if ((wireMap != null) && wireMap.containsKey(revision))
{
@@ -190,6 +624,12 @@
}
finally
{
+ // Clear resolving flag.
+ m_isResolving = false;
+ // Clear whitelist.
+ m_whitelist = null;
+ // Always clear any hooks.
+ m_hooks.clear();
// Always release the global lock.
m_felix.releaseGlobalLock();
}
@@ -225,7 +665,7 @@
for (BundleCapability cap : revision.getWiring().getCapabilities(null))
{
if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
- && cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
+ && cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
{
return false;
}
@@ -242,7 +682,7 @@
// there is a matching one for the package from which we want to
// load a class.
Map<String, Object> attrs = new HashMap(1);
- attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+ attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
BundleRequirementImpl req = new BundleRequirementImpl(
revision,
BundleRevision.PACKAGE_NAMESPACE,
@@ -342,7 +782,7 @@
{
importedPkgs.put(
(String) rw.getCapability().getAttributes()
- .get(BundleCapabilityImpl.PACKAGE_ATTR),
+ .get(BundleRevision.PACKAGE_NAMESPACE),
rw.getProvider());
}
else if (rw.getCapability().getNamespace()
@@ -527,7 +967,7 @@
if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
{
pkgs.add((String)
- cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+ cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
}
}
@@ -577,4 +1017,351 @@
return pkgs;
}
+
+ class ResolverStateImpl implements Resolver.ResolverState
+ {
+ private final Logger m_logger;
+ // 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;
+ // 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(Logger logger, String fwkExecEnvStr)
+ {
+ m_logger = logger;
+ m_revisions = new HashSet<BundleRevision>();
+ m_fragments = new HashSet<BundleRevision>();
+ m_capSets = new HashMap<String, CapabilitySet>();
+
+ 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));
+ }
+
+// TODO: OSGi R4.3/RESOLVER HOOK - We could maintain a separate list to optimize this.
+ synchronized Set<BundleRevision> getUnresolvedRevisions()
+ {
+ Set<BundleRevision> unresolved = new HashSet<BundleRevision>();
+ for (BundleRevision revision : m_revisions)
+ {
+ if (revision.getWiring() == null)
+ {
+ unresolved.add(revision);
+ }
+ }
+ return unresolved;
+ }
+
+ synchronized void addRevision(BundleRevision br)
+ {
+ m_revisions.add(br);
+ List<BundleCapability> caps = (br.getWiring() == null)
+ ? br.getDeclaredCapabilities(null)
+ : br.getWiring().getCapabilities(null);
+ if (caps != null)
+ {
+ for (BundleCapability cap : caps)
+ {
+ CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+ if (capSet == null)
+ {
+ capSet = new CapabilitySet(null, true);
+ m_capSets.put(cap.getNamespace(), capSet);
+ }
+ capSet.addCapability(cap);
+ }
+ }
+
+ if (Util.isFragment(br))
+ {
+ m_fragments.add(br);
+ }
+ }
+
+ 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)
+ {
+ for (BundleCapability cap : caps)
+ {
+ CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+ if (capSet != null)
+ {
+ capSet.removeCapability(cap);
+ }
+ }
+ }
+
+ if (Util.isFragment(br))
+ {
+ m_fragments.remove(br);
+ }
+ }
+
+ synchronized Set<BundleRevision> getFragments()
+ {
+ 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.
+ //
+
+ public synchronized SortedSet<BundleCapability> getCandidates(
+ BundleRequirementImpl req, boolean obeyMandatory)
+ {
+ BundleRevisionImpl reqRevision = (BundleRevisionImpl) req.getRevision();
+ SortedSet<BundleCapability> result =
+ new TreeSet<BundleCapability>(new CandidateComparator());
+
+ CapabilitySet capSet = m_capSets.get(req.getNamespace());
+ if (capSet != null)
+ {
+ Set<BundleCapability> matches = capSet.match(req.getFilter(), obeyMandatory);
+ for (BundleCapability cap : matches)
+ {
+ if (System.getSecurityManager() != null)
+ {
+ if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE) && (
+ !((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())
+ {
+ continue;
+ }
+ }
+ else if (req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE) && (
+ !((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))
+ )))
+ {
+ continue;
+ }
+ else if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE) &&
+ (!((BundleProtectionDomain) reqRevision.getProtectionDomain())
+ .impliesDirect(new BundlePermission(
+ reqRevision.getSymbolicName(),
+ BundlePermission.FRAGMENT))
+ || !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain())
+ .impliesDirect(new BundlePermission(
+ cap.getRevision().getSymbolicName(),
+ BundlePermission.HOST))))
+ {
+ continue;
+ }
+ }
+
+ 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.
+// TODO: OSGi R4.3 - It would be better if we could think of a way to do this
+// filtering that was less costly. One possibility it to do the check in
+// ResolverState.checkExecutionEnvironment(), since it will only need to
+// be done once for any black listed revision. However, as we move toward
+// OBR-like API, this is a non-standard call, so doing it here is the only
+// standard way of achieving it.
+ 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)
+ {
+ hook.filterMatches(req, shrinkable);
+ }
+ }
+
+ return result;
+ }
+
+ public 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))
+ {
+ 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);
+ }
+ }
+ }
+ }
+
+ public 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.
+// TODO: OSGi R4.3 - Is it sufficient to just check declared native libs here?
+// List<R4Library> libs = ((BundleWiringImpl) revision.getWiring()).getNativeLibraries();
+ List<R4Library> libs = ((BundleRevisionImpl) revision).getDeclaredNativeLibraries();
+ if (libs != 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);
+ }
+ }
+ }
+ }
+
+ //
+ // 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
+ * system variable 'org.osgi.framework.executionenvironment'.
+ * @param fwkExecEnvStr Comma delimited string of provided execution environments
+ * @return the parsed set of execution environments
+ **/
+ private static Set<String> parseExecutionEnvironments(String fwkExecEnvStr)
+ {
+ Set<String> newSet = new HashSet<String>();
+ if (fwkExecEnvStr != null)
+ {
+ StringTokenizer tokens = new StringTokenizer(fwkExecEnvStr, ",");
+ while (tokens.hasMoreTokens())
+ {
+ newSet.add(tokens.nextToken().trim());
+ }
+ }
+ return newSet;
+ }
}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java b/framework/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java
index 3140a1c..dccd7ca 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java
@@ -47,8 +47,8 @@
// Compare revision capabilities.
if ((c == 0) && cap1.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
{
- c = ((Comparable) cap1.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
- .compareTo(cap2.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
+ c = ((Comparable) cap1.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE))
+ .compareTo(cap2.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE));
if (c == 0)
{
Version v1 = (!cap1.getAttributes().containsKey(Constants.BUNDLE_VERSION_ATTRIBUTE))
@@ -65,8 +65,8 @@
// Compare package capabilities.
else if ((c == 0) && cap1.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
{
- c = ((Comparable) cap1.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR))
- .compareTo(cap2.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+ c = ((Comparable) cap1.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE))
+ .compareTo(cap2.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
if (c == 0)
{
Version v1 = (!cap1.getAttributes().containsKey(BundleCapabilityImpl.VERSION_ATTR))
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 b5c09cf..bdcf0b3 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
@@ -31,10 +31,8 @@
import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.felix.framework.BundleRevisionImpl;
-import org.apache.felix.framework.BundleWiringImpl;
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;
@@ -44,10 +42,8 @@
class Candidates
{
- private final BundleRevision m_root;
-
- // Set of all candidate bundle revisions.
- private final Set<BundleRevision> m_candidateRevisions;
+ // Set of all involved bundle revisions.
+ private final Set<BundleRevision> m_involvedRevisions;
// Maps a capability to requirements that match it.
private final Map<BundleCapability, Set<BundleRequirement>> m_dependentMap;
// Maps a requirement to the capability it matches.
@@ -69,23 +65,20 @@
/**
* Private copy constructor used by the copy() method.
- * @param root the root module for the resolve.
* @param dependentMap the capability dependency map.
* @param candidateMap the requirement candidate map.
* @param hostFragments the fragment map.
* @param wrappedHosts the wrapped hosts map.
**/
private Candidates(
- BundleRevision root,
- Set<BundleRevision> candidateRevisions,
+ Set<BundleRevision> involvedRevisions,
Map<BundleCapability, Set<BundleRequirement>> dependentMap,
Map<BundleRequirement, SortedSet<BundleCapability>> candidateMap,
Map<BundleCapability, Map<String, Map<Version, List<BundleRequirement>>>> hostFragments,
Map<BundleRevision, HostBundleRevision> wrappedHosts, Map<BundleRevision, Object> populateResultCache,
boolean fragmentsPresent)
{
- m_root = root;
- m_candidateRevisions = candidateRevisions;
+ m_involvedRevisions = involvedRevisions;
m_dependentMap = dependentMap;
m_candidateMap = candidateMap;
m_hostFragments = hostFragments;
@@ -95,49 +88,17 @@
}
/**
- * Constructs a new populated Candidates object for the specified root module.
- * @param state the resolver state used for populating the candidates.
- * @param root the root module for the resolve.
+ * Constructs an empty Candidates object.
**/
- public Candidates(ResolverState state, BundleRevision root)
+ public Candidates()
{
- m_root = root;
- m_candidateRevisions = new HashSet<BundleRevision>();
+ m_involvedRevisions = new HashSet<BundleRevision>();
m_dependentMap = new HashMap<BundleCapability, Set<BundleRequirement>>();
m_candidateMap = new HashMap<BundleRequirement, SortedSet<BundleCapability>>();
m_hostFragments =
new HashMap<BundleCapability, Map<String, Map<Version, List<BundleRequirement>>>>();
m_allWrappedHosts = new HashMap<BundleRevision, HostBundleRevision>();
m_populateResultCache = new HashMap<BundleRevision, Object>();
-
- populate(state, m_root);
- }
-
- /**
- * Constructs a new populated Candidates object with the specified root module and
- * starting requirement and matching candidates. This constructor is used
- * when the root module is performing a dynamic import for the given
- * requirement and the given potential candidates.
- * @param state the resolver state used for populating the candidates.
- * @param root the module with a dynamic import to resolve.
- * @param req the requirement being resolved.
- * @param candidates the potential candidates matching the requirement.
- **/
- public Candidates(ResolverState state, BundleRevision root,
- BundleRequirement req, SortedSet<BundleCapability> candidates)
- {
- m_root = root;
- m_candidateRevisions = new HashSet<BundleRevision>();
- m_dependentMap = new HashMap<BundleCapability, Set<BundleRequirement>>();
- m_candidateMap = new HashMap<BundleRequirement, SortedSet<BundleCapability>>();
- m_hostFragments =
- new HashMap<BundleCapability, Map<String, Map<Version, List<BundleRequirement>>>>();
- m_allWrappedHosts = new HashMap<BundleRevision, HostBundleRevision>();
- m_populateResultCache = new HashMap<BundleRevision, Object>();
-
- add(req, candidates);
-
- populateDynamic(state, m_root);
}
/**
@@ -314,6 +275,9 @@
}
else if (cycleCount.intValue() == 0)
{
+ // Record invoved revision.
+ m_involvedRevisions.add(revision);
+
// Record that the revision was successfully populated.
m_populateResultCache.put(revision, Boolean.TRUE);
@@ -325,26 +289,31 @@
}
}
- public final void populateOptional(ResolverState state, BundleRevision revision)
+// TODO: OSGi R4.3 - Related to resolve() method clean up, can this just
+// become the normal case? Currently, it just swallows the resolve
+// exception, which would have to change.
+ public final boolean populate(
+ ResolverState state, BundleRevision revision, boolean isGreedyAttach)
{
- // We will always attempt to populate optional fragments, since this
- // is necessary for greedy resolving of fragment. Howevere, we'll only
- // attempt to populate optional non-fragment revisions if they aren't
- // already resolved.
+ // 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
+ // resolved.
boolean isFragment = Util.isFragment(revision);
if (!isFragment && (revision.getWiring() != null))
{
- return;
+ return false;
}
try
{
- // If the optional revision is a fragment, then we only want to populate
- // the fragment if it has a candidate host in the set of already populated
- // revisions. We do this to avoid unnecessary work in prepare(). If the
- // fragment has a host, we'll prepopulate the result cache here to avoid
- // having to do the host lookup again in populate().
- if (isFragment)
+ // If the optional revision is a fragment and this is a greedy attach,
+ // then only populate the fragment if it has a candidate host in the
+ // set of already populated revisions. We do this to avoid resolving
+ // unneeded fragments and hosts. If the fragment has a host, we'll
+ // prepopulate the result cache here to avoid having to do the host
+ // lookup again in populate().
+ if (isGreedyAttach && isFragment)
{
// Get the current result cache value, to make sure the revision
// hasn't already been populated.
@@ -385,7 +354,7 @@
// return since this fragment isn't needed.
if (hosts.isEmpty())
{
- return;
+ return false;
}
// If there are populates host candidates, then finish up
@@ -423,26 +392,37 @@
}
catch (ResolveException ex)
{
- // Ignore since the revision is optional.
+ return false;
}
+
+ return true;
}
- private boolean isPopulated(BundleRevision revision)
+ public boolean isPopulated(BundleRevision revision)
{
Object value = m_populateResultCache.get(revision);
return ((value != null) && (value instanceof Boolean));
}
- private void populateDynamic(ResolverState state, BundleRevision revision)
+ public ResolveException getResolveException(BundleRevision revision)
{
- // There should be one entry in the candidate map, which are the
- // the candidates for the matching dynamic requirement. Get the
- // matching candidates and populate their candidates if necessary.
+ 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)
+ {
+ // Add the dynamic imports candidates.
+// TODO: OSGi R4.3 - Can we just calculate the candidates inside here too?
+// I think we don't because of performance reasons since we have to
+// look them up already. If so, maybe it is not worth doing it here.
+ add(req, candidates);
+
+ // Populate the candidates for the dynamic import.
ResolveException rethrow = null;
- Entry<BundleRequirement, SortedSet<BundleCapability>> entry =
- m_candidateMap.entrySet().iterator().next();
- BundleRequirement dynReq = entry.getKey();
- SortedSet<BundleCapability> candidates = entry.getValue();
for (Iterator<BundleCapability> itCandCap = candidates.iterator();
itCandCap.hasNext(); )
{
@@ -468,10 +448,12 @@
{
if (rethrow == null)
{
- rethrow = new ResolveException("Dynamic import failed.", revision, dynReq);
+ rethrow = new ResolveException("Dynamic import failed.", revision, req);
}
throw rethrow;
}
+
+ m_populateResultCache.put(revision, Boolean.TRUE);
}
/**
@@ -492,16 +474,6 @@
// Record the candidates.
m_candidateMap.put(req, candidates);
-
- // Make a list of all candidate revisions for determining singetons.
- // Add the requirement as a dependent on the candidates. Keep track
- // of fragments for hosts.
- for (BundleCapability cap : candidates)
- {
- // Remember the revision for all capabilities so we can
- // determine which ones are singletons.
- m_candidateRevisions.add(cap.getRevision());
- }
}
/**
@@ -569,7 +541,7 @@
final Map<String, BundleRevision> singletons = new HashMap<String, BundleRevision>();
- for (Iterator<BundleRevision> it = m_candidateRevisions.iterator(); it.hasNext(); )
+ for (Iterator<BundleRevision> it = m_involvedRevisions.iterator(); it.hasNext(); )
{
BundleRevision br = it.next();
if (isSingleton(br))
@@ -597,18 +569,25 @@
// if it wasn't selected.
if (singleton != null)
{
- removeRevision(singleton);
+ removeRevision(
+ singleton,
+ new ResolveException(
+ "Conflict with another singleton.", singleton, null));
}
}
else
{
- removeRevision(br);
+ removeRevision(br,
+ new ResolveException(
+ "Conflict with another singleton.", br, null));
}
}
}
// If the root is a singleton, then prefer it over any other singleton.
- if (isSingleton(m_root))
+// 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);
@@ -627,6 +606,7 @@
removeRevision(singleton);
}
}
+*/
// Make sure selected singletons do not conflict with existing
// singletons passed into this method.
@@ -637,7 +617,9 @@
if ((singleton != null) && (singleton != existing))
{
singletons.remove(singleton.getSymbolicName());
- removeRevision(singleton);
+ removeRevision(singleton,
+ new ResolveException(
+ "Conflict with another singleton.", singleton, null));
}
}
@@ -712,7 +694,9 @@
// Step 3
for (BundleRevision br : unselectedFragments)
{
- removeRevision(br);
+ removeRevision(br,
+ new ResolveException(
+ "Fragment was not selected for attachment.", br, null));
}
// Step 4
@@ -722,15 +706,20 @@
// from the merged host.
for (BundleCapability c : hostRevision.getDeclaredCapabilities(null))
{
- Set<BundleRequirement> dependents =
- m_dependentMap.get(((HostedCapability) c).getDeclaredCapability());
- if (dependents != null)
+ // Don't replace the host capability, since the fragment will
+ // really be attached to the original host, not the wrapper.
+ if (!c.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
{
- for (BundleRequirement r : dependents)
+ Set<BundleRequirement> dependents =
+ m_dependentMap.get(((HostedCapability) c).getDeclaredCapability());
+ if (dependents != null)
{
- Set<BundleCapability> cands = m_candidateMap.get(r);
- cands.remove(((HostedCapability) c).getDeclaredCapability());
- cands.add(c);
+ for (BundleRequirement r : dependents)
+ {
+ Set<BundleCapability> cands = m_candidateMap.get(r);
+ cands.remove(((HostedCapability) c).getDeclaredCapability());
+ cands.add(c);
+ }
}
}
}
@@ -806,17 +795,14 @@
* @param revision the module to remove.
* @throws ResolveException if removing the module caused the resolve to fail.
**/
- private void removeRevision(BundleRevision revision) throws ResolveException
+ private void removeRevision(BundleRevision revision, ResolveException ex)
{
- if (m_root.equals(revision))
- {
-// TODO: SINGLETON RESOLVER - Improve this message.
- String msg = "Unable to resolve " + m_root;
- ResolveException ex = new ResolveException(msg, m_root, null);
- throw ex;
- }
+ // Add removal reason to result cache.
+ m_populateResultCache.put(revision, ex);
+ // Remove from dependents.
Set<BundleRevision> unresolvedRevisions = new HashSet<BundleRevision>();
remove(revision, unresolvedRevisions);
+ // Remove dependents that failed as a result of removing revision.
while (!unresolvedRevisions.isEmpty())
{
Iterator<BundleRevision> it = unresolvedRevisions.iterator();
@@ -928,13 +914,10 @@
m_candidateMap.remove(r);
if (!((BundleRequirementImpl) r).isOptional())
{
- if (m_root.equals(r.getRevision()))
- {
- String msg = "Unable to resolve " + m_root
- + ": missing requirement " + r;
- ResolveException ex = new ResolveException(msg, m_root, r);
- throw ex;
- }
+ String msg = "Unable to resolve " + r.getRevision()
+ + ": missing requirement " + r;
+ m_populateResultCache.put(
+ r.getRevision(), new ResolveException(msg, r.getRevision(), r));
unresolvedRevisions.add(r.getRevision());
}
}
@@ -968,7 +951,7 @@
}
return new Candidates(
- m_root, m_candidateRevisions, dependentMap, candidateMap,
+ m_involvedRevisions, dependentMap, candidateMap,
m_hostFragments, m_allWrappedHosts, m_populateResultCache,
m_fragmentsPresent);
}
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java b/framework/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java
index abe24e4..f24ada3 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java
@@ -113,7 +113,9 @@
if (getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
{
return "[" + m_host + "] "
- + getNamespace() + "; " + getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+ + getNamespace()
+ + "; "
+ + getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
}
return "[" + m_host + "] " + getNamespace() + "; " + getAttributes();
}
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java b/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
index 25e0ad0..c2f12b7 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
@@ -31,6 +31,8 @@
Map<BundleRevision, List<ResolverWire>> resolve(
ResolverState state, BundleRevision revision, Set<BundleRevision> optional);
Map<BundleRevision, List<ResolverWire>> resolve(
+ ResolverState state, Set<BundleRevision> revisions, Set<BundleRevision> optional);
+ Map<BundleRevision, List<ResolverWire>> resolve(
ResolverState state, BundleRevision revision, String pkgName,
Set<BundleRevision> fragments);
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
index c0ea759..9e90ea7 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
@@ -71,17 +71,25 @@
try
{
- // Populate all candidates.
- Candidates allCandidates = new Candidates(state, revision);
+ // Populate revision's candidates.
+ Candidates allCandidates = new Candidates();
+ allCandidates.populate(state, revision);
// Try to populate optional fragments.
for (BundleRevision br : optional)
{
- allCandidates.populateOptional(state, br);
+ allCandidates.populate(state, br, true);
}
// Merge any fragments into hosts.
allCandidates.prepare(getResolvedSingletons(state));
+ // Make sure revision is still valid, since it could
+ // fail due to fragment and/or singleton selection.
+// TODO: OSGi R4.3 - Could this be merged back into Candidates?
+ if (!allCandidates.isPopulated(revision))
+ {
+ throw allCandidates.getResolveException(revision);
+ }
// Record the initial candidate permutation.
m_usesPermutations.add(allCandidates);
@@ -183,6 +191,182 @@
}
public Map<BundleRevision, List<ResolverWire>> resolve(
+ ResolverState state, Set<BundleRevision> revisions, Set<BundleRevision> optional)
+ {
+ Map<BundleRevision, List<ResolverWire>> wireMap = new HashMap<BundleRevision, List<ResolverWire>>();
+ Map<BundleRevision, Packages> revisionPkgMap = new HashMap<BundleRevision, Packages>();
+
+ boolean retry;
+ do
+ {
+ retry = false;
+
+ try
+ {
+ // Create object to hold all candidates.
+ Candidates allCandidates = new Candidates();
+
+ // Populate revisions.
+ for (Iterator<BundleRevision> it = revisions.iterator(); it.hasNext(); )
+ {
+ BundleRevision br = it.next();
+// TODO: OSGi R4.3 - This is not correct for fragments, since they may have wires already
+// but we still need to resolve them.
+ if ((br.getWiring() != null) || !allCandidates.populate(state, br, false))
+ {
+ it.remove();
+ }
+ }
+
+ // Try to populate optional fragments.
+ for (BundleRevision br : optional)
+ {
+ allCandidates.populate(state, br, true);
+ }
+
+ // Merge any fragments into hosts.
+ allCandidates.prepare(getResolvedSingletons(state));
+
+ // Prune failed revisions.
+// TODO: OSGi R4.3 - Again, can we merge this stuff into Candidates?
+// for (Iterator<BundleRevision> it = revisions.iterator(); it.hasNext(); )
+// {
+// if (!allCandidates.isPopulated(it.next()))
+// {
+// it.remove();
+// }
+// }
+
+ // Record the initial candidate permutation.
+ m_usesPermutations.add(allCandidates);
+
+ ResolveException rethrow = null;
+
+ // If the requested revision is a fragment, then
+ // ultimately we will verify the host., so store
+ // any host requirements
+ Map<BundleRevision, List<BundleRequirement>> hostReqs =
+ new HashMap<BundleRevision, List<BundleRequirement>>();
+ for (BundleRevision br : revisions)
+ {
+ hostReqs.put(
+ br, br.getDeclaredRequirements(BundleRevision.HOST_NAMESPACE));
+ }
+
+ do
+ {
+ rethrow = null;
+
+ revisionPkgMap.clear();
+ m_packageSourcesCache.clear();
+
+ allCandidates = (m_usesPermutations.size() > 0)
+ ? m_usesPermutations.remove(0)
+ : m_importPermutations.remove(0);
+//allCandidates.dump();
+
+ for (BundleRevision br : revisions)
+ {
+ BundleRevision target = br;
+
+ // If we are resolving a fragment, then we
+ // actually want to verify its host.
+ List<BundleRequirement> hostReq = hostReqs.get(br);
+ if (!hostReq.isEmpty())
+ {
+ target = allCandidates.getCandidates(hostReq.get(0))
+ .iterator().next().getRevision();
+ }
+
+ calculatePackageSpaces(
+ allCandidates.getWrappedHost(target), allCandidates, revisionPkgMap,
+ new HashMap(), new HashSet());
+//System.out.println("+++ PACKAGE SPACES START +++");
+//dumpRevisionPkgMap(revisionPkgMap);
+//System.out.println("+++ PACKAGE SPACES END +++");
+
+ try
+ {
+ checkPackageSpaceConsistency(
+ false, allCandidates.getWrappedHost(target),
+ allCandidates, revisionPkgMap, new HashMap());
+ }
+ catch (ResolveException ex)
+ {
+ rethrow = ex;
+ }
+ }
+ }
+ while ((rethrow != null)
+ && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
+
+ // If there is a resolve exception, then determine if an
+ // optionally resolved revision is to blame (typically a fragment).
+ // If so, then remove the optionally resolved resolved and try
+ // again; otherwise, rethrow the resolve exception.
+ if (rethrow != null)
+ {
+ BundleRevision faultyRevision =
+ getActualBundleRevision(rethrow.getRevision());
+ if (rethrow.getRequirement() instanceof HostedRequirement)
+ {
+ faultyRevision =
+ ((HostedRequirement) rethrow.getRequirement())
+ .getDeclaredRequirement().getRevision();
+ }
+ if (revisions.remove(faultyRevision))
+ {
+ retry = true;
+ }
+ else if (optional.remove(faultyRevision))
+ {
+ retry = true;
+ }
+ else
+ {
+ throw rethrow;
+ }
+ }
+ // If there is no exception to rethrow, then this was a clean
+ // resolve, so populate the wire map.
+ else
+ {
+ for (BundleRevision br : revisions)
+ {
+ BundleRevision target = br;
+
+ // If we are resolving a fragment, then we
+ // actually want to populate its host's wires.
+ List<BundleRequirement> hostReq = hostReqs.get(br);
+ if (!hostReq.isEmpty())
+ {
+ target = allCandidates.getCandidates(hostReq.get(0))
+ .iterator().next().getRevision();
+ }
+
+ if (allCandidates.isPopulated(target))
+ {
+ wireMap =
+ populateWireMap(
+ allCandidates.getWrappedHost(target),
+ revisionPkgMap, wireMap, allCandidates);
+ }
+ }
+ }
+ }
+ finally
+ {
+ // Always clear the state.
+ m_usesPermutations.clear();
+ m_importPermutations.clear();
+ }
+ }
+ while (retry);
+
+ return wireMap;
+ }
+
+ public Map<BundleRevision, List<ResolverWire>> resolve(
ResolverState state, BundleRevision revision, String pkgName,
Set<BundleRevision> optional)
{
@@ -212,11 +396,18 @@
// Try to populate optional fragments.
for (BundleRevision br : optional)
{
- allCandidates.populateOptional(state, br);
+ allCandidates.populate(state, br, true);
}
// Merge any fragments into hosts.
allCandidates.prepare(getResolvedSingletons(state));
+ // Make sure revision is still valid, since it could
+ // fail due to fragment and/or singleton selection.
+// TODO: OSGi R4.3 - Could this be merged back into Candidates?
+ if (!allCandidates.isPopulated(revision))
+ {
+ throw allCandidates.getResolveException(revision);
+ }
// Record the initial candidate permutation.
m_usesPermutations.add(allCandidates);
@@ -349,7 +540,7 @@
for (BundleCapability cap : revision.getWiring().getCapabilities(null))
{
if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
- && cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
+ && cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
{
return null;
}
@@ -366,7 +557,7 @@
// there is a matching one for the package from which we want to
// load a class.
Map<String, Object> attrs = new HashMap(1);
- attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+ attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
BundleRequirementImpl req = new BundleRequirementImpl(
revision,
BundleRevision.PACKAGE_NAMESPACE,
@@ -417,7 +608,8 @@
if (candidates.size() > 0)
{
- allCandidates = new Candidates(state, revision, dynReq, candidates);
+ allCandidates = new Candidates();
+ allCandidates.populateDynamic(state, revision, dynReq, candidates);
}
return allCandidates;
@@ -682,7 +874,7 @@
// for imported or required packages, appropriately.
String pkgName = (String)
- candCap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+ candCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
List blameReqs = new ArrayList();
blameReqs.add(currentReq);
@@ -1147,7 +1339,7 @@
if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
{
exports.put(
- (String) cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR),
+ (String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
cap);
}
}
@@ -1165,7 +1357,7 @@
BundleRevision.PACKAGE_NAMESPACE))
{
String pkgName = (String) wire.getCapability()
- .getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+ .getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
exports.remove(pkgName);
}
}
@@ -1181,7 +1373,7 @@
if ((cands != null) && !cands.isEmpty())
{
String pkgName = (String) cands.iterator().next()
- .getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+ .getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
exports.remove(pkgName);
}
}
@@ -1267,7 +1459,7 @@
// Get the package name associated with the capability.
String pkgName = cap.getAttributes()
- .get(BundleCapabilityImpl.PACKAGE_ATTR).toString();
+ .get(BundleRevision.PACKAGE_NAMESPACE).toString();
// Since a revision can export the same package more than once, get
// all package capabilities for the specified package name.
@@ -1277,7 +1469,7 @@
for (int capIdx = 0; capIdx < caps.size(); capIdx++)
{
if (caps.get(capIdx).getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
- && caps.get(capIdx).getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
+ && caps.get(capIdx).getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
{
sources.add(caps.get(capIdx));
}
@@ -1423,7 +1615,7 @@
{
// Ignore revisions that import themselves.
if (!revision.equals(blame.m_cap.getRevision())
- && blame.m_cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)
+ && blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
.equals(pkgName))
{
if (blame.m_cap.getRevision().getWiring() == null)
@@ -1434,7 +1626,7 @@
Packages candPkgs = revisionPkgMap.get(blame.m_cap.getRevision());
Map<String, Object> attrs = new HashMap(1);
- attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+ attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
packageWires.add(
new ResolverWireImpl(
revision,
@@ -1530,9 +1722,9 @@
(BundleRequirementImpl) blame.m_reqs.get(i));
if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
{
- sb.append(BundleCapabilityImpl.PACKAGE_ATTR);
+ sb.append(BundleRevision.PACKAGE_NAMESPACE);
sb.append("=");
- sb.append(cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).toString());
+ sb.append(cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).toString());
BundleCapability usedCap;
if ((i + 2) < blame.m_reqs.size())
{
@@ -1547,7 +1739,7 @@
(BundleRequirementImpl) blame.m_reqs.get(i + 1));
}
sb.append("; uses:=");
- sb.append(usedCap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+ sb.append(usedCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
}
else
{
@@ -1560,18 +1752,18 @@
BundleCapability export = Util.getSatisfyingCapability(
blame.m_cap.getRevision(),
(BundleRequirementImpl) blame.m_reqs.get(i));
- sb.append(BundleCapabilityImpl.PACKAGE_ATTR);
+ sb.append(BundleRevision.PACKAGE_NAMESPACE);
sb.append("=");
- sb.append(export.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).toString());
- if (!export.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)
- .equals(blame.m_cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)))
+ sb.append(export.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).toString());
+ if (!export.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
+ .equals(blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)))
{
sb.append("; uses:=");
- sb.append(blame.m_cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+ sb.append(blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
sb.append("\n export: ");
- sb.append(BundleCapabilityImpl.PACKAGE_ATTR);
+ sb.append(BundleRevision.PACKAGE_NAMESPACE);
sb.append("=");
- sb.append(blame.m_cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).toString());
+ sb.append(blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).toString());
}
sb.append("\n ");
sb.append(blame.m_cap.getRevision().getSymbolicName());
@@ -1617,7 +1809,7 @@
public String toString()
{
return m_cap.getRevision()
- + "." + m_cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)
+ + "." + m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
+ (((m_reqs == null) || m_reqs.isEmpty())
? " NO BLAME"
: " BLAMED ON " + m_reqs);
diff --git a/framework/src/main/java/org/apache/felix/framework/util/ShrinkableCollection.java b/framework/src/main/java/org/apache/felix/framework/util/ShrinkableCollection.java
index 5719e17..8313d70 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/ShrinkableCollection.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/ShrinkableCollection.java
@@ -24,21 +24,21 @@
/** This collection wraps any other collection but prohibits calls to add
* elements to the collection.
*/
-public class ShrinkableCollection implements Collection
+public class ShrinkableCollection<T> implements Collection<T>
{
- private final Collection m_delegate;
+ private final Collection<T> m_delegate;
- public ShrinkableCollection(Collection delegate)
+ public ShrinkableCollection(Collection<T> delegate)
{
m_delegate = delegate;
}
- public boolean add(Object o)
+ public boolean add(T o)
{
throw new UnsupportedOperationException();
}
- public boolean addAll(Collection c)
+ public boolean addAll(Collection<? extends T> c)
{
throw new UnsupportedOperationException();
}
@@ -53,16 +53,18 @@
return m_delegate.contains(o);
}
- public boolean containsAll(Collection c)
+ public boolean containsAll(Collection<?> c)
{
return m_delegate.containsAll(c);
}
+ @Override
public boolean equals(Object o)
{
return m_delegate.equals(o);
}
+ @Override
public int hashCode()
{
return m_delegate.hashCode();
@@ -83,12 +85,12 @@
return m_delegate.remove(o);
}
- public boolean removeAll(Collection c)
+ public boolean removeAll(Collection<?> c)
{
return m_delegate.removeAll(c);
}
- public boolean retainAll(Collection c)
+ public boolean retainAll(Collection<?> c)
{
return m_delegate.retainAll(c);
}
@@ -103,7 +105,7 @@
return m_delegate.toArray();
}
- public Object[] toArray(Object[] a)
+ public <A> A[] toArray(A[] a)
{
return m_delegate.toArray(a);
}
diff --git a/framework/src/main/java/org/apache/felix/framework/util/Util.java b/framework/src/main/java/org/apache/felix/framework/util/Util.java
index 3bdf0c3..d2b9151 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/Util.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/Util.java
@@ -358,7 +358,7 @@
if (w.getCapability().getNamespace()
.equals(BundleRevision.PACKAGE_NAMESPACE) &&
w.getCapability().getAttributes()
- .get(BundleCapabilityImpl.PACKAGE_ATTR).equals(name))
+ .get(BundleRevision.PACKAGE_NAMESPACE).equals(name))
{
return w;
}
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index f862ced..58ed7ec 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -95,11 +95,11 @@
// Parse bundle symbolic name.
//
- BundleCapabilityImpl requireCap = parseBundleSymbolicName(owner, m_headerMap);
- if (requireCap != null)
+ BundleCapabilityImpl bundleCap = parseBundleSymbolicName(owner, m_headerMap);
+ if (bundleCap != null)
{
m_bundleSymbolicName = (String)
- requireCap.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+ bundleCap.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE);
// Add a bundle capability and a host capability to all
// non-fragment bundles. A host capability is the same
@@ -109,12 +109,15 @@
// dependencies.
if (headerMap.get(Constants.FRAGMENT_HOST) == null)
{
- capList.add(requireCap);
+ capList.add(bundleCap);
+ Map<String, Object> hostAttrs =
+ new HashMap<String, Object>(bundleCap.getAttributes());
+ Object value = hostAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
+ hostAttrs.put(BundleRevision.HOST_NAMESPACE, value);
capList.add(new BundleCapabilityImpl(
owner, BundleRevision.HOST_NAMESPACE,
Collections.EMPTY_MAP,
-// TODO: OSGi R4.3 - Wraps map as unmodifiable twice.
- requireCap.getAttributes()));
+ hostAttrs));
}
// Add a singleton capability if the bundle is a singleton.
@@ -123,13 +126,16 @@
// attach this information to the bundle or host capabilities
// because fragments don't have those capabilities, but fragments
// can be singletons too.
- if (isSingleton(requireCap))
+ if (isSingleton(bundleCap))
{
+ Map<String, Object> singletonAttrs =
+ new HashMap<String, Object>(bundleCap.getAttributes());
+ Object value = singletonAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
+ singletonAttrs.put(BundleCapabilityImpl.SINGLETON_NAMESPACE, value);
capList.add(new BundleCapabilityImpl(
owner, BundleCapabilityImpl.SINGLETON_NAMESPACE,
Collections.EMPTY_MAP,
-// TODO: OSGi R4.3 - Wraps map as unmodifiable twice.
- requireCap.getAttributes()));
+ singletonAttrs));
}
}
@@ -418,7 +424,7 @@
// notion where namespace is also the name of the key attribute.
Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
newAttrs.put(
- BundleCapabilityImpl.PACKAGE_ATTR,
+ BundleRevision.PACKAGE_NAMESPACE,
path);
newAttrs.putAll(attrs);
@@ -812,7 +818,7 @@
Map<String, Object> attrs = clause.m_attrs;
Map<String, Object> newAttrs = new HashMap<String, Object>(attrs.size() + 1);
newAttrs.put(
- BundleCapabilityImpl.PACKAGE_ATTR,
+ BundleRevision.PACKAGE_NAMESPACE,
pkgName);
newAttrs.putAll(attrs);
@@ -1124,7 +1130,7 @@
for (int i = 0; i < exports.size(); i++)
{
if (map.get(exports.get(i).getAttributes()
- .get(BundleCapabilityImpl.PACKAGE_ATTR)) == null)
+ .get(BundleRevision.PACKAGE_NAMESPACE)) == null)
{
// Convert Version to VersionRange.
Map<String, Object> attrs = new HashMap<String, Object>();
@@ -1138,7 +1144,7 @@
List<String> paths = new ArrayList();
paths.add((String)
- exports.get(i).getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+ exports.get(i).getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
clauseList.add(
new ParsedHeaderClause(
paths, Collections.EMPTY_MAP, attrs, Collections.EMPTY_MAP));
@@ -1252,7 +1258,7 @@
// Create a require capability and return it.
String symName = (String) clauses.get(0).m_paths.get(0);
Map<String, Object> attrs = new HashMap<String, Object>(2);
- attrs.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symName);
+ attrs.put(BundleRevision.BUNDLE_NAMESPACE, symName);
attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersion);
return new BundleCapabilityImpl(
owner,
@@ -1317,7 +1323,7 @@
Map<String, Object> attrs = clauses.get(0).m_attrs;
Map<String, Object> newAttrs = new HashMap<String, Object>(attrs.size() + 1);
newAttrs.put(
- Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE,
+ BundleRevision.HOST_NAMESPACE,
clauses.get(0).m_paths.get(0));
newAttrs.putAll(attrs);
@@ -1403,7 +1409,7 @@
// Prepend the symbolic name to the array of attributes.
Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
newAttrs.put(
- Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE,
+ BundleRevision.BUNDLE_NAMESPACE,
path);
newAttrs.putAll(attrs);
diff --git a/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
index 77c24ae..72529cc 100644
--- a/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
@@ -36,7 +36,6 @@
{
public static final String SINGLETON_NAMESPACE = "singleton";
- public static final String PACKAGE_ATTR = "package";
public static final String VERSION_ATTR = "version";
private final BundleRevision m_revision;
@@ -199,7 +198,7 @@
if (m_namespace.equals(BundleRevision.PACKAGE_NAMESPACE))
{
return "[" + m_revision + "] "
- + m_namespace + "; " + m_attrs.get(PACKAGE_ATTR);
+ + m_namespace + "; " + m_attrs.get(BundleRevision.PACKAGE_NAMESPACE);
}
return "[" + m_revision + "] " + m_namespace + "; " + m_attrs;
}