Some refactoring to prepare for resolver hooks. (FELIX-2986)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1133386 13f79535-47bb-0310-9956-ffa450edef68
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 fbd016b..8ac2d61 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
@@ -29,7 +29,6 @@
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
-import org.apache.felix.framework.Felix.StatefulResolver;
import org.apache.felix.framework.cache.Content;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.SecureAction;
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 ea0ca0a..1326b4a 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -26,7 +26,6 @@
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
-import java.security.ProtectionDomain;
import java.security.SecureClassLoader;
import java.util.ArrayList;
import java.util.Collection;
@@ -38,11 +37,7 @@
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
-import java.util.SortedSet;
import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.jar.Manifest;
-import org.apache.felix.framework.Felix.StatefulResolver;
import org.apache.felix.framework.cache.JarContent;
import org.apache.felix.framework.cache.Content;
import org.apache.felix.framework.resolver.HostedCapability;
@@ -51,7 +46,6 @@
import org.apache.felix.framework.resolver.ResourceNotFoundException;
import org.apache.felix.framework.util.CompoundEnumeration;
import org.apache.felix.framework.util.FelixConstants;
-import org.apache.felix.framework.util.SecureAction;
import org.apache.felix.framework.util.SecurityManagerEx;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.util.manifestparser.ManifestParser;
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 5c63347..9561f7f 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -37,9 +37,7 @@
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
-import org.apache.felix.framework.Felix.StatefulResolver;
-import org.apache.felix.framework.resolver.ResolverWire;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.StringMap;
import org.apache.felix.framework.util.Util;
@@ -56,7 +54,6 @@
import org.osgi.framework.Version;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
-import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
/**
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 b362f2a..105e6cb 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -371,11 +371,7 @@
m_bundleStreamHandler = new URLHandlersBundleStreamHandler(this);
// Create a resolver and its state.
- m_resolver = new StatefulResolver(
- new ResolverImpl(m_logger),
- new ResolverStateImpl(
- m_logger,
- (String) m_configMap.get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT)));
+ m_resolver = new StatefulResolver(this);
// Create the extension manager, which we will use as the
// revision for the system bundle.
@@ -423,6 +419,11 @@
return m_resolver;
}
+ BundleRevisionDependencies getDependencies()
+ {
+ return m_dependencies;
+ }
+
URLStreamHandler getBundleStreamHandler()
{
return m_bundleStreamHandler;
@@ -4099,7 +4100,7 @@
* @param type The type of bundle event to fire.
* @param bundle The bundle associated with the event.
**/
- private void fireBundleEvent(int type, Bundle bundle)
+ void fireBundleEvent(int type, Bundle bundle)
{
m_dispatcher.fireBundleEvent(new BundleEvent(type, bundle));
}
@@ -4317,536 +4318,6 @@
// Miscellaneous inner classes.
//
- class StatefulResolver
- {
- private final Resolver m_resolver;
- private final ResolverStateImpl m_resolverState;
-
- StatefulResolver(Resolver resolver, ResolverStateImpl resolverState)
- {
- m_resolver = resolver;
- m_resolverState = resolverState;
- }
-
- void addRevision(BundleRevision br)
- {
- m_resolverState.addRevision(br);
- }
-
- void removeRevision(BundleRevision br)
- {
- m_resolverState.removeRevision(br);
- }
-
- Set<BundleCapability> getCandidates(BundleRequirementImpl req, boolean obeyMandatory)
- {
- return m_resolverState.getCandidates(req, obeyMandatory);
- }
-
- void resolve(BundleRevision rootRevision) throws ResolveException
- {
- // 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.
- boolean locked = acquireGlobalLock();
- if (!locked)
- {
- throw new ResolveException(
- "Unable to acquire global lock for resolve.", rootRevision, null);
- }
-
- Map<BundleRevision, List<ResolverWire>> wireMap = null;
- try
- {
- BundleImpl bundle = (BundleImpl) rootRevision.getBundle();
-
- // Extensions are resolved differently.
- if (bundle.isExtension())
- {
- return;
- }
-
- // Resolve the revision.
- wireMap = m_resolver.resolve(
- m_resolverState, rootRevision, m_resolverState.getFragments());
-
- // Mark all revisions as resolved.
- markResolvedRevisions(wireMap);
- }
- finally
- {
- // Always release the global lock.
- releaseGlobalLock();
- }
-
- fireResolvedEvents(wireMap);
- }
- }
-
- BundleRevision resolve(BundleRevision revision, String pkgName)
- throws ResolveException
- {
- BundleRevision provider = null;
-
- // We cannot dynamically import if the revision is not already resolved
- // or if it is not allowed, so check that first. Note: We check if the
- // dynamic import is allowed without holding any locks, but this is
- // okay since the resolver will double check later after we have
- // acquired the global lock below.
- if ((revision.getWiring() != null) && isAllowedDynamicImport(revision, pkgName))
- {
- // Acquire global lock.
- boolean locked = acquireGlobalLock();
- if (!locked)
- {
- throw new ResolveException(
- "Unable to acquire global lock for resolve.", revision, null);
- }
-
- Map<BundleRevision, List<ResolverWire>> wireMap = null;
- try
- {
- // Double check to make sure that someone hasn't beaten us to
- // dynamically importing the package, which can happen if two
- // threads are racing to do so. If we have an existing wire,
- // then just return it instead.
- provider = ((BundleWiringImpl) revision.getWiring())
- .getImportedPackageSource(pkgName);
- if (provider == null)
- {
- wireMap = m_resolver.resolve(
- m_resolverState, revision, pkgName,
- m_resolverState.getFragments());
-
- if ((wireMap != null) && wireMap.containsKey(revision))
- {
- List<ResolverWire> dynamicWires = wireMap.remove(revision);
- ResolverWire dynamicWire = dynamicWires.get(0);
-
- // Mark all revisions as resolved.
- markResolvedRevisions(wireMap);
-
- // Dynamically add new wire to importing revision.
- if (dynamicWire != null)
- {
- m_dependencies.addDependent(
- dynamicWire.getProvider(),
- dynamicWire.getCapability(),
- revision);
-
- ((BundleWiringImpl) revision.getWiring())
- .addDynamicWire(
- new BundleWireImpl(
- dynamicWire.getRequirer(),
- dynamicWire.getRequirement(),
- dynamicWire.getProvider(),
- dynamicWire.getCapability()));
-
- m_logger.log(
- Logger.LOG_DEBUG,
- "DYNAMIC WIRE: " + dynamicWire);
-
- provider = ((BundleWiringImpl) revision.getWiring())
- .getImportedPackageSource(pkgName);
- }
- }
- }
- }
- finally
- {
- // Always release the global lock.
- releaseGlobalLock();
- }
-
- fireResolvedEvents(wireMap);
- }
-
- return provider;
- }
-
- // This method duplicates a lot of logic from:
- // ResolverImpl.getDynamicImportCandidates()
- boolean isAllowedDynamicImport(BundleRevision revision, String pkgName)
- {
- // Unresolved revisions cannot dynamically import, nor can the default
- // package be dynamically imported.
- if ((revision.getWiring() == null) || pkgName.length() == 0)
- {
- return false;
- }
-
- // If the revision doesn't have dynamic imports, then just return
- // immediately.
- List<BundleRequirement> dynamics =
- Util.getDynamicRequirements(revision.getWiring().getRequirements(null));
- if ((dynamics == null) || dynamics.isEmpty())
- {
- return false;
- }
-
- // If the revision exports this package, then we cannot
- // attempt to dynamically import it.
- for (BundleCapability cap : revision.getWiring().getCapabilities(null))
- {
- if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
- && cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
- {
- return false;
- }
- }
-
- // If this revision already imports or requires this package, then
- // we cannot dynamically import it.
- if (((BundleWiringImpl) revision.getWiring()).hasPackageSource(pkgName))
- {
- return false;
- }
-
- // Loop through the importer's dynamic requirements to determine if
- // 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);
- BundleRequirementImpl req = new BundleRequirementImpl(
- revision,
- BundleRevision.PACKAGE_NAMESPACE,
- Collections.EMPTY_MAP,
- attrs);
- Set<BundleCapability> candidates = m_resolverState.getCandidates(req, false);
-
- return !candidates.isEmpty();
- }
-
- private void markResolvedRevisions(Map<BundleRevision, List<ResolverWire>> wireMap)
- throws ResolveException
- {
-// DO THIS IN THREE PASSES:
-// 1. Aggregate fragments per host.
-// 2. Attach wires and fragments to hosts.
-// -> If fragments fail to attach, then undo.
-// 3. Mark hosts and fragments as resolved.
- if (wireMap != null)
- {
- // First pass: Loop through the wire map to find the host wires
- // for any fragments and map a host to all of its fragments.
- Map<BundleRevision, List<BundleRevision>> hosts =
- new HashMap<BundleRevision, List<BundleRevision>>();
- for (Entry<BundleRevision, List<ResolverWire>> entry : wireMap.entrySet())
- {
- BundleRevision revision = entry.getKey();
- List<ResolverWire> wires = entry.getValue();
-
- if (Util.isFragment(revision))
- {
- for (Iterator<ResolverWire> itWires = wires.iterator();
- itWires.hasNext(); )
- {
- ResolverWire w = itWires.next();
- List<BundleRevision> fragments = hosts.get(w.getProvider());
- if (fragments == null)
- {
- fragments = new ArrayList<BundleRevision>();
- hosts.put(w.getProvider(), fragments);
- }
- fragments.add(w.getRequirer());
- }
- }
- }
-
- // Second pass: Loop through the wire map to do three things:
- // 1) convert resolver wires to bundle wires 2) create wiring
- // objects for revisions and 3) record dependencies among
- // revisions. We don't actually set the wirings here because
- // that indicates that a revision is resolved and we don't want
- // to mark anything as resolved unless we succussfully create
- // all wirings.
- Map<BundleRevision, BundleWiringImpl> wirings =
- new HashMap<BundleRevision, BundleWiringImpl>(wireMap.size());
- for (Entry<BundleRevision, List<ResolverWire>> entry : wireMap.entrySet())
- {
- BundleRevision revision = entry.getKey();
- List<ResolverWire> resolverWires = entry.getValue();
-
- List<BundleWire> bundleWires =
- new ArrayList<BundleWire>(resolverWires.size());
-
- // Loop through resolver wires to calculate the package
- // space implied by the wires as well as to record the
- // dependencies.
- Map<String, BundleRevision> importedPkgs =
- new HashMap<String, BundleRevision>();
- Map<String, List<BundleRevision>> requiredPkgs =
- new HashMap<String, List<BundleRevision>>();
- for (ResolverWire rw : resolverWires)
- {
- bundleWires.add(
- new BundleWireImpl(
- rw.getRequirer(),
- rw.getRequirement(),
- rw.getProvider(),
- rw.getCapability()));
-
- m_dependencies.addDependent(
- rw.getProvider(), rw.getCapability(), rw.getRequirer());
-
- if (Util.isFragment(revision))
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "FRAGMENT WIRE: " + rw.toString());
- }
- else
- {
- m_logger.log(Logger.LOG_DEBUG, "WIRE: " + rw.toString());
-
- if (rw.getCapability().getNamespace()
- .equals(BundleRevision.PACKAGE_NAMESPACE))
- {
- importedPkgs.put(
- (String) rw.getCapability().getAttributes()
- .get(BundleCapabilityImpl.PACKAGE_ATTR),
- rw.getProvider());
- }
- else if (rw.getCapability().getNamespace()
- .equals(BundleRevision.BUNDLE_NAMESPACE))
- {
- Set<String> pkgs = calculateExportedAndReexportedPackages(
- rw.getProvider(),
- wireMap,
- new HashSet<String>(),
- new HashSet<BundleRevision>());
- for (String pkg : pkgs)
- {
- List<BundleRevision> revs = requiredPkgs.get(pkg);
- if (revs == null)
- {
- revs = new ArrayList<BundleRevision>();
- requiredPkgs.put(pkg, revs);
- }
- revs.add(rw.getProvider());
- }
- }
- }
- }
-
- List<BundleRevision> fragments = hosts.get(revision);
- try
- {
- wirings.put(
- revision,
- new BundleWiringImpl(
- m_logger,
- m_configMap,
- this,
- (BundleRevisionImpl) revision,
- fragments,
- bundleWires,
- importedPkgs,
- requiredPkgs));
- }
- catch (Exception ex)
- {
- // This is a fatal error, so undo everything and
- // throw an exception.
- for (Entry<BundleRevision, BundleWiringImpl> wiringEntry
- : wirings.entrySet())
- {
- // Dispose of wiring.
- try
- {
- wiringEntry.getValue().dispose();
- }
- catch (Exception ex2)
- {
- // We are in big trouble.
- RuntimeException rte = new RuntimeException(
- "Unable to clean up resolver failure.", ex2);
- m_logger.log(
- Logger.LOG_ERROR,
- rte.getMessage(), ex2);
- throw rte;
- }
-
- // Reindex host with no fragments.
- m_resolverState.addRevision(revision);
- }
-
- ResolveException re = new ResolveException(
- "Unable to resolve " + revision,
- revision, null);
- re.initCause(ex);
- m_logger.log(
- Logger.LOG_ERROR,
- re.getMessage(), ex);
- throw re;
- }
- }
-
- // Third pass: Loop through the wire map to mark revision as resolved
- // and update the resolver state.
- for (Entry<BundleRevision, BundleWiringImpl> entry : wirings.entrySet())
- {
- BundleRevisionImpl revision = (BundleRevisionImpl) entry.getKey();
-
- // Mark revision as resolved.
- revision.resolve(entry.getValue());
-
- // Update resolver state to remove substituted capabilities.
- if (!Util.isFragment(revision))
- {
- // Update resolver state by reindexing host with attached
- // fragments and removing any substituted exports.
-// TODO: OSGi R4.3 - We could avoid reindexing for fragments if we check it
-// the revision has fragments or not.
- m_resolverState.addRevision(revision);
- m_resolverState.removeSubstitutedCapabilities(revision);
- }
-
- // Update the state of the revision's bundle to resolved as well.
- markBundleResolved(revision);
- }
- }
- }
-
- private void markBundleResolved(BundleRevision revision)
- {
- // Update the bundle's state to resolved when the
- // current revision is resolved; just ignore resolve
- // events for older revisions since this only occurs
- // when an update is done on an unresolved bundle
- // and there was no refresh performed.
- BundleImpl bundle = (BundleImpl) revision.getBundle();
-
- // Lock the bundle first.
- try
- {
- // Acquire bundle lock.
- try
- {
- acquireBundleLock(bundle, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE);
- }
- catch (IllegalStateException ex)
- {
- // There is nothing we can do.
- }
- if (bundle.getCurrentRevision() == revision)
- {
- if (bundle.getState() != Bundle.INSTALLED)
- {
- m_logger.log(bundle,
- Logger.LOG_WARNING,
- "Received a resolve event for a bundle that has already been resolved.");
- }
- else
- {
- setBundleStateAndNotify(bundle, Bundle.RESOLVED);
- }
- }
- }
- finally
- {
- releaseBundleLock(bundle);
- }
- }
-
- private void fireResolvedEvents(Map<BundleRevision, List<ResolverWire>> wireMap)
- {
- if (wireMap != null)
- {
- Iterator<Entry<BundleRevision, List<ResolverWire>>> iter = wireMap.entrySet().iterator();
- // Iterate over the map to fire necessary RESOLVED events.
- while (iter.hasNext())
- {
- Entry<BundleRevision, List<ResolverWire>> entry = iter.next();
- BundleRevision revision = entry.getKey();
-
- // Fire RESOLVED events for all fragments.
- List<BundleRevision> fragments =
- ((BundleWiringImpl) revision.getWiring()).getFragments();
- for (int i = 0; (fragments != null) && (i < fragments.size()); i++)
- {
- fireBundleEvent(BundleEvent.RESOLVED, fragments.get(i).getBundle());
- }
- fireBundleEvent(BundleEvent.RESOLVED, revision.getBundle());
- }
- }
- }
- }
-
- private static Set<String> calculateExportedAndReexportedPackages(
- BundleRevision br,
- Map<BundleRevision, List<ResolverWire>> wireMap,
- Set<String> pkgs,
- Set<BundleRevision> cycles)
- {
- if (!cycles.contains(br))
- {
- cycles.add(br);
-
- // Add all exported packages.
- for (BundleCapability cap : br.getDeclaredCapabilities(null))
- {
- if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
- {
- pkgs.add((String)
- cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
- }
- }
-
- // Now check to see if any required bundles are required with reexport
- // visibility, since we need to include those packages too.
- if (br.getWiring() == null)
- {
- for (ResolverWire rw : wireMap.get(br))
- {
- if (rw.getCapability().getNamespace().equals(
- BundleRevision.BUNDLE_NAMESPACE))
- {
- String dir = rw.getRequirement()
- .getDirectives().get(Constants.VISIBILITY_DIRECTIVE);
- if ((dir != null) && (dir.equals(Constants.VISIBILITY_REEXPORT)))
- {
- calculateExportedAndReexportedPackages(
- rw.getProvider(),
- wireMap,
- pkgs,
- cycles);
- }
- }
- }
- }
- else
- {
- for (BundleWire bw : br.getWiring().getRequiredWires(null))
- {
- if (bw.getCapability().getNamespace().equals(
- BundleRevision.BUNDLE_NAMESPACE))
- {
- String dir = bw.getRequirement()
- .getDirectives().get(Constants.VISIBILITY_DIRECTIVE);
- if ((dir != null) && (dir.equals(Constants.VISIBILITY_REEXPORT)))
- {
- calculateExportedAndReexportedPackages(
- bw.getProviderWiring().getRevision(),
- wireMap,
- pkgs,
- cycles);
- }
- }
- }
- }
- }
-
- return pkgs;
- }
-
class SystemBundleActivator implements BundleActivator
{
public void start(BundleContext context) throws Exception
@@ -5302,7 +4773,7 @@
* @return <tt>true</tt> if the global lock was successfully acquired,
* <tt>false</tt> otherwise.
**/
- private boolean acquireGlobalLock()
+ boolean acquireGlobalLock()
{
synchronized (m_bundleLock)
{
@@ -5358,7 +4829,7 @@
* @throws java.lang.IllegalStateException If the calling thread does not
* own the global lock.
**/
- private void releaseGlobalLock()
+ void releaseGlobalLock()
{
synchronized (m_bundleLock)
{
diff --git a/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java b/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java
index ef1ca60..b884ec9 100644
--- a/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java
@@ -20,9 +20,7 @@
import java.util.*;
import org.apache.felix.framework.util.VersionRange;
-import org.osgi.framework.AdminPermission;
import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
import org.osgi.framework.wiring.BundleRevision;
diff --git a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
index d9b4cd9..8afa535 100644
--- a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
+++ b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
@@ -25,6 +25,7 @@
import org.apache.felix.framework.wiring.BundleCapabilityImpl;
import org.osgi.framework.*;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.framework.hooks.service.*;
import org.osgi.framework.hooks.weaving.WeavingHook;
import org.osgi.framework.wiring.BundleCapability;
@@ -56,6 +57,7 @@
FindHook.class,
ListenerHook.class,
WeavingHook.class,
+ ResolverHookFactory.class,
URLStreamHandlerService.class,
ContentHandler.class
};
diff --git a/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java b/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
new file mode 100644
index 0000000..ba2426e
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
@@ -0,0 +1,580 @@
+/*
+ * 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.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+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.Util;
+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.Constants;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+
+class StatefulResolver
+{
+ private final Felix m_felix;
+ private final Resolver m_resolver;
+ private final ResolverStateImpl m_resolverState;
+
+ StatefulResolver(Felix felix)
+ {
+ m_felix = felix;
+ m_resolver = new ResolverImpl(m_felix.getLogger());
+ m_resolverState = new ResolverStateImpl(m_felix.getLogger(),
+ (String) m_felix.getConfig().get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT));
+ }
+
+ void addRevision(BundleRevision br)
+ {
+ m_resolverState.addRevision(br);
+ }
+
+ void removeRevision(BundleRevision br)
+ {
+ m_resolverState.removeRevision(br);
+ }
+
+ Set<BundleCapability> getCandidates(BundleRequirementImpl req, boolean obeyMandatory)
+ {
+ return m_resolverState.getCandidates(req, obeyMandatory);
+ }
+
+ void resolve(BundleRevision rootRevision) throws ResolveException
+ {
+ // 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.
+ boolean locked = m_felix.acquireGlobalLock();
+ if (!locked)
+ {
+ throw new ResolveException(
+ "Unable to acquire global lock for resolve.", rootRevision, null);
+ }
+
+ Map<BundleRevision, List<ResolverWire>> wireMap = null;
+ try
+ {
+ BundleImpl bundle = (BundleImpl) rootRevision.getBundle();
+
+ // Extensions are resolved differently.
+ if (bundle.isExtension())
+ {
+ return;
+ }
+
+ // Resolve the revision.
+ wireMap = m_resolver.resolve(
+ m_resolverState, rootRevision, m_resolverState.getFragments());
+
+ // Mark all revisions as resolved.
+ markResolvedRevisions(wireMap);
+ }
+ finally
+ {
+ // Always release the global lock.
+ m_felix.releaseGlobalLock();
+ }
+
+ fireResolvedEvents(wireMap);
+ }
+ }
+
+ BundleRevision resolve(BundleRevision revision, String pkgName)
+ throws ResolveException
+ {
+ BundleRevision provider = null;
+
+ // We cannot dynamically import if the revision is not already resolved
+ // or if it is not allowed, so check that first. Note: We check if the
+ // dynamic import is allowed without holding any locks, but this is
+ // okay since the resolver will double check later after we have
+ // acquired the global lock below.
+ if ((revision.getWiring() != null) && isAllowedDynamicImport(revision, pkgName))
+ {
+ // Acquire global lock.
+ boolean locked = m_felix.acquireGlobalLock();
+ if (!locked)
+ {
+ throw new ResolveException(
+ "Unable to acquire global lock for resolve.", revision, null);
+ }
+
+ Map<BundleRevision, List<ResolverWire>> wireMap = null;
+ try
+ {
+ // Double check to make sure that someone hasn't beaten us to
+ // dynamically importing the package, which can happen if two
+ // threads are racing to do so. If we have an existing wire,
+ // then just return it instead.
+ provider = ((BundleWiringImpl) revision.getWiring())
+ .getImportedPackageSource(pkgName);
+ if (provider == null)
+ {
+ wireMap = m_resolver.resolve(
+ m_resolverState, revision, pkgName,
+ m_resolverState.getFragments());
+
+ if ((wireMap != null) && wireMap.containsKey(revision))
+ {
+ List<ResolverWire> dynamicWires = wireMap.remove(revision);
+ ResolverWire dynamicWire = dynamicWires.get(0);
+
+ // Mark all revisions as resolved.
+ markResolvedRevisions(wireMap);
+
+ // Dynamically add new wire to importing revision.
+ if (dynamicWire != null)
+ {
+ m_felix.getDependencies().addDependent(
+ dynamicWire.getProvider(),
+ dynamicWire.getCapability(),
+ revision);
+
+ ((BundleWiringImpl) revision.getWiring())
+ .addDynamicWire(
+ new BundleWireImpl(
+ dynamicWire.getRequirer(),
+ dynamicWire.getRequirement(),
+ dynamicWire.getProvider(),
+ dynamicWire.getCapability()));
+
+ m_felix.getLogger().log(
+ Logger.LOG_DEBUG,
+ "DYNAMIC WIRE: " + dynamicWire);
+
+ provider = ((BundleWiringImpl) revision.getWiring())
+ .getImportedPackageSource(pkgName);
+ }
+ }
+ }
+ }
+ finally
+ {
+ // Always release the global lock.
+ m_felix.releaseGlobalLock();
+ }
+
+ fireResolvedEvents(wireMap);
+ }
+
+ return provider;
+ }
+
+ // This method duplicates a lot of logic from:
+ // ResolverImpl.getDynamicImportCandidates()
+ boolean isAllowedDynamicImport(BundleRevision revision, String pkgName)
+ {
+ // Unresolved revisions cannot dynamically import, nor can the default
+ // package be dynamically imported.
+ if ((revision.getWiring() == null) || pkgName.length() == 0)
+ {
+ return false;
+ }
+
+ // If the revision doesn't have dynamic imports, then just return
+ // immediately.
+ List<BundleRequirement> dynamics =
+ Util.getDynamicRequirements(revision.getWiring().getRequirements(null));
+ if ((dynamics == null) || dynamics.isEmpty())
+ {
+ return false;
+ }
+
+ // If the revision exports this package, then we cannot
+ // attempt to dynamically import it.
+ for (BundleCapability cap : revision.getWiring().getCapabilities(null))
+ {
+ if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+ && cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
+ {
+ return false;
+ }
+ }
+
+ // If this revision already imports or requires this package, then
+ // we cannot dynamically import it.
+ if (((BundleWiringImpl) revision.getWiring()).hasPackageSource(pkgName))
+ {
+ return false;
+ }
+
+ // Loop through the importer's dynamic requirements to determine if
+ // 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);
+ BundleRequirementImpl req = new BundleRequirementImpl(
+ revision,
+ BundleRevision.PACKAGE_NAMESPACE,
+ Collections.EMPTY_MAP,
+ attrs);
+ Set<BundleCapability> candidates = m_resolverState.getCandidates(req, false);
+
+ return !candidates.isEmpty();
+ }
+
+ private void markResolvedRevisions(Map<BundleRevision, List<ResolverWire>> wireMap)
+ throws ResolveException
+ {
+ // DO THIS IN THREE PASSES:
+ // 1. Aggregate fragments per host.
+ // 2. Attach wires and fragments to hosts.
+ // -> If fragments fail to attach, then undo.
+ // 3. Mark hosts and fragments as resolved.
+
+ // First pass.
+ if (wireMap != null)
+ {
+ // First pass: Loop through the wire map to find the host wires
+ // for any fragments and map a host to all of its fragments.
+ Map<BundleRevision, List<BundleRevision>> hosts =
+ new HashMap<BundleRevision, List<BundleRevision>>();
+ for (Entry<BundleRevision, List<ResolverWire>> entry : wireMap.entrySet())
+ {
+ BundleRevision revision = entry.getKey();
+ List<ResolverWire> wires = entry.getValue();
+
+ if (Util.isFragment(revision))
+ {
+ for (Iterator<ResolverWire> itWires = wires.iterator();
+ itWires.hasNext(); )
+ {
+ ResolverWire w = itWires.next();
+ List<BundleRevision> fragments = hosts.get(w.getProvider());
+ if (fragments == null)
+ {
+ fragments = new ArrayList<BundleRevision>();
+ hosts.put(w.getProvider(), fragments);
+ }
+ fragments.add(w.getRequirer());
+ }
+ }
+ }
+
+ // Second pass: Loop through the wire map to do three things:
+ // 1) convert resolver wires to bundle wires 2) create wiring
+ // objects for revisions and 3) record dependencies among
+ // revisions. We don't actually set the wirings here because
+ // that indicates that a revision is resolved and we don't want
+ // to mark anything as resolved unless we succussfully create
+ // all wirings.
+ Map<BundleRevision, BundleWiringImpl> wirings =
+ new HashMap<BundleRevision, BundleWiringImpl>(wireMap.size());
+ for (Entry<BundleRevision, List<ResolverWire>> entry : wireMap.entrySet())
+ {
+ BundleRevision revision = entry.getKey();
+ List<ResolverWire> resolverWires = entry.getValue();
+
+ List<BundleWire> bundleWires =
+ new ArrayList<BundleWire>(resolverWires.size());
+
+ // Loop through resolver wires to calculate the package
+ // space implied by the wires as well as to record the
+ // dependencies.
+ Map<String, BundleRevision> importedPkgs =
+ new HashMap<String, BundleRevision>();
+ Map<String, List<BundleRevision>> requiredPkgs =
+ new HashMap<String, List<BundleRevision>>();
+ for (ResolverWire rw : resolverWires)
+ {
+ bundleWires.add(
+ new BundleWireImpl(
+ rw.getRequirer(),
+ rw.getRequirement(),
+ rw.getProvider(),
+ rw.getCapability()));
+
+ m_felix.getDependencies().addDependent(
+ rw.getProvider(), rw.getCapability(), rw.getRequirer());
+
+ if (Util.isFragment(revision))
+ {
+ m_felix.getLogger().log(
+ Logger.LOG_DEBUG,
+ "FRAGMENT WIRE: " + rw.toString());
+ }
+ else
+ {
+ m_felix.getLogger().log(Logger.LOG_DEBUG, "WIRE: " + rw.toString());
+
+ if (rw.getCapability().getNamespace()
+ .equals(BundleRevision.PACKAGE_NAMESPACE))
+ {
+ importedPkgs.put(
+ (String) rw.getCapability().getAttributes()
+ .get(BundleCapabilityImpl.PACKAGE_ATTR),
+ rw.getProvider());
+ }
+ else if (rw.getCapability().getNamespace()
+ .equals(BundleRevision.BUNDLE_NAMESPACE))
+ {
+ Set<String> pkgs = calculateExportedAndReexportedPackages(
+ rw.getProvider(),
+ wireMap,
+ new HashSet<String>(),
+ new HashSet<BundleRevision>());
+ for (String pkg : pkgs)
+ {
+ List<BundleRevision> revs = requiredPkgs.get(pkg);
+ if (revs == null)
+ {
+ revs = new ArrayList<BundleRevision>();
+ requiredPkgs.put(pkg, revs);
+ }
+ revs.add(rw.getProvider());
+ }
+ }
+ }
+ }
+
+ List<BundleRevision> fragments = hosts.get(revision);
+ try
+ {
+ wirings.put(
+ revision,
+ new BundleWiringImpl(
+ m_felix.getLogger(),
+ m_felix.getConfig(),
+ this,
+ (BundleRevisionImpl) revision,
+ fragments,
+ bundleWires,
+ importedPkgs,
+ requiredPkgs));
+ }
+ catch (Exception ex)
+ {
+ // This is a fatal error, so undo everything and
+ // throw an exception.
+ for (Entry<BundleRevision, BundleWiringImpl> wiringEntry
+ : wirings.entrySet())
+ {
+ // Dispose of wiring.
+ try
+ {
+ wiringEntry.getValue().dispose();
+ }
+ catch (Exception ex2)
+ {
+ // We are in big trouble.
+ RuntimeException rte = new RuntimeException(
+ "Unable to clean up resolver failure.", ex2);
+ m_felix.getLogger().log(
+ Logger.LOG_ERROR,
+ rte.getMessage(), ex2);
+ throw rte;
+ }
+
+ // Reindex host with no fragments.
+ m_resolverState.addRevision(revision);
+ }
+
+ ResolveException re = new ResolveException(
+ "Unable to resolve " + revision,
+ revision, null);
+ re.initCause(ex);
+ m_felix.getLogger().log(
+ Logger.LOG_ERROR,
+ re.getMessage(), ex);
+ throw re;
+ }
+ }
+
+ // Third pass: Loop through the wire map to mark revision as resolved
+ // and update the resolver state.
+ for (Entry<BundleRevision, BundleWiringImpl> entry : wirings.entrySet())
+ {
+ BundleRevisionImpl revision = (BundleRevisionImpl) entry.getKey();
+
+ // Mark revision as resolved.
+ revision.resolve(entry.getValue());
+
+ // Update resolver state to remove substituted capabilities.
+ if (!Util.isFragment(revision))
+ {
+ // Update resolver state by reindexing host with attached
+ // fragments and removing any substituted exports.
+// TODO: OSGi R4.3 - We could avoid reindexing for fragments if we check it
+// the revision has fragments or not.
+ m_resolverState.addRevision(revision);
+ m_resolverState.removeSubstitutedCapabilities(revision);
+ }
+
+ // Update the state of the revision's bundle to resolved as well.
+ markBundleResolved(revision);
+ }
+ }
+ }
+
+ private void markBundleResolved(BundleRevision revision)
+ {
+ // Update the bundle's state to resolved when the
+ // current revision is resolved; just ignore resolve
+ // events for older revisions since this only occurs
+ // when an update is done on an unresolved bundle
+ // and there was no refresh performed.
+ BundleImpl bundle = (BundleImpl) revision.getBundle();
+
+ // Lock the bundle first.
+ try
+ {
+ // Acquire bundle lock.
+ try
+ {
+ m_felix.acquireBundleLock(
+ bundle, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE);
+ }
+ catch (IllegalStateException ex)
+ {
+ // There is nothing we can do.
+ }
+ if (bundle.getCurrentRevision() == revision)
+ {
+ if (bundle.getState() != Bundle.INSTALLED)
+ {
+ m_felix.getLogger().log(bundle,
+ Logger.LOG_WARNING,
+ "Received a resolve event for a bundle that has already been resolved.");
+ }
+ else
+ {
+ m_felix.setBundleStateAndNotify(bundle, Bundle.RESOLVED);
+ }
+ }
+ }
+ finally
+ {
+ m_felix.releaseBundleLock(bundle);
+ }
+ }
+
+ private void fireResolvedEvents(Map<BundleRevision, List<ResolverWire>> wireMap)
+ {
+ if (wireMap != null)
+ {
+ Iterator<Entry<BundleRevision, List<ResolverWire>>> iter = wireMap.entrySet().iterator();
+ // Iterate over the map to fire necessary RESOLVED events.
+ while (iter.hasNext())
+ {
+ Entry<BundleRevision, List<ResolverWire>> entry = iter.next();
+ BundleRevision revision = entry.getKey();
+
+ // Fire RESOLVED events for all fragments.
+ List<BundleRevision> fragments =
+ ((BundleWiringImpl) revision.getWiring()).getFragments();
+ for (int i = 0; (fragments != null) && (i < fragments.size()); i++)
+ {
+ m_felix.fireBundleEvent(BundleEvent.RESOLVED, fragments.get(i).getBundle());
+ }
+ m_felix.fireBundleEvent(BundleEvent.RESOLVED, revision.getBundle());
+ }
+ }
+ }
+
+ private static Set<String> calculateExportedAndReexportedPackages(
+ BundleRevision br,
+ Map<BundleRevision, List<ResolverWire>> wireMap,
+ Set<String> pkgs,
+ Set<BundleRevision> cycles)
+ {
+ if (!cycles.contains(br))
+ {
+ cycles.add(br);
+
+ // Add all exported packages.
+ for (BundleCapability cap : br.getDeclaredCapabilities(null))
+ {
+ if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+ {
+ pkgs.add((String)
+ cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+ }
+ }
+
+ // Now check to see if any required bundles are required with reexport
+ // visibility, since we need to include those packages too.
+ if (br.getWiring() == null)
+ {
+ for (ResolverWire rw : wireMap.get(br))
+ {
+ if (rw.getCapability().getNamespace().equals(
+ BundleRevision.BUNDLE_NAMESPACE))
+ {
+ String dir = rw.getRequirement()
+ .getDirectives().get(Constants.VISIBILITY_DIRECTIVE);
+ if ((dir != null) && (dir.equals(Constants.VISIBILITY_REEXPORT)))
+ {
+ calculateExportedAndReexportedPackages(
+ rw.getProvider(),
+ wireMap,
+ pkgs,
+ cycles);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (BundleWire bw : br.getWiring().getRequiredWires(null))
+ {
+ if (bw.getCapability().getNamespace().equals(
+ BundleRevision.BUNDLE_NAMESPACE))
+ {
+ String dir = bw.getRequirement()
+ .getDirectives().get(Constants.VISIBILITY_DIRECTIVE);
+ if ((dir != null) && (dir.equals(Constants.VISIBILITY_REEXPORT)))
+ {
+ calculateExportedAndReexportedPackages(
+ bw.getProviderWiring().getRevision(),
+ wireMap,
+ pkgs,
+ cycles);
+ }
+ }
+ }
+ }
+ }
+
+ return pkgs;
+ }
+}
\ No newline at end of file