Started refactoring of resolver code, breaking R4SearchPolicyCore into
the class loader delegation portion and the resolver portion. (FELIX-851)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@729083 13f79535-47bb-0310-9956-ffa450edef68
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 a5f52ed..1d17403 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -27,6 +27,8 @@
import org.apache.felix.framework.cache.*;
import org.apache.felix.framework.ext.SecurityProvider;
import org.apache.felix.framework.searchpolicy.*;
+import org.apache.felix.framework.searchpolicy.PackageSource;
+import org.apache.felix.framework.searchpolicy.Resolver.ResolverState;
import org.apache.felix.framework.util.*;
import org.apache.felix.framework.util.manifestparser.*;
import org.apache.felix.moduleloader.*;
@@ -52,6 +54,9 @@
// MODULE FACTORY.
private IModuleFactory m_factory = null;
+ private final Resolver m_resolver;
+ private final FelixResolverState m_resolverState;
+ private final FelixResolver m_felixResolver;
private R4SearchPolicyCore m_policyCore = null;
// Lock object used to determine if an individual bundle
@@ -283,74 +288,16 @@
// Create default bundle stream handler.
m_bundleStreamHandler = new URLHandlersBundleStreamHandler(this);
+ // Create a resolver and its state.
+ m_resolver = new Resolver(m_logger);
+ m_resolverState = new FelixResolverState(m_logger);
+ m_felixResolver = new FelixResolver(m_resolver, m_resolverState);
+
// Create search policy for module loader.
- m_policyCore = new R4SearchPolicyCore(m_logger, m_configMap);
-
- // Add a resolver listener to the search policy
- // so that we will be notified when modules are resolved
- // in order to update the bundle state.
- m_policyCore.addResolverListener(new ResolveListener() {
- public void moduleResolved(ModuleEvent event)
- {
- FelixBundle bundle = null;
- try
- {
- long id = Util.getBundleIdFromModuleId(
- event.getModule().getId());
- if (id > 0)
- {
- // Update the bundle's state to resolved when the
- // current module 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.
- bundle = (FelixBundle) getBundle(id);
-
- // Lock the bundle first.
- try
- {
- acquireBundleLock(bundle);
- if (bundle.getInfo().getCurrentModule() == event.getModule())
- {
- if (bundle.getInfo().getState() != Bundle.INSTALLED)
- {
- m_logger.log(
- Logger.LOG_WARNING,
- "Received a resolve event for a bundle that has already been resolved.");
- }
- else
- {
- bundle.getInfo().setState(Bundle.RESOLVED);
- fireBundleEvent(BundleEvent.RESOLVED, bundle);
- }
- }
- }
- finally
- {
- releaseBundleLock(bundle);
- }
- }
- }
- catch (NumberFormatException ex)
- {
- // Ignore.
- }
- }
-
- public void moduleUnresolved(ModuleEvent event)
- {
- // We can ignore this, because the only time it
- // should happen is when a refresh occurs. The
- // refresh operation resets the bundle's state
- // by calling BundleInfo.reset(), thus it is not
- // necessary for us to reset the bundle's state
- // here.
- }
- });
+ m_policyCore = new R4SearchPolicyCore(m_logger, m_configMap, m_felixResolver);
// Create the module factory and attach it to the search policy.
m_factory = new ModuleFactoryImpl(m_logger);
- m_policyCore.setModuleFactory(m_factory);
// Create the system bundle info object, which will hold state info.
m_sbi = new SystemBundleInfo(m_logger, null);
@@ -358,6 +305,7 @@
// definition for creating the system bundle module.
m_extensionManager = new ExtensionManager(m_logger, m_configMap, m_sbi);
m_sbi.addModule(m_factory.createModule("0", m_extensionManager));
+ m_resolverState.addModule(m_sbi.getCurrentModule());
// Set the extension manager as the content loader for the system
// bundle module.
m_extensionManager.setSearchPolicy(
@@ -749,7 +697,7 @@
// state to be set to RESOLVED.
try
{
- m_policyCore.resolve(m_sbi.getCurrentModule());
+ m_felixResolver.resolve(m_sbi.getCurrentModule());
}
catch (ResolveException ex)
{
@@ -1783,7 +1731,7 @@
IModule module = bundle.getInfo().getCurrentModule();
try
{
- m_policyCore.resolve(module);
+ m_felixResolver.resolve(module);
}
catch (ResolveException ex)
{
@@ -2989,8 +2937,8 @@
protected ExportedPackage[] getExportedPackages(String pkgName)
{
// First, get all exporters of the package.
- R4SearchPolicyCore.PackageSource[] exporters =
- m_policyCore.getResolvedCandidates(
+ PackageSource[] exporters =
+ m_resolverState.getResolvedCandidates(
new Requirement(
ICapability.PACKAGE_NAMESPACE,
null,
@@ -3120,7 +3068,7 @@
// "in use" exporters of the package.
if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
{
- R4SearchPolicyCore.PackageSource[] inUseModules = m_policyCore.getResolvedCandidates(
+ PackageSource[] inUseModules = m_resolverState.getResolvedCandidates(
new Requirement(
ICapability.PACKAGE_NAMESPACE,
null,
@@ -3478,6 +3426,7 @@
// Create the module using the module definition.
IModule module = m_factory.createModule(
Long.toString(targetId) + "." + Integer.toString(revision), md);
+ m_resolverState.addModule(module);
// Create the content loader from the module archive.
IContentLoader contentLoader = new ContentLoaderImpl(
@@ -3513,6 +3462,11 @@
// has not yet been added to the bundle to be removed later.
m_factory.removeModule(module);
throw new BundleException("Native library does not exist: " + entryName);
+// TODO: REFACTOR - We have a memory leak here since we added a module above
+// and then don't remove it in case of an error; this may also
+// be a general issue for installing/updating bundles, so check.
+// This will likely go away when we refactor out the module
+// factory, but we will track it under FELIX-835 until then.
}
}
}
@@ -3575,6 +3529,7 @@
for (int i = 0; i < modules.length; i++)
{
m_factory.removeModule(modules[i]);
+ m_resolverState.removeModule(modules[i]);
}
// Purge all bundle revisions, but the current one.
@@ -3599,6 +3554,7 @@
for (int i = 0; i < modules.length; i++)
{
m_factory.removeModule(modules[i]);
+ m_resolverState.removeModule(modules[i]);
}
// Remove the bundle from the cache.
@@ -3818,6 +3774,191 @@
// Miscellaneous inner classes.
//
+ public class FelixResolver
+ {
+ private final Resolver m_resolver;
+ private final FelixResolverState m_resolverState;
+
+ public FelixResolver(Resolver resolver, FelixResolverState resolverState)
+ {
+ m_resolver = resolver;
+ m_resolverState = resolverState;
+ }
+
+ public void resolve(IModule rootModule) throws ResolveException
+ {
+ if (!m_resolverState.isResolved(rootModule))
+ {
+ Resolver.Result result = m_resolver.resolve(m_resolverState, rootModule);
+
+ // Mark all modules as resolved.
+ markResolvedModules(result.m_resolvedModuleWireMap);
+
+ // Attach and mark all fragments as resolved.
+ attachFragments(result.m_host, result.m_fragmentMap);
+ }
+ }
+
+ public IWire resolveDynamicImport(IModule importer, String pkgName) throws ResolveException
+ {
+ IWire candidateWire = null;
+
+ if (m_resolverState.isResolved(importer))
+ {
+ Object[] result = m_resolver.resolveDynamicImport(m_resolverState, importer, pkgName);
+ if (result != null)
+ {
+ candidateWire = (IWire) result[0];
+ Map resolvedModuleWireMap = (Map) result[1];
+
+ // Mark all modules as resolved.
+ markResolvedModules(resolvedModuleWireMap);
+
+ // Dynamically add new wire to importing module.
+ if (candidateWire != null)
+ {
+ IWire[] wires = importer.getWires();
+ IWire[] newWires = null;
+ if (wires == null)
+ {
+ newWires = new IWire[1];
+ }
+ else
+ {
+ newWires = new IWire[wires.length + 1];
+ System.arraycopy(wires, 0, newWires, 0, wires.length);
+ }
+
+ newWires[newWires.length - 1] = candidateWire;
+ ((ModuleImpl) importer).setWires(newWires);
+m_logger.log(Logger.LOG_DEBUG, "DYNAMIC WIRE: " + newWires[newWires.length - 1]);
+ }
+ }
+ }
+
+ return candidateWire;
+ }
+
+ public synchronized PackageSource[] getResolvedCandidates(IRequirement req)
+ {
+ return m_resolverState.getResolvedCandidates(req);
+ }
+
+ public synchronized PackageSource[] getUnresolvedCandidates(IRequirement req)
+ {
+ return m_resolverState.getUnresolvedCandidates(req);
+ }
+
+ private void markResolvedModules(Map resolvedModuleWireMap)
+ {
+ Iterator iter = resolvedModuleWireMap.entrySet().iterator();
+ // Iterate over the map to mark the modules as resolved and
+ // update our resolver data structures.
+ while (iter.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ IModule module = (IModule) entry.getKey();
+ IWire[] wires = (IWire[]) entry.getValue();
+
+ // Only add wires attribute if some exist; export
+ // only modules may not have wires.
+ if (wires.length > 0)
+ {
+ ((ModuleImpl) module).setWires(wires);
+for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
+{
+ m_logger.log(Logger.LOG_DEBUG, "WIRE: " + wires[wireIdx]);
+}
+ }
+
+ // Update the resolver state to show the module as resolved.
+ m_resolverState.setResolved(module, true);
+ // Update the state of the module's bundle to resolved as well.
+ markBundleResolved(module);
+ }
+ }
+
+ private void attachFragments(IModule host, Map fragmentMap)
+ {
+ // Attach fragments to host module.
+ if ((fragmentMap != null) && (fragmentMap.size() > 0))
+ {
+ List list = new ArrayList();
+ for (Iterator iter = fragmentMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String symName = (String) entry.getKey();
+ IModule[] fragments = (IModule[]) entry.getValue();
+// TODO: FRAGMENT - For now, just attach first candidate.
+ list.add(fragments[0]);
+m_logger.log(Logger.LOG_DEBUG, "(FRAGMENT) WIRE: "
+ + host + " -> " + symName + " -> " + fragments[0]);
+
+ // Update the resolver state to show the module as resolved.
+ m_resolverState.setResolved(fragments[0], true);
+ // Update the state of the module's bundle to resolved as well.
+ markBundleResolved(fragments[0]);
+ }
+ try
+ {
+ ((ModuleImpl) host).attachFragments(
+ (IModule[]) list.toArray(new IModule[list.size()]));
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(Logger.LOG_ERROR, "Unable to attach fragments", ex);
+ }
+ }
+ }
+
+ private void markBundleResolved(IModule module)
+ {
+ // Mark associated bundle as resolved.
+ try
+ {
+ long id = Util.getBundleIdFromModuleId(module.getId());
+ if (id > 0)
+ {
+ // Update the bundle's state to resolved when the
+ // current module 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.
+ FelixBundle bundle = (FelixBundle) getBundle(id);
+
+ // Lock the bundle first.
+ try
+ {
+// TODO: RESOLVER - Seems like we should release the lock before we fire the event.
+ acquireBundleLock(bundle);
+ if (bundle.getInfo().getCurrentModule() == module)
+ {
+ if (bundle.getInfo().getState() != Bundle.INSTALLED)
+ {
+ m_logger.log(
+ Logger.LOG_WARNING,
+ "Received a resolve event for a bundle that has already been resolved.");
+ }
+ else
+ {
+ bundle.getInfo().setState(Bundle.RESOLVED);
+ fireBundleEvent(BundleEvent.RESOLVED, bundle);
+ }
+ }
+ }
+ finally
+ {
+ releaseBundleLock(bundle);
+ }
+ }
+ }
+ catch (NumberFormatException ex)
+ {
+ // Ignore.
+ }
+ }
+ }
+
class SystemBundleActivator implements BundleActivator, Runnable
{
public void start(BundleContext context) throws Exception
@@ -3895,6 +4036,7 @@
try
{
m_factory.removeModule(modules[j]);
+ m_resolverState.removeModule(modules[j]);
}
catch (Exception ex)
{
diff --git a/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java b/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
new file mode 100644
index 0000000..c334276
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
@@ -0,0 +1,735 @@
+/*
+ * 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.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.apache.felix.framework.searchpolicy.Resolver;
+import org.apache.felix.framework.searchpolicy.PackageSource;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.Requirement;
+import org.apache.felix.moduleloader.ICapability;
+import org.apache.felix.moduleloader.IModule;
+import org.apache.felix.moduleloader.IRequirement;
+import org.apache.felix.moduleloader.IWire;
+import org.apache.felix.moduleloader.ModuleImpl;
+import org.osgi.framework.Constants;
+import org.osgi.framework.PackagePermission;
+import org.osgi.framework.Version;
+
+public class FelixResolverState implements Resolver.ResolverState
+{
+ private final Logger m_logger;
+ // List of all modules.
+ private final List m_moduleList = new ArrayList();
+ // Maps a package name to an array of modules.
+ private final Map m_unresolvedPkgIndexMap = new HashMap();
+ // Maps a package name to an array of modules.
+ private final Map m_resolvedPkgIndexMap = new HashMap();
+ // Maps a module to an array of capabilities.
+ private final Map m_resolvedCapMap = new HashMap();
+
+ private final Map m_moduleDataMap = new HashMap();
+
+ // Reusable empty array.
+ private static final IModule[] m_emptyModules = new IModule[0];
+ private static final PackageSource[] m_emptySources = new PackageSource[0];
+
+ public FelixResolverState(Logger logger)
+ {
+ m_logger = logger;
+ }
+
+ public synchronized void addModule(IModule module)
+ {
+ // When a module is added, create an aggregated list of unresolved
+ // exports to simplify later processing when resolving bundles.
+ m_moduleList.add(module);
+
+ ICapability[] caps = module.getDefinition().getCapabilities();
+
+ // Add exports to unresolved package map.
+ for (int i = 0; (caps != null) && (i < caps.length); i++)
+ {
+ if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ indexPackageCapability(m_unresolvedPkgIndexMap, module, caps[i]);
+ }
+ }
+
+System.out.println("UNRESOLVED PACKAGES:");
+dumpPackageIndexMap(m_unresolvedPkgIndexMap);
+System.out.println("RESOLVED PACKAGES:");
+dumpPackageIndexMap(m_resolvedPkgIndexMap);
+ }
+
+ public synchronized void removeModule(IModule module)
+ {
+ // When a module is removed from the system, we need remove
+ // its exports from the "resolved" and "unresolved" package maps,
+ // remove the module's dependencies on fragments and exporters,
+ // and remove the module from the module data map.
+
+ m_moduleList.remove(module);
+
+ // Remove exports from package maps.
+ ICapability[] caps = module.getDefinition().getCapabilities();
+ for (int i = 0; (caps != null) && (i < caps.length); i++)
+ {
+ if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ // Get package name.
+ String pkgName = (String)
+ caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
+ // Remove from "unresolved" package map.
+ IModule[] modules = (IModule[]) m_unresolvedPkgIndexMap.get(pkgName);
+ if (modules != null)
+ {
+ modules = removeModuleFromArray(modules, module);
+ m_unresolvedPkgIndexMap.put(pkgName, modules);
+ }
+
+ // Remove from "resolved" package map.
+ modules = (IModule[]) m_resolvedPkgIndexMap.get(pkgName);
+ if (modules != null)
+ {
+ modules = removeModuleFromArray(modules, module);
+ m_resolvedPkgIndexMap.put(pkgName, modules);
+ }
+ }
+ }
+
+ // Set fragments to null, which will remove the module from all
+ // of its dependent fragment modules.
+ try
+ {
+ ((ModuleImpl) module).attachFragments(null);
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(Logger.LOG_ERROR, "Error detaching fragments.", ex);
+ }
+ // Set wires to null, which will remove the module from all
+ // of its dependent modules.
+ ((ModuleImpl) module).setWires(null);
+ // Remove the module from the "resolved" map.
+// TODO: RB - Maybe this can be merged with ModuleData.
+ m_resolvedCapMap.remove(module);
+ // Finally, remove module data.
+ m_moduleDataMap.remove(module);
+ }
+
+ private void dumpPackageIndexMap(Map pkgIndexMap)
+ {
+ for (Iterator i = pkgIndexMap.entrySet().iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ IModule[] modules = (IModule[]) entry.getValue();
+ if ((modules != null) && (modules.length > 0))
+ {
+ if (!((modules.length == 1) && modules[0].getId().equals("0")))
+ {
+ System.out.println(" " + entry.getKey());
+ for (int j = 0; j < modules.length; j++)
+ {
+ System.out.println(" " + modules[j]);
+ }
+ }
+ }
+ }
+ }
+
+ public synchronized IModule[] getModules()
+ {
+ return (IModule[]) m_moduleList.toArray(new IModule[m_moduleList.size()]);
+ }
+
+ public String getBundleSymbolicName(IModule module)
+ {
+ ICapability[] caps = module.getDefinition().getCapabilities();
+ for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+ {
+ if (caps[capIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ return (String)
+ caps[capIdx].getProperties().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+ }
+ }
+ return null;
+ }
+
+// TODO: FRAGMENT - Not very efficient.
+ private static Version getBundleVersion(IModule module)
+ {
+ ICapability[] caps = module.getDefinition().getCapabilities();
+ for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+ {
+ if (caps[capIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ return (Version)
+ caps[capIdx].getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ }
+ }
+ return Version.emptyVersion;
+ }
+
+ public synchronized boolean isResolved(IModule module)
+ {
+ ModuleData data = (ModuleData) m_moduleDataMap.get(module);
+ if (data != null)
+ {
+ return data.m_resolved;
+ }
+ return false;
+ }
+
+ public synchronized void setResolved(IModule module, boolean b)
+ {
+ ModuleData data = (ModuleData) m_moduleDataMap.get(module);
+ if (data == null)
+ {
+ data = new ModuleData(module);
+ m_moduleDataMap.put(module, data);
+ }
+ data.m_resolved = b;
+
+ if (data.m_resolved)
+ {
+ // At this point, we need to remove all of the resolved module's
+ // capabilities from the "unresolved" package map and put them in
+ // in the "resolved" package map, with the exception of any
+ // package exports that are also imported. In that case we need
+ // to make sure that the import actually points to the resolved
+ // module and not another module. If it points to another module
+ // then the capability should be ignored, since the framework
+ // decided to honor the import and discard the export.
+ ICapability[] caps = module.getDefinition().getCapabilities();
+
+ // First remove all existing capabilities from the "unresolved" map.
+ for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+ {
+ if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ // Get package name.
+ String pkgName = (String)
+ caps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
+ // Remove the module's capability for the package.
+ m_unresolvedPkgIndexMap.put(
+ pkgName,
+ removeModuleFromArray(
+ (IModule[]) m_unresolvedPkgIndexMap.get(pkgName),
+ module));
+ }
+ }
+
+ // Next create a copy of the module's capabilities so we can
+ // null out any capabilities that should be ignored.
+ ICapability[] capsCopy = (caps == null) ? null : new ICapability[caps.length];
+ if (capsCopy != null)
+ {
+ System.arraycopy(caps, 0, capsCopy, 0, caps.length);
+ }
+ // Loop through the module's capabilities to determine which ones
+ // can be ignored by seeing which ones satifies the wire requirements.
+// TODO: RB - Bug here because a requirement for a package need not overlap the
+// capability for that package and this assumes it does. This might
+// require us to introduce the notion of a substitutable capability.
+ IWire[] wires = module.getWires();
+ for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
+ {
+ // Loop through all wires to see if the current capability
+ // satisfies any of the wire requirements.
+ for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
+ {
+ // If the wire requirement is satisfied by the current capability,
+ // then check to see if the wire is to the module itself. If it
+ // is to another module, then null the current capability since
+ // it was both providing and requiring the same capability and
+ // the resolve process chose to import rather than provide that
+ // capability, therefore we should ignore it.
+ if (wires[wireIdx].getRequirement().isSatisfied(capsCopy[capIdx]))
+ {
+ if (!wires[wireIdx].getExporter().equals(module))
+ {
+ capsCopy[capIdx] = null;
+ }
+ break;
+ }
+ }
+ }
+
+ // Now loop through all capabilities and add them to the "resolved"
+ // capability and package index maps, ignoring any that were nulled out.
+ for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
+ {
+ if (capsCopy[capIdx] != null)
+ {
+ ICapability[] resolvedCaps = (ICapability[]) m_resolvedCapMap.get(module);
+ resolvedCaps = addCapabilityToArray(resolvedCaps, capsCopy[capIdx]);
+ m_resolvedCapMap.put(module, resolvedCaps);
+
+ // If the capability is a package, then add the exporter module
+ // of the wire to the "resolved" package index and remove it
+ // from the "unresolved" package index.
+ if (capsCopy[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ // Add to "resolved" package index.
+ indexPackageCapability(
+ m_resolvedPkgIndexMap,
+ module,
+ capsCopy[capIdx]);
+ }
+ }
+ }
+ }
+
+System.out.println("UNRESOLVED PACKAGES:");
+dumpPackageIndexMap(m_unresolvedPkgIndexMap);
+System.out.println("RESOLVED PACKAGES:");
+dumpPackageIndexMap(m_resolvedPkgIndexMap);
+ }
+
+ // TODO: FRAGMENT - Not very efficient.
+ public synchronized List getPotentialHosts(IModule fragment)
+ {
+ List hostList = new ArrayList();
+
+ IRequirement[] reqs = fragment.getDefinition().getRequirements();
+ IRequirement hostReq = null;
+ for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
+ {
+ if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
+ {
+ hostReq = reqs[reqIdx];
+ break;
+ }
+ }
+
+ IModule[] modules = getModules();
+ for (int modIdx = 0; (hostReq != null) && (modIdx < modules.length); modIdx++)
+ {
+ if (!fragment.equals(modules[modIdx]) && !isResolved(modules[modIdx]))
+ {
+ ICapability[] caps = modules[modIdx].getDefinition().getCapabilities();
+ for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+ {
+ if (caps[capIdx].getNamespace().equals(ICapability.HOST_NAMESPACE)
+ && hostReq.isSatisfied(caps[capIdx])
+ && !modules[modIdx].isStale())
+ {
+ hostList.add(modules[modIdx]);
+ break;
+ }
+ }
+ }
+ }
+
+ return hostList;
+ }
+
+// TODO: FRAGMENT - Not very efficient.
+ public synchronized Map getPotentialFragments(IModule host)
+ {
+// TODO: FRAGMENT - This should check to make sure that the host allows fragments.
+ Map fragmentMap = new HashMap();
+
+ ICapability[] caps = host.getDefinition().getCapabilities();
+ ICapability bundleCap = null;
+ for (int capIdx = 0; capIdx < caps.length; capIdx++)
+ {
+ if (caps[capIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
+ {
+ bundleCap = caps[capIdx];
+ break;
+ }
+ }
+
+ IModule[] modules = getModules();
+ for (int modIdx = 0; (bundleCap != null) && (modIdx < modules.length); modIdx++)
+ {
+ if (!host.equals(modules[modIdx]))
+ {
+ IRequirement[] reqs = modules[modIdx].getDefinition().getRequirements();
+ for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
+ {
+ if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE)
+ && reqs[reqIdx].isSatisfied(bundleCap)
+ && !modules[modIdx].isStale())
+ {
+ indexFragment(fragmentMap, modules[modIdx]);
+ break;
+ }
+ }
+ }
+ }
+
+ return fragmentMap;
+ }
+
+ public synchronized PackageSource[] getResolvedCandidates(IRequirement req)
+ {
+ // Synchronized on the module manager to make sure that no
+ // modules are added, removed, or resolved.
+ PackageSource[] candidates = m_emptySources;
+ if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
+ && (((Requirement) req).getPackageName() != null))
+ {
+ String pkgName = ((Requirement) req).getPackageName();
+ IModule[] modules = (IModule[]) m_resolvedPkgIndexMap.get(pkgName);
+
+ for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
+ {
+ ICapability resolvedCap = Util.getSatisfyingCapability(modules[modIdx], req);
+ if (resolvedCap != null)
+ {
+// TODO: RB - Is this permission check correct.
+ if ((System.getSecurityManager() != null) &&
+ !((BundleProtectionDomain) modules[modIdx].getContentLoader().getSecurityContext()).impliesDirect(
+ new PackagePermission(pkgName,
+ PackagePermission.EXPORT)))
+ {
+ m_logger.log(Logger.LOG_DEBUG,
+ "PackagePermission.EXPORT denied for "
+ + pkgName
+ + "from " + modules[modIdx].getId());
+ }
+ else
+ {
+ PackageSource[] tmp = new PackageSource[candidates.length + 1];
+ System.arraycopy(candidates, 0, tmp, 0, candidates.length);
+ tmp[candidates.length] =
+ new PackageSource(modules[modIdx], resolvedCap);
+ candidates = tmp;
+ }
+ }
+ }
+ }
+ else
+ {
+ Iterator i = m_resolvedCapMap.entrySet().iterator();
+ while (i.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ IModule module = (IModule) entry.getKey();
+ ICapability[] resolvedCaps = (ICapability[]) entry.getValue();
+ for (int capIdx = 0; capIdx < resolvedCaps.length; capIdx++)
+ {
+ if (req.isSatisfied(resolvedCaps[capIdx]))
+ {
+// TODO: RB - Is this permission check correct.
+ if (resolvedCaps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
+ (System.getSecurityManager() != null) &&
+ !((BundleProtectionDomain) module.getContentLoader().getSecurityContext()).impliesDirect(
+ new PackagePermission(
+ (String) resolvedCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY),
+ PackagePermission.EXPORT)))
+ {
+ m_logger.log(Logger.LOG_DEBUG,
+ "PackagePermission.EXPORT denied for "
+ + resolvedCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY)
+ + "from " + module.getId());
+ }
+ else
+ {
+ PackageSource[] tmp = new PackageSource[candidates.length + 1];
+ System.arraycopy(candidates, 0, tmp, 0, candidates.length);
+ tmp[candidates.length] = new PackageSource(module, resolvedCaps[capIdx]);
+ candidates = tmp;
+ }
+ }
+ }
+ }
+ }
+ Arrays.sort(candidates);
+ return candidates;
+ }
+
+ public synchronized PackageSource[] getUnresolvedCandidates(IRequirement req)
+ {
+ // Get all modules.
+ IModule[] modules = null;
+ if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
+ (((Requirement) req).getPackageName() != null))
+ {
+ modules = (IModule[]) m_unresolvedPkgIndexMap.get(((Requirement) req).getPackageName());
+ }
+ else
+ {
+ modules = getModules();
+ }
+
+ // Create list of compatible providers.
+ PackageSource[] candidates = m_emptySources;
+ for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
+ {
+ // Get the module's export package for the target package.
+ ICapability cap = Util.getSatisfyingCapability(modules[modIdx], req);
+ // If compatible and it is not currently resolved, then add
+ // the unresolved candidate to the list.
+ if ((cap != null) && !isResolved(modules[modIdx]))
+ {
+ PackageSource[] tmp = new PackageSource[candidates.length + 1];
+ System.arraycopy(candidates, 0, tmp, 0, candidates.length);
+ tmp[candidates.length] = new PackageSource(modules[modIdx], cap);
+ candidates = tmp;
+ }
+ }
+ Arrays.sort(candidates);
+ return candidates;
+ }
+
+ //
+ // Utility methods.
+ //
+
+ private void indexPackageCapability(Map map, IModule module, ICapability capability)
+ {
+ if (capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ String pkgName = (String)
+ capability.getProperties().get(ICapability.PACKAGE_PROPERTY);
+ IModule[] modules = (IModule[]) map.get(pkgName);
+
+ // We want to add the module into the list of exporters
+ // in sorted order (descending version and ascending bundle
+ // identifier). Insert using a simple binary search algorithm.
+ if (modules == null)
+ {
+ modules = new IModule[] { module };
+ }
+ else
+ {
+ Version version = (Version)
+ capability.getProperties().get(ICapability.VERSION_PROPERTY);
+ Version middleVersion = null;
+ int top = 0, bottom = modules.length - 1, middle = 0;
+ while (top <= bottom)
+ {
+ middle = (bottom - top) / 2 + top;
+ middleVersion = (Version)
+ getExportPackageCapability(
+ modules[middle], pkgName)
+ .getProperties()
+ .get(ICapability.VERSION_PROPERTY);
+ // Sort in reverse version order.
+ int cmp = middleVersion.compareTo(version);
+ if (cmp < 0)
+ {
+ bottom = middle - 1;
+ }
+ else if (cmp == 0)
+ {
+ // Sort further by ascending bundle ID.
+ long middleId = Util.getBundleIdFromModuleId(modules[middle].getId());
+ long exportId = Util.getBundleIdFromModuleId(module.getId());
+ if (middleId < exportId)
+ {
+ top = middle + 1;
+ }
+ else
+ {
+ bottom = middle - 1;
+ }
+ }
+ else
+ {
+ top = middle + 1;
+ }
+ }
+
+ // Ignore duplicates.
+ if ((top >= modules.length) || (modules[top] != module))
+ {
+ IModule[] newMods = new IModule[modules.length + 1];
+ System.arraycopy(modules, 0, newMods, 0, top);
+ System.arraycopy(modules, top, newMods, top + 1, modules.length - top);
+ newMods[top] = module;
+ modules = newMods;
+ }
+ }
+
+ map.put(pkgName, modules);
+ }
+ }
+
+ private void indexFragment(Map map, IModule module)
+ {
+ String symName = getBundleSymbolicName(module);
+ IModule[] modules = (IModule[]) map.get(symName);
+
+ // We want to add the fragment into the list of matching
+ // fragments in sorted order (descending version and
+ // ascending bundle identifier). Insert using a simple
+ // binary search algorithm.
+ if (modules == null)
+ {
+ modules = new IModule[] { module };
+ }
+ else
+ {
+ Version version = getBundleVersion(module);
+ Version middleVersion = null;
+ int top = 0, bottom = modules.length - 1, middle = 0;
+ while (top <= bottom)
+ {
+ middle = (bottom - top) / 2 + top;
+ middleVersion = getBundleVersion(modules[middle]);
+ // Sort in reverse version order.
+ int cmp = middleVersion.compareTo(version);
+ if (cmp < 0)
+ {
+ bottom = middle - 1;
+ }
+ else if (cmp == 0)
+ {
+ // Sort further by ascending bundle ID.
+ long middleId = Util.getBundleIdFromModuleId(modules[middle].getId());
+ long exportId = Util.getBundleIdFromModuleId(module.getId());
+ if (middleId < exportId)
+ {
+ top = middle + 1;
+ }
+ else
+ {
+ bottom = middle - 1;
+ }
+ }
+ else
+ {
+ top = middle + 1;
+ }
+ }
+
+ // Ignore duplicates.
+ if ((top >= modules.length) || (modules[top] != module))
+ {
+ IModule[] newMods = new IModule[modules.length + 1];
+ System.arraycopy(modules, 0, newMods, 0, top);
+ System.arraycopy(modules, top, newMods, top + 1, modules.length - top);
+ newMods[top] = module;
+ modules = newMods;
+ }
+ }
+
+ map.put(symName, modules);
+ }
+
+ private static IModule[] removeModuleFromArray(IModule[] modules, IModule m)
+ {
+ if (modules == null)
+ {
+ return m_emptyModules;
+ }
+
+ int idx = -1;
+ do
+ {
+ idx = -1;
+ for (int i = 0; i < modules.length; i++)
+ {
+ if (modules[i] == m)
+ {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx >= 0)
+ {
+ // If this is the module, then point to empty list.
+ if ((modules.length - 1) == 0)
+ {
+ modules = m_emptyModules;
+ }
+ // Otherwise, we need to do some array copying.
+ else
+ {
+ IModule[] newModules = new IModule[modules.length - 1];
+ System.arraycopy(modules, 0, newModules, 0, idx);
+ if (idx < newModules.length)
+ {
+ System.arraycopy(
+ modules, idx + 1, newModules, idx, newModules.length - idx);
+ }
+ modules = newModules;
+ }
+ }
+ }
+ while (idx >= 0);
+
+ return modules;
+ }
+
+ public static ICapability getExportPackageCapability(IModule m, String pkgName)
+ {
+ ICapability[] caps = m.getDefinition().getCapabilities();
+ for (int i = 0; (caps != null) && (i < caps.length); i++)
+ {
+ if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
+ caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
+ {
+ return caps[i];
+ }
+ }
+ return null;
+ }
+
+ private static ICapability[] addCapabilityToArray(ICapability[] caps, ICapability cap)
+ {
+ // Verify that the capability is not already in the array.
+ for (int i = 0; (caps != null) && (i < caps.length); i++)
+ {
+ if (caps[i].equals(cap))
+ {
+ return caps;
+ }
+ }
+
+ if (caps != null)
+ {
+ ICapability[] newCaps = new ICapability[caps.length + 1];
+ System.arraycopy(caps, 0, newCaps, 0, caps.length);
+ newCaps[caps.length] = cap;
+ caps = newCaps;
+ }
+ else
+ {
+ caps = new ICapability[] { cap };
+ }
+
+ return caps;
+ }
+ //
+ // Simple utility classes.
+ //
+
+ private static class ModuleData
+ {
+ public IModule m_module = null;
+ public boolean m_resolved = false;
+ public ModuleData(IModule module)
+ {
+ m_module = module;
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/PackageSource.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/PackageSource.java
new file mode 100644
index 0000000..e2367db
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/PackageSource.java
@@ -0,0 +1,120 @@
+/*
+ * 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.searchpolicy;
+
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.Capability;
+import org.apache.felix.moduleloader.ICapability;
+import org.apache.felix.moduleloader.IModule;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+/**
+ * This utility class represents a source for a given package, where
+ * the package is indicated by a particular module and the module's
+ * capability associated with that package. This class also implements
+ * <tt>Comparable</tt> so that two package sources can be compared based
+ * on version and bundle identifiers.
+ */
+public class PackageSource implements Comparable
+{
+ public IModule m_module = null;
+ public ICapability m_capability = null;
+
+ public PackageSource(IModule module, ICapability capability)
+ {
+ super();
+ m_module = module;
+ m_capability = capability;
+ }
+
+ public int compareTo(Object o)
+ {
+ PackageSource ps = (PackageSource) o;
+ Version thisVersion = null;
+ Version version = null;
+ if (m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ thisVersion = ((Capability) m_capability).getPackageVersion();
+ version = ((Capability) ps.m_capability).getPackageVersion();
+ }
+ else if (m_capability.getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ thisVersion = (Version) m_capability.getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ version = (Version) ps.m_capability.getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ }
+ if ((thisVersion != null) && (version != null))
+ {
+ int cmp = thisVersion.compareTo(version);
+ if (cmp < 0)
+ {
+ return 1;
+ }
+ else if (cmp > 0)
+ {
+ return -1;
+ }
+ else
+ {
+ long thisId = Util.getBundleIdFromModuleId(m_module.getId());
+ long id = Util.getBundleIdFromModuleId(ps.m_module.getId());
+ if (thisId < id)
+ {
+ return -1;
+ }
+ else if (thisId > id)
+ {
+ return 1;
+ }
+ return 0;
+ }
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ public int hashCode()
+ {
+ final int PRIME = 31;
+ int result = 1;
+ result = PRIME * result + ((m_capability == null) ? 0 : m_capability.hashCode());
+ result = PRIME * result + ((m_module == null) ? 0 : m_module.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+ if (o == null)
+ {
+ return false;
+ }
+ if (getClass() != o.getClass())
+ {
+ return false;
+ }
+ PackageSource ps = (PackageSource) o;
+ return m_module.equals(ps.m_module) && (m_capability == ps.m_capability);
+ }
+}
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
index 1fdf121..e7299e0 100755
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
@@ -22,72 +22,49 @@
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
-import org.apache.felix.framework.BundleProtectionDomain;
+import org.apache.felix.framework.Felix.FelixResolver;
import org.apache.felix.framework.Logger;
import org.apache.felix.framework.util.CompoundEnumeration;
import org.apache.felix.framework.util.SecurityManagerEx;
import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.util.manifestparser.Capability;
-import org.apache.felix.framework.util.manifestparser.R4Attribute;
-import org.apache.felix.framework.util.manifestparser.R4Directive;
import org.apache.felix.framework.util.manifestparser.R4Library;
import org.apache.felix.framework.util.manifestparser.Requirement;
import org.apache.felix.moduleloader.ICapability;
import org.apache.felix.moduleloader.IModule;
-import org.apache.felix.moduleloader.IModuleFactory;
import org.apache.felix.moduleloader.IRequirement;
import org.apache.felix.moduleloader.IWire;
-import org.apache.felix.moduleloader.ModuleEvent;
-import org.apache.felix.moduleloader.ModuleImpl;
-import org.apache.felix.moduleloader.ModuleListener;
import org.apache.felix.moduleloader.ResourceNotFoundException;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.PackagePermission;
-import org.osgi.framework.Version;
import org.osgi.framework.Bundle;
-public class R4SearchPolicyCore implements ModuleListener
+public class R4SearchPolicyCore
{
- private Logger m_logger = null;
- private Map m_configMap = null;
- private IModuleFactory m_factory = null;
- // Maps a package name to an array of modules.
- private Map m_unresolvedPkgIndexMap = new HashMap();
- // Maps a package name to an array of modules.
- private Map m_resolvedPkgIndexMap = new HashMap();
- // Maps a module to an array of capabilities.
- private Map m_resolvedCapMap = new HashMap();
- private Map m_moduleDataMap = new HashMap();
+ private final Logger m_logger;
+ private final Map m_configMap;
+ private final FelixResolver m_resolver;
// Boot delegation packages.
private String[] m_bootPkgs = null;
private boolean[] m_bootPkgWildcards = null;
- // Listener-related instance variables.
- private static final ResolveListener[] m_emptyListeners = new ResolveListener[0];
- private ResolveListener[] m_listeners = m_emptyListeners;
-
// Reusable empty array.
- public static final IModule[] m_emptyModules = new IModule[0];
public static final ICapability[] m_emptyCapabilities = new ICapability[0];
- public static final PackageSource[] m_emptySources = new PackageSource[0];
// Re-usable security manager for accessing class context.
private static SecurityManagerEx m_sm = new SecurityManagerEx();
- public R4SearchPolicyCore(Logger logger, Map configMap)
+ public R4SearchPolicyCore(
+ Logger logger, Map configMap, FelixResolver resolver)
{
m_logger = logger;
m_configMap = configMap;
+ m_resolver = resolver;
// Read the boot delegation property and parse it.
String s = (String) m_configMap.get(Constants.FRAMEWORK_BOOTDELEGATION);
@@ -107,53 +84,6 @@
}
}
- public IModuleFactory getModuleFactory()
- {
- return m_factory;
- }
-
- public void setModuleFactory(IModuleFactory factory)
- throws IllegalStateException
- {
- if (m_factory == null)
- {
- m_factory = factory;
- m_factory.addModuleListener(this);
- }
- else
- {
- throw new IllegalStateException(
- "Module manager is already initialized");
- }
- }
-
- /**
- * Private utility method to check module resolved state.
- * CONCURRENCY NOTE: This method must be called while holding
- * a lock on m_factory.
- **/
- private boolean isResolved(IModule module)
- {
- ModuleData data = (ModuleData) m_moduleDataMap.get(module);
- return (data == null) ? false : data.m_resolved;
- }
-
- /**
- * Private utility method to set module resolved state.
- * CONCURRENCY NOTE: This method must be called while holding
- * a lock on m_factory.
- **/
- private void setResolved(IModule module, boolean resolved)
- {
- ModuleData data = (ModuleData) m_moduleDataMap.get(module);
- if (data == null)
- {
- data = new ModuleData(module);
- m_moduleDataMap.put(module, data);
- }
- data.m_resolved = resolved;
- }
-
public Object[] definePackage(IModule module, String pkgName)
{
Map headerMap = ((ModuleDefinition) module.getDefinition()).getHeaders();
@@ -233,7 +163,7 @@
// for each class load.
try
{
- resolve(module);
+ m_resolver.resolve(module);
}
catch (ResolveException ex)
{
@@ -350,7 +280,15 @@
// At this point, the module's imports were searched and so was the
// the module's content. Now we make an attempt to load the
// class/resource via a dynamic import, if possible.
- IWire wire = attemptDynamicImport(module, pkgName);
+ IWire wire = null;
+ try
+ {
+ wire = m_resolver.resolveDynamicImport(module, pkgName);
+ }
+ catch (ResolveException ex)
+ {
+ // Ignore this since it is likely normal.
+ }
if (wire != null)
{
urls = wire.getResources(name);
@@ -374,7 +312,7 @@
// for each class load.
try
{
- resolve(module);
+ m_resolver.resolve(module);
}
catch (ResolveException ex)
{
@@ -521,7 +459,15 @@
// At this point, the module's imports were searched and so was the
// the module's content. Now we make an attempt to load the
// class/resource via a dynamic import, if possible.
- IWire wire = attemptDynamicImport(module, pkgName);
+ IWire wire = null;
+ try
+ {
+ wire = m_resolver.resolveDynamicImport(module, pkgName);
+ }
+ catch (ResolveException ex)
+ {
+ // Ignore this since it is likely normal.
+ }
// If the dynamic import was successful, then this initial
// time we must directly return the result from dynamically
@@ -622,259 +568,6 @@
return null;
}
- private IWire attemptDynamicImport(IModule importer, String pkgName)
- {
- R4Wire wire = null;
- PackageSource candidate = null;
-
- // We can only search dynamic imports if the bundle
- // doesn't import, export, nor require the package in
- // question. Check these conditions first.
- if (isDynamicImportAllowed(importer, pkgName))
- {
- // 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.
- IRequirement[] dynamics = importer.getDefinition().getDynamicRequirements();
- for (int dynIdx = 0; (dynamics != null) && (dynIdx < dynamics.length); dynIdx++)
- {
- IRequirement target =
- createDynamicRequirement(dynamics[dynIdx], pkgName);
- if (target != null)
- {
- // See if there is a candidate exporter that satisfies the
- // constrained dynamic requirement.
- try
- {
- // Lock module manager instance to ensure that nothing changes.
- synchronized (m_factory)
- {
- // Get "resolved" and "unresolved" candidates and put
- // the "resolved" candidates first.
- PackageSource[] resolved = getResolvedCandidates(target);
- PackageSource[] unresolved = getUnresolvedCandidates(target);
- PackageSource[] candidates = new PackageSource[resolved.length + unresolved.length];
- System.arraycopy(resolved, 0, candidates, 0, resolved.length);
- System.arraycopy(unresolved, 0, candidates, resolved.length, unresolved.length);
-
- // Take the first candidate that can resolve.
- for (int candIdx = 0;
- (candidate == null) && (candIdx < candidates.length);
- candIdx++)
- {
- try
- {
- if (resolveDynamicImportCandidate(
- candidates[candIdx].m_module, importer))
- {
- candidate = candidates[candIdx];
- }
- }
- catch (ResolveException ex)
- {
- // Ignore candidates that cannot resolve.
- }
- }
-
- if (candidate != null)
- {
- IWire[] wires = importer.getWires();
- IWire[] newWires = null;
- if (wires == null)
- {
- newWires = new IWire[1];
- }
- else
- {
- newWires = new IWire[wires.length + 1];
- System.arraycopy(wires, 0, newWires, 0, wires.length);
- }
-
- // Create the wire and add it to the module.
- wire = new R4Wire(
- importer, dynamics[dynIdx], candidate.m_module, candidate.m_capability);
- newWires[newWires.length - 1] = wire;
- ((ModuleImpl) importer).setWires(newWires);
-m_logger.log(Logger.LOG_DEBUG, "WIRE: " + newWires[newWires.length - 1]);
- return wire;
- }
- }
- }
- catch (Exception ex)
- {
- m_logger.log(Logger.LOG_ERROR, "Unable to dynamically import package.", ex);
- }
- }
- }
- }
-
- return null;
- }
-
- private boolean isDynamicImportAllowed(IModule importer, String pkgName)
- {
- // If any of the module exports this package, then we cannot
- // attempt to dynamically import it.
- ICapability[] caps = importer.getDefinition().getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
- {
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
- && caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
- {
- return false;
- }
- }
- // If any of our wires have this package, then we cannot
- // attempt to dynamically import it.
- IWire[] wires = importer.getWires();
- for (int i = 0; (wires != null) && (i < wires.length); i++)
- {
- if (wires[i].hasPackage(pkgName))
- {
- return false;
- }
- }
- // Ok to attempt to dynamically import the package.
- return true;
- }
-
- private IRequirement createDynamicRequirement(IRequirement dynReq, String pkgName)
- {
- IRequirement req = null;
-
- // First check to see if the dynamic requirement matches the
- // package name; this means we have to do wildcard matching.
- String dynPkgName = ((Requirement) dynReq).getPackageName();
- boolean wildcard = (dynPkgName.lastIndexOf(".*") >= 0);
- dynPkgName = (wildcard)
- ? dynPkgName.substring(0, dynPkgName.length() - 2) : dynPkgName;
- // If the dynamic requirement matches the package name, then
- // create a new requirement for the specific package.
- if (dynPkgName.equals("*") ||
- pkgName.equals(dynPkgName) ||
- (wildcard && pkgName.startsWith(dynPkgName + ".")))
- {
- // Create a new requirement based on the dynamic requirement,
- // but substitute the precise package name for which we are
- // looking, because it is not possible to use the potentially
- // wildcarded version in the dynamic requirement.
- R4Directive[] dirs = ((Requirement) dynReq).getDirectives();
- R4Attribute[] attrs = ((Requirement) dynReq).getAttributes();
- R4Attribute[] newAttrs = new R4Attribute[attrs.length];
- System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
- for (int attrIdx = 0; attrIdx < newAttrs.length; attrIdx++)
- {
- if (newAttrs[attrIdx].getName().equals(ICapability.PACKAGE_PROPERTY))
- {
- newAttrs[attrIdx] = new R4Attribute(
- ICapability.PACKAGE_PROPERTY, pkgName, false);
- break;
- }
- }
- req = new Requirement(ICapability.PACKAGE_NAMESPACE, dirs, newAttrs);
- }
-
- return req;
- }
-
- private boolean resolveDynamicImportCandidate(IModule provider, IModule importer)
- throws ResolveException
- {
- // If the provider of the dynamically imported package is not
- // resolved, then we need to calculate the candidates to resolve
- // it and see if there is a consistent class space for the
- // provider. If there is no consistent class space, then a resolve
- // exception is thrown.
- Map candidatesMap = new HashMap();
- if (!isResolved(provider))
- {
- populateCandidatesMap(candidatesMap, provider);
- findConsistentClassSpace(candidatesMap, provider);
- }
-
- // If the provider can be successfully resolved, then verify that
- // its class space is consistent with the existing class space of the
- // module that instigated the dynamic import.
- Map moduleMap = new HashMap();
- Map importerPkgMap = getModulePackages(moduleMap, importer, candidatesMap);
-
- // Now we need to calculate the "uses" constraints of every package
- // accessible to the provider module based on its current candidates.
- Map usesMap = calculateUsesConstraints(provider, moduleMap, candidatesMap);
-
- // Verify that none of the provider's implied "uses" constraints
- // in the uses map conflict with anything in the importing module's
- // package map.
- for (Iterator iter = usesMap.entrySet().iterator(); iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
-
- // For the given "used" package, get that package from the
- // importing module's package map, if present.
- ResolvedPackage rp = (ResolvedPackage) importerPkgMap.get(entry.getKey());
-
- // If the "used" package is also visible to the importing
- // module, make sure there is no conflicts in the implied
- // "uses" constraints.
- if (rp != null)
- {
- // Clone the resolve package so we can modify it.
- rp = (ResolvedPackage) rp.clone();
-
- // Loop through all implied "uses" constraints for the current
- // "used" package and verify that all package sources are
- // compatible with the package source of the importing module's
- // package map.
- List constraintList = (List) entry.getValue();
- for (int constIdx = 0; constIdx < constraintList.size(); constIdx++)
- {
- // Get a specific "uses" constraint for the current "used"
- // package.
- ResolvedPackage rpUses = (ResolvedPackage) constraintList.get(constIdx);
- // Determine if the implied "uses" constraint is compatible with
- // the improting module's package sources for the given "used"
- // package. They are compatible if one is the subset of the other.
- // Retain the union of the two sets if they are compatible.
- if (rpUses.isSubset(rp))
- {
- // Do nothing because we already have the superset.
- }
- else if (rp.isSubset(rpUses))
- {
- // Keep the superset, i.e., the union.
- rp.m_sourceList.clear();
- rp.m_sourceList.addAll(rpUses.m_sourceList);
- }
- else
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "Constraint violation for " + importer
- + " detected; module can see "
- + rp + " and " + rpUses);
- return false;
- }
- }
- }
- }
-
- Map resolvedModuleWireMap = createWires(candidatesMap, provider);
-
- // Fire resolved events for all resolved modules;
- // the resolved modules array will only be set if the resolve
- // was successful.
- if (resolvedModuleWireMap != null)
- {
- Iterator iter = resolvedModuleWireMap.entrySet().iterator();
- while (iter.hasNext())
- {
- fireModuleResolved((IModule) ((Map.Entry) iter.next()).getKey());
- }
- }
-
- return true;
- }
-
public String findLibrary(IModule module, String name)
{
// Remove leading slash, if present.
@@ -896,1914 +589,6 @@
return null;
}
- public PackageSource[] getResolvedCandidates(IRequirement req)
- {
- // Synchronized on the module manager to make sure that no
- // modules are added, removed, or resolved.
- synchronized (m_factory)
- {
- PackageSource[] candidates = m_emptySources;
- if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
- && (((Requirement) req).getPackageName() != null))
- {
- String pkgName = ((Requirement) req).getPackageName();
- IModule[] modules = (IModule[]) m_resolvedPkgIndexMap.get(pkgName);
-
- for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
- {
- ICapability resolvedCap = Util.getSatisfyingCapability(modules[modIdx], req);
- if (resolvedCap != null)
- {
-// TODO: RB - Is this permission check correct.
- if ((System.getSecurityManager() != null) &&
- !((BundleProtectionDomain) modules[modIdx].getContentLoader().getSecurityContext()).impliesDirect(
- new PackagePermission(pkgName,
- PackagePermission.EXPORT)))
- {
- m_logger.log(Logger.LOG_DEBUG,
- "PackagePermission.EXPORT denied for "
- + pkgName
- + "from " + modules[modIdx].getId());
- }
- else
- {
- PackageSource[] tmp = new PackageSource[candidates.length + 1];
- System.arraycopy(candidates, 0, tmp, 0, candidates.length);
- tmp[candidates.length] =
- new PackageSource(modules[modIdx], resolvedCap);
- candidates = tmp;
- }
- }
- }
- }
- else
- {
- Iterator i = m_resolvedCapMap.entrySet().iterator();
- while (i.hasNext())
- {
- Map.Entry entry = (Map.Entry) i.next();
- IModule module = (IModule) entry.getKey();
- ICapability[] resolvedCaps = (ICapability[]) entry.getValue();
- for (int capIdx = 0; capIdx < resolvedCaps.length; capIdx++)
- {
- if (req.isSatisfied(resolvedCaps[capIdx]))
- {
-// TODO: RB - Is this permission check correct.
- if (resolvedCaps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
- (System.getSecurityManager() != null) &&
- !((BundleProtectionDomain) module.getContentLoader().getSecurityContext()).impliesDirect(
- new PackagePermission(
- (String) resolvedCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY),
- PackagePermission.EXPORT)))
- {
- m_logger.log(Logger.LOG_DEBUG,
- "PackagePermission.EXPORT denied for "
- + resolvedCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY)
- + "from " + module.getId());
- }
- else
- {
- PackageSource[] tmp = new PackageSource[candidates.length + 1];
- System.arraycopy(candidates, 0, tmp, 0, candidates.length);
- tmp[candidates.length] = new PackageSource(module, resolvedCaps[capIdx]);
- candidates = tmp;
- }
- }
- }
- }
- }
- Arrays.sort(candidates);
- return candidates;
- }
- }
-
- public PackageSource[] getUnresolvedCandidates(IRequirement req)
- {
- // Synchronized on the module manager to make sure that no
- // modules are added, removed, or resolved.
- synchronized (m_factory)
- {
- // Get all modules.
- IModule[] modules = null;
- if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
- (((Requirement) req).getPackageName() != null))
- {
- modules = (IModule[]) m_unresolvedPkgIndexMap.get(((Requirement) req).getPackageName());
- }
- else
- {
- modules = m_factory.getModules();
- }
-
- // Create list of compatible providers.
- PackageSource[] candidates = m_emptySources;
- for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
- {
- // Get the module's export package for the target package.
- ICapability cap = Util.getSatisfyingCapability(modules[modIdx], req);
- // If compatible and it is not currently resolved, then add
- // the unresolved candidate to the list.
- if ((cap != null) && !isResolved(modules[modIdx]))
- {
- PackageSource[] tmp = new PackageSource[candidates.length + 1];
- System.arraycopy(candidates, 0, tmp, 0, candidates.length);
- tmp[candidates.length] = new PackageSource(modules[modIdx], cap);
- candidates = tmp;
- }
- }
- Arrays.sort(candidates);
- return candidates;
- }
- }
-
- public void resolve(IModule rootModule)
- throws ResolveException
- {
- // This map will be used to hold the final wires for all
- // resolved modules, which can then be used to fire resolved
- // events outside of the synchronized block.
- Map resolvedModuleWireMap = null;
- Map fragmentMap = null;
-
- // Synchronize on the module manager, because we don't want
- // any modules being added or removed while we are in the
- // middle of this operation.
- synchronized (m_factory)
- {
- // If the module is already resolved, then we can just return.
- if (isResolved(rootModule))
- {
- return;
- }
-
- // The root module is either a host or a fragment. If it is a host,
- // then we want to go ahead and resolve it. If it is a fragment, then
- // we want to select a host and resolve the host instead.
- IModule targetFragment = null;
-// TODO: FRAGMENT - Currently we just make a single selection of the available
-// fragments or hosts and try to resolve. In case of failure, we do not
-// backtrack. We will likely want to add backtracking.
- if (Util.isFragment(rootModule))
- {
- targetFragment = rootModule;
- List hostList = getPotentialHosts(targetFragment);
- rootModule = (IModule) hostList.get(0);
- }
-
- // Get the available fragments for the host.
- fragmentMap = getPotentialFragments(rootModule);
-
- // If the resolve was for a specific fragment, then
- // eliminate all other potential candidate fragments
- // of the same symbolic name.
- if (targetFragment != null)
- {
- fragmentMap.put(
- getBundleSymbolicName(targetFragment),
- new IModule[] { targetFragment });
- }
-
- // This variable maps an unresolved module to a list of candidate
- // sets, where there is one candidate set for each requirement that
- // must be resolved. A candidate set contains the potential canidates
- // available to resolve the requirement and the currently selected
- // candidate index.
- Map candidatesMap = new HashMap();
-
- // The first step is to populate the candidates map. This
- // will use the target module to populate the candidates map
- // with all potential modules that need to be resolved as a
- // result of resolving the target module. The key of the
- // map is a potential module to be resolved and the value is
- // a list of candidate sets, one for each of the module's
- // requirements, where each candidate set contains the potential
- // candidates for resolving the requirement. Not all modules in
- // this map will be resolved, only the target module and
- // any candidates selected to resolve its requirements and the
- // transitive requirements this implies.
- populateCandidatesMap(candidatesMap, rootModule);
-
- // The next step is to use the candidates map to determine if
- // the class space for the root module is consistent. This
- // is an iterative process that transitively walks the "uses"
- // relationships of all packages visible from the root module
- // checking for conflicts. If a conflict is found, it "increments"
- // the configuration of currently selected potential candidates
- // and tests them again. If this method returns, then it has found
- // a consistent set of candidates; otherwise, a resolve exception
- // is thrown if it exhausts all possible combinations and could
- // not find a consistent class space.
- findConsistentClassSpace(candidatesMap, rootModule);
-
- // The final step is to create the wires for the root module and
- // transitively all modules that are to be resolved from the
- // selected candidates for resolving the root module's imports.
- // When this call returns, each module's wiring and resolved
- // attributes are set. The resulting wiring map is used below
- // to fire resolved events outside of the synchronized block.
- // The resolved module wire map maps a module to its array of
- // wires.
- resolvedModuleWireMap = createWires(candidatesMap, rootModule);
-
- // Attach fragments to root module.
- if ((fragmentMap != null) && (fragmentMap.size() > 0))
- {
- List list = new ArrayList();
- for (Iterator iter = fragmentMap.entrySet().iterator(); iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
- String symName = (String) entry.getKey();
- IModule[] fragments = (IModule[]) entry.getValue();
-// TODO: FRAGMENT - For now, just attach first candidate.
- list.add(fragments[0]);
- setResolved(fragments[0], true);
-m_logger.log(Logger.LOG_DEBUG, "(FRAGMENT) WIRE: "
- + rootModule + " -> " + symName + " -> " + fragments[0]);
- }
- try
- {
- ((ModuleImpl) rootModule).attachFragments(
- (IModule[]) list.toArray(new IModule[list.size()]));
- }
- catch (Exception ex)
- {
- m_logger.log(Logger.LOG_ERROR, "Unable to attach fragments", ex);
- }
- }
- } // End of synchronized block on module manager.
-
- // Fire resolved events for all resolved modules;
- // the resolved modules array will only be set if the resolve
- // was successful after the root module was resolved.
- if (resolvedModuleWireMap != null)
- {
- Iterator iter = resolvedModuleWireMap.entrySet().iterator();
- while (iter.hasNext())
- {
- fireModuleResolved((IModule) ((Map.Entry) iter.next()).getKey());
- }
- iter = fragmentMap.entrySet().iterator();
- while (iter.hasNext())
- {
- fireModuleResolved(((IModule[]) ((Map.Entry) iter.next()).getValue())[0]);
- }
- }
- }
-
- // TODO: FRAGMENT - Not very efficient.
- private List getPotentialHosts(IModule fragment)
- throws ResolveException
- {
- List hostList = new ArrayList();
-
- IRequirement[] reqs = fragment.getDefinition().getRequirements();
- IRequirement hostReq = null;
- for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
- {
- if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
- {
- hostReq = reqs[reqIdx];
- break;
- }
- }
-
- IModule[] modules = m_factory.getModules();
- for (int modIdx = 0; (hostReq != null) && (modIdx < modules.length); modIdx++)
- {
- if (!fragment.equals(modules[modIdx]) && !isResolved(modules[modIdx]))
- {
- ICapability[] caps = modules[modIdx].getDefinition().getCapabilities();
- for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
- {
- if (caps[capIdx].getNamespace().equals(ICapability.HOST_NAMESPACE)
- && hostReq.isSatisfied(caps[capIdx])
- && !modules[modIdx].isStale())
- {
- hostList.add(modules[modIdx]);
- break;
- }
- }
- }
- }
-
- if (hostList.size() == 0)
- {
- throw new ResolveException("Unable to resolve.", fragment, hostReq);
- }
-
- return hostList;
- }
-
-// TODO: FRAGMENT - Not very efficient.
- private Map getPotentialFragments(IModule host)
- {
-// TODO: FRAGMENT - This should check to make sure that the host allows fragments.
- Map fragmentMap = new HashMap();
-
- ICapability[] caps = host.getDefinition().getCapabilities();
- ICapability bundleCap = null;
- for (int capIdx = 0; capIdx < caps.length; capIdx++)
- {
- if (caps[capIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
- {
- bundleCap = caps[capIdx];
- break;
- }
- }
-
- IModule[] modules = m_factory.getModules();
- for (int modIdx = 0; (bundleCap != null) && (modIdx < modules.length); modIdx++)
- {
- if (!host.equals(modules[modIdx]))
- {
- IRequirement[] reqs = modules[modIdx].getDefinition().getRequirements();
- for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
- {
- if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE)
- && reqs[reqIdx].isSatisfied(bundleCap)
- && !modules[modIdx].isStale())
- {
- indexFragment(fragmentMap, modules[modIdx]);
- break;
- }
- }
- }
- }
-
- return fragmentMap;
- }
-
-// TODO: FRAGMENT - Not very efficient.
- private static String getBundleSymbolicName(IModule module)
- {
- ICapability[] caps = module.getDefinition().getCapabilities();
- for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
- {
- if (caps[capIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- return (String)
- caps[capIdx].getProperties().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
- }
- }
- return null;
- }
-
-// TODO: FRAGMENT - Not very efficient.
- private static Version getBundleVersion(IModule module)
- {
- ICapability[] caps = module.getDefinition().getCapabilities();
- for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
- {
- if (caps[capIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- return (Version)
- caps[capIdx].getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
- }
- }
- return Version.emptyVersion;
- }
-
- private void indexFragment(Map map, IModule module)
- {
- String symName = getBundleSymbolicName(module);
- IModule[] modules = (IModule[]) map.get(symName);
-
- // We want to add the fragment into the list of matching
- // fragments in sorted order (descending version and
- // ascending bundle identifier). Insert using a simple
- // binary search algorithm.
- if (modules == null)
- {
- modules = new IModule[] { module };
- }
- else
- {
- Version version = getBundleVersion(module);
- Version middleVersion = null;
- int top = 0, bottom = modules.length - 1, middle = 0;
- while (top <= bottom)
- {
- middle = (bottom - top) / 2 + top;
- middleVersion = getBundleVersion(modules[middle]);
- // Sort in reverse version order.
- int cmp = middleVersion.compareTo(version);
- if (cmp < 0)
- {
- bottom = middle - 1;
- }
- else if (cmp == 0)
- {
- // Sort further by ascending bundle ID.
- long middleId = Util.getBundleIdFromModuleId(modules[middle].getId());
- long exportId = Util.getBundleIdFromModuleId(module.getId());
- if (middleId < exportId)
- {
- top = middle + 1;
- }
- else
- {
- bottom = middle - 1;
- }
- }
- else
- {
- top = middle + 1;
- }
- }
-
- // Ignore duplicates.
- if ((top >= modules.length) || (modules[top] != module))
- {
- IModule[] newMods = new IModule[modules.length + 1];
- System.arraycopy(modules, 0, newMods, 0, top);
- System.arraycopy(modules, top, newMods, top + 1, modules.length - top);
- newMods[top] = module;
- modules = newMods;
- }
- }
-
- map.put(symName, modules);
- }
-
- private void populateCandidatesMap(Map candidatesMap, IModule module)
- throws ResolveException
- {
- // Detect cycles.
- if (candidatesMap.get(module) != null)
- {
- return;
- }
-
- // List to hold the resolving candidate sets for the module's
- // requirements.
- List candSetList = new ArrayList();
-
- // Even though the candidate set list is currently empty, we
- // record it in the candidates map early so we can use it to
- // detect cycles.
- candidatesMap.put(module, candSetList);
-
- // Loop through each requirement and calculate its resolving
- // set of candidates.
- IRequirement[] reqs = module.getDefinition().getRequirements();
- for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
- {
- // Get the candidates from the "resolved" and "unresolved"
- // package maps. The "resolved" candidates have higher priority
- // than "unresolved" ones, so put the "resolved" candidates
- // at the front of the list of candidates.
- PackageSource[] resolved = getResolvedCandidates(reqs[reqIdx]);
- PackageSource[] unresolved = getUnresolvedCandidates(reqs[reqIdx]);
- PackageSource[] candidates = new PackageSource[resolved.length + unresolved.length];
- System.arraycopy(resolved, 0, candidates, 0, resolved.length);
- System.arraycopy(unresolved, 0, candidates, resolved.length, unresolved.length);
-
- // If we have candidates, then we need to recursively populate
- // the resolver map with each of them.
- ResolveException rethrow = null;
- if (candidates.length > 0)
- {
- for (int candIdx = 0; candIdx < candidates.length; candIdx++)
- {
- try
- {
- // Only populate the resolver map with modules that
- // are not already resolved.
- if (!isResolved(candidates[candIdx].m_module))
- {
- populateCandidatesMap(candidatesMap, candidates[candIdx].m_module);
- }
- }
- catch (ResolveException ex)
- {
- // If we received a resolve exception, then the
- // current candidate is not resolvable for some
- // reason and should be removed from the list of
- // candidates. For now, just null it.
- candidates[candIdx] = null;
- rethrow = ex;
- }
- }
-
- // Remove any nulled candidates to create the final list
- // of available candidates.
- candidates = shrinkCandidateArray(candidates);
- }
-
- // If no candidates exist at this point, then throw a
- // resolve exception unless the import is optional.
- if ((candidates.length == 0) && !reqs[reqIdx].isOptional())
- {
- // If we have received an exception while trying to populate
- // the resolver map, rethrow that exception since it might
- // be useful. NOTE: This is not necessarily the "only"
- // correct exception, since it is possible that multiple
- // candidates were not resolvable, but it is better than
- // nothing.
- if (rethrow != null)
- {
- throw rethrow;
- }
- else
- {
- throw new ResolveException(
- "Unable to resolve.", module, reqs[reqIdx]);
- }
- }
- else if (candidates.length > 0)
- {
- candSetList.add(
- new CandidateSet(module, reqs[reqIdx], candidates));
- }
- }
- }
-
- private void dumpPackageIndexMap(Map pkgIndexMap)
- {
- synchronized (this)
- {
- for (Iterator i = pkgIndexMap.entrySet().iterator(); i.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) i.next();
- IModule[] modules = (IModule[]) entry.getValue();
- if ((modules != null) && (modules.length > 0))
- {
- if (!((modules.length == 1) && modules[0].getId().equals("0")))
- {
- System.out.println(" " + entry.getKey());
- for (int j = 0; j < modules.length; j++)
- {
- System.out.println(" " + modules[j]);
- }
- }
- }
- }
- }
- }
-
- private void dumpPackageSources(Map pkgMap)
- {
- for (Iterator i = pkgMap.entrySet().iterator(); i.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) i.next();
- ResolvedPackage rp = (ResolvedPackage) entry.getValue();
- System.out.println(rp);
- }
- }
-
- private void findConsistentClassSpace(Map candidatesMap, IModule rootModule)
- throws ResolveException
- {
- List candidatesList = null;
-
- // The reusable module map maps a module to a map of
- // resolved packages that are accessible by the given
- // module. The set of resolved packages is calculated
- // from the current candidates of the candidates map
- // and the module's metadata.
- Map moduleMap = new HashMap();
-
- // Reusable map used to test for cycles.
- Map cycleMap = new HashMap();
-
- // Test the current potential candidates to determine if they
- // are consistent. Keep looping until we find a consistent
- // set or an exception is thrown.
- while (!isSingletonConsistent(rootModule, moduleMap, candidatesMap) ||
- !isClassSpaceConsistent(rootModule, moduleMap, cycleMap, candidatesMap))
- {
- // The incrementCandidateConfiguration() method requires
- // ordered access to the candidates map, so we will create
- // a reusable list once right here.
- if (candidatesList == null)
- {
- candidatesList = new ArrayList();
- for (Iterator iter = candidatesMap.entrySet().iterator();
- iter.hasNext(); )
- {
- candidatesList.add(((Map.Entry) iter.next()).getValue());
- }
- }
-
- // Increment the candidate configuration so we can test again.
- incrementCandidateConfiguration(candidatesList);
-
- // Clear the module map.
- moduleMap.clear();
-
- // Clear the cycle map.
- cycleMap.clear();
- }
- }
-
- /**
- * This methd checks to see if the target module and any of the candidate
- * modules to resolve its dependencies violate any singleton constraints.
- * Actually, it just creates a map of resolved singleton modules and then
- * delegates all checking to another recursive method.
- *
- * @param targetModule the module that is the root of the tree of modules to check.
- * @param moduleMap a map to cache the package space of each module.
- * @param candidatesMap a map containing the all candidates to resolve all
- * dependencies for all modules.
- * @return <tt>true</tt> if all candidates are consistent with respect to singletons,
- * <tt>false</tt> otherwise.
- **/
- private boolean isSingletonConsistent(IModule targetModule, Map moduleMap, Map candidatesMap)
- {
- // Create a map of all resolved singleton modules.
- Map singletonMap = new HashMap();
- IModule[] modules = m_factory.getModules();
- for (int i = 0; (modules != null) && (i < modules.length); i++)
- {
- if (isResolved(modules[i]) && isSingleton(modules[i]))
- {
- String symName = getBundleSymbolicName(modules[i]);
- singletonMap.put(symName, symName);
- }
- }
-
- return areCandidatesSingletonConsistent(
- targetModule, singletonMap, moduleMap, new HashMap(), candidatesMap);
- }
-
- /**
- * This method recursive checks the target module and all of its transitive
- * dependency modules to verify that they do not violate a singleton constraint.
- * If the target module is a singleton, then it checks that againts existing
- * singletons. Then it checks all current unresolved candidates recursively.
- *
- * @param targetModule the module that is the root of the tree of modules to check.
- * @param singletonMap the current map of singleton symbolic names.
- * @param moduleMap a map to cache the package space of each module.
- * @param cycleMap a map to detect cycles.
- * @param candidatesMap a map containing the all candidates to resolve all
- * dependencies for all modules.
- * @return <tt>true</tt> if all candidates are consistent with respect to singletons,
- * <tt>false</tt> otherwise.
- **/
- private boolean areCandidatesSingletonConsistent(
- IModule targetModule, Map singletonMap, Map moduleMap, Map cycleMap, Map candidatesMap)
- {
- // If we are in a cycle, then assume true for now.
- if (cycleMap.get(targetModule) != null)
- {
- return true;
- }
-
- // Record the target module in the cycle map.
- cycleMap.put(targetModule, targetModule);
-
- // Check to see if the targetModule violates a singleton.
- // If not and it is a singleton, then add it to the singleton
- // map since it will constrain other singletons.
- String symName = getBundleSymbolicName(targetModule);
- boolean isSingleton = isSingleton(targetModule);
- if (isSingleton && singletonMap.containsKey(symName))
- {
- return false;
- }
- else if (isSingleton)
- {
- singletonMap.put(symName, symName);
- }
-
- // Get the package space of the target module.
- Map pkgMap = null;
- try
- {
- pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
- }
- catch (ResolveException ex)
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "Constraint violation for " + targetModule + " detected.",
- ex);
- return false;
- }
-
- // Loop through all of the target module's accessible packages and
- // verify that all package sources are consistent.
- for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
- // Get the resolved package, which contains the set of all
- // package sources for the given package.
- ResolvedPackage rp = (ResolvedPackage) entry.getValue();
- // Loop through each package source and test if it is consistent.
- for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
- {
- // If the module for this package source is not resolved, then
- // we have to see if resolving it would violate a singleton
- // constraint.
- PackageSource ps = (PackageSource) rp.m_sourceList.get(srcIdx);
- if (!isResolved(ps.m_module))
- {
- return areCandidatesSingletonConsistent(ps.m_module, singletonMap, moduleMap, cycleMap, candidatesMap);
- }
- }
- }
-
- return true;
- }
-
- /**
- * Returns true if the specified module is a singleton
- * (i.e., directive singleton:=true in Bundle-SymbolicName).
- *
- * @param module the module to check for singleton status.
- * @return true if the module is a singleton, false otherwise.
- **/
- private boolean isSingleton(IModule module)
- {
- final ICapability[] modCaps = Util.getCapabilityByNamespace(
- module, Capability.MODULE_NAMESPACE);
- if (modCaps == null || modCaps.length == 0)
- {
- // this should never happen?
- return false;
- }
- final R4Directive[] dirs = ((Capability) modCaps[0]).getDirectives();
- for (int dirIdx = 0; (dirs != null) && (dirIdx < dirs.length); dirIdx++)
- {
- if (dirs[dirIdx].getName().equalsIgnoreCase(Constants.SINGLETON_DIRECTIVE)
- && Boolean.valueOf(dirs[dirIdx].getValue()).booleanValue())
- {
- return true;
- }
- }
- return false;
- }
-
- private boolean isClassSpaceConsistent(
- IModule targetModule, Map moduleMap, Map cycleMap, Map candidatesMap)
- {
-//System.out.println("isClassSpaceConsistent("+targetModule+")");
- // If we are in a cycle, then assume true for now.
- if (cycleMap.get(targetModule) != null)
- {
- return true;
- }
-
- // Record the target module in the cycle map.
- cycleMap.put(targetModule, targetModule);
-
- // Get the package map for the target module, which is a
- // map of all packages accessible to the module and their
- // associated package sources.
- Map pkgMap = null;
- try
- {
- pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
- }
- catch (ResolveException ex)
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "Constraint violation for " + targetModule + " detected.",
- ex);
- return false;
- }
-
- // Loop through all of the target module's accessible packages and
- // verify that all package sources are consistent.
- for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
- // Get the resolved package, which contains the set of all
- // package sources for the given package.
- ResolvedPackage rp = (ResolvedPackage) entry.getValue();
- // Loop through each package source and test if it is consistent.
- for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
- {
- PackageSource ps = (PackageSource) rp.m_sourceList.get(srcIdx);
- if (!isClassSpaceConsistent(ps.m_module, moduleMap, cycleMap, candidatesMap))
- {
- return false;
- }
- }
- }
-
- // Now we need to calculate the "uses" constraints of every package
- // accessible to the target module based on the current candidates.
- Map usesMap = null;
- try
- {
- usesMap = calculateUsesConstraints(targetModule, moduleMap, candidatesMap);
- }
- catch (ResolveException ex)
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "Constraint violation for " + targetModule + " detected.",
- ex);
- return false;
- }
-
- // Verify that none of the implied "uses" constraints in the uses map
- // conflict with anything in the target module's package map.
- for (Iterator iter = usesMap.entrySet().iterator(); iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
-
- // For the given "used" package, get that package from the
- // target module's package map, if present.
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
-
- // If the "used" package is also visible to the target module,
- // make sure there is no conflicts in the implied "uses"
- // constraints.
- if (rp != null)
- {
- // Clone the resolve package so we can modify it.
- rp = (ResolvedPackage) rp.clone();
-
- // Loop through all implied "uses" constraints for the current
- // "used" package and verify that all package sources are
- // compatible with the package source of the root module's
- // package map.
- List constraintList = (List) entry.getValue();
- for (int constIdx = 0; constIdx < constraintList.size(); constIdx++)
- {
- // Get a specific "uses" constraint for the current "used"
- // package.
- ResolvedPackage rpUses = (ResolvedPackage) constraintList.get(constIdx);
- // Determine if the implied "uses" constraint is compatible with
- // the target module's package sources for the given "used"
- // package. They are compatible if one is the subset of the other.
- // Retain the union of the two sets if they are compatible.
- if (rpUses.isSubset(rp))
- {
- // Do nothing because we already have the superset.
- }
- else if (rp.isSubset(rpUses))
- {
- // Keep the superset, i.e., the union.
- rp.m_sourceList.clear();
- rp.m_sourceList.addAll(rpUses.m_sourceList);
- }
- else
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "Constraint violation for " + targetModule
- + " detected; module can see "
- + rp + " and " + rpUses);
- return false;
- }
- }
- }
- }
-
- return true;
- }
-
- private Map calculateUsesConstraints(
- IModule targetModule, Map moduleMap, Map candidatesMap)
- throws ResolveException
- {
-//System.out.println("calculateUsesConstraints("+targetModule+")");
- // Map to store calculated uses constraints. This maps a
- // package name to a list of resolved packages, where each
- // resolved package represents a constraint on anyone
- // importing the given package name. This map is returned
- // by this method.
- Map usesMap = new HashMap();
-
- // Re-usable map to detect cycles.
- Map cycleMap = new HashMap();
-
- // Get all packages accessible by the target module.
- Map pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
-
- // Each package accessible from the target module is potentially
- // comprised of one or more modules, called package sources. The
- // "uses" constraints implied by all package sources must be
- // calculated and combined to determine the complete set of implied
- // "uses" constraints for each package accessible by the target module.
- for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
- ResolvedPackage rp = (ResolvedPackage) entry.getValue();
- for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
- {
- usesMap = calculateUsesConstraints(
- (PackageSource) rp.m_sourceList.get(srcIdx),
- moduleMap, usesMap, cycleMap, candidatesMap);
- }
- }
- return usesMap;
- }
-
- private Map calculateUsesConstraints(
- PackageSource psTarget, Map moduleMap, Map usesMap,
- Map cycleMap, Map candidatesMap)
- throws ResolveException
- {
-//System.out.println("calculateUsesConstraints2("+psTarget.m_module+")");
- // If we are in a cycle, then return for now.
- if (cycleMap.get(psTarget) != null)
- {
- return usesMap;
- }
-
- // Record the target package source in the cycle map.
- cycleMap.put(psTarget, psTarget);
-
- // Get all packages accessible from the module of the
- // target package source.
- Map pkgMap = getModulePackages(moduleMap, psTarget.m_module, candidatesMap);
-
- // Get capability (i.e., package) of the target package source.
- Capability cap = (Capability) psTarget.m_capability;
-
- // Loop through all "used" packages of the capability.
- for (int i = 0; i < cap.getUses().length; i++)
- {
- // The target package source module should have a resolved package
- // for the "used" package in its set of accessible packages,
- // since it claims to use it, so get the associated resolved
- // package.
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(cap.getUses()[i]);
-
- // In general, the resolved package should not be null,
- // but check for safety.
- if (rp != null)
- {
- // First, iterate through all package sources for the resolved
- // package associated with the current "used" package and calculate
- // and combine the "uses" constraints for each package source.
- for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
- {
- usesMap = calculateUsesConstraints(
- (PackageSource) rp.m_sourceList.get(srcIdx),
- moduleMap, usesMap, cycleMap, candidatesMap);
- }
-
- // Then, add the resolved package for the current "used" package
- // as a "uses" constraint too; add it to an existing constraint
- // list if the current "used" package is already in the uses map.
- List constraintList = (List) usesMap.get(cap.getUses()[i]);
- if (constraintList == null)
- {
- constraintList = new ArrayList();
- }
- constraintList.add(rp);
- usesMap.put(cap.getUses()[i], constraintList);
- }
- }
-
- return usesMap;
- }
-
- private Map getModulePackages(Map moduleMap, IModule module, Map candidatesMap)
- throws ResolveException
- {
- Map map = (Map) moduleMap.get(module);
-
- if (map == null)
- {
- map = calculateModulePackages(module, candidatesMap);
- moduleMap.put(module, map);
- }
- return map;
- }
-
- /**
- * <p>
- * Calculates the module's set of accessible packages and their
- * assocaited package sources. This method uses the current candidates
- * for resolving the module's requirements from the candidate map
- * to calculate the module's accessible packages.
- * </p>
- * @param module the module whose package map is to be calculated.
- * @param candidatesMap the map of potential candidates for resolving
- * the module's requirements.
- * @return a map of the packages accessible to the specified module where
- * the key of the map is the package name and the value of the map
- * is a ResolvedPackage.
- **/
- private Map calculateModulePackages(IModule module, Map candidatesMap)
- throws ResolveException
- {
-//System.out.println("calculateModulePackages("+module+")");
- Map importedPackages = calculateImportedPackages(module, candidatesMap);
- Map exportedPackages = calculateExportedPackages(module);
- Map requiredPackages = calculateRequiredPackages(module, candidatesMap);
-
- // Merge exported packages into required packages. If a package is both
- // exported and required, then append the exported source to the end of
- // the require package sources; otherwise just add it to the package map.
- for (Iterator i = exportedPackages.entrySet().iterator(); i.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) i.next();
- ResolvedPackage rpReq = (ResolvedPackage) requiredPackages.get(entry.getKey());
- if (rpReq != null)
- {
- // Merge exported and required packages, avoiding duplicate
- // package sources and maintaining ordering.
- ResolvedPackage rpExport = (ResolvedPackage) entry.getValue();
- rpReq.merge(rpExport);
- }
- else
- {
- requiredPackages.put(entry.getKey(), entry.getValue());
- }
- }
-
- // Merge imported packages into required packages. Imports overwrite
- // any required and/or exported package.
- for (Iterator i = importedPackages.entrySet().iterator(); i.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) i.next();
- requiredPackages.put(entry.getKey(), entry.getValue());
- }
-
- return requiredPackages;
- }
-
- private Map calculateImportedPackages(IModule targetModule, Map candidatesMap)
- throws ResolveException
- {
- return (candidatesMap.get(targetModule) == null)
- ? calculateImportedPackagesResolved(targetModule)
- : calculateImportedPackagesUnresolved(targetModule, candidatesMap);
- }
-
- private Map calculateImportedPackagesUnresolved(IModule targetModule, Map candidatesMap)
- throws ResolveException
- {
-//System.out.println("calculateImportedPackagesUnresolved("+targetModule+")");
- Map pkgMap = new HashMap();
-
- // Get the candidate set list to get all candidates for
- // all of the target module's requirements.
- List candSetList = (List) candidatesMap.get(targetModule);
-
- // Loop through all candidate sets that represent import dependencies
- // for the target module and add the current candidate's package source
- // to the imported package map.
- for (int candSetIdx = 0; (candSetList != null) && (candSetIdx < candSetList.size()); candSetIdx++)
- {
- CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
- PackageSource ps = cs.m_candidates[cs.m_idx];
-
- if (ps.m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- String pkgName = (String)
- ps.m_capability.getProperties().get(ICapability.PACKAGE_PROPERTY);
-
- ResolvedPackage rp = new ResolvedPackage(pkgName);
- rp.m_sourceList.add(ps);
- pkgMap.put(rp.m_name, rp);
- }
- }
-
- return pkgMap;
- }
-
- private Map calculateImportedPackagesResolved(IModule targetModule)
- throws ResolveException
- {
-//System.out.println("calculateImportedPackagesResolved("+targetModule+")");
- Map pkgMap = new HashMap();
-
- // Loop through the target module's wires for package
- // dependencies and add the resolved package source to the
- // imported package map.
- IWire[] wires = targetModule.getWires();
- for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
- {
- if (wires[wireIdx].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- String pkgName = (String)
- wires[wireIdx].getCapability().getProperties().get(ICapability.PACKAGE_PROPERTY);
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
- rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
- rp.m_sourceList.add(new PackageSource(wires[wireIdx].getExporter(), wires[wireIdx].getCapability()));
- pkgMap.put(rp.m_name, rp);
- }
- }
-
- return pkgMap;
- }
-
- private Map calculateExportedPackages(IModule targetModule)
- {
-//System.out.println("calculateExportedPackages("+targetModule+")");
- Map pkgMap = new HashMap();
-
- // Loop through the target module's capabilities that represent
- // exported packages and add them to the exported package map.
- ICapability[] caps = targetModule.getDefinition().getCapabilities();
- for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
- {
- if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- String pkgName = (String)
- caps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
- rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
- rp.m_sourceList.add(new PackageSource(targetModule, caps[capIdx]));
- pkgMap.put(rp.m_name, rp);
- }
- }
-
- return pkgMap;
- }
-
- private Map calculateRequiredPackages(IModule targetModule, Map candidatesMap)
- {
- return (candidatesMap.get(targetModule) == null)
- ? calculateRequiredPackagesResolved(targetModule)
- : calculateRequiredPackagesUnresolved(targetModule, candidatesMap);
- }
-
- private Map calculateRequiredPackagesUnresolved(IModule targetModule, Map candidatesMap)
- {
-//System.out.println("calculateRequiredPackagesUnresolved("+targetModule+")");
- Map pkgMap = new HashMap();
-
- // Loop through target module's candidate list for candidates
- // for its module dependencies and merge re-exported packages.
- List candSetList = (List) candidatesMap.get(targetModule);
- for (int candSetIdx = 0; (candSetList != null) && (candSetIdx < candSetList.size()); candSetIdx++)
- {
- CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
- PackageSource ps = cs.m_candidates[cs.m_idx];
-
- // If the capabaility is a module dependency, then flatten it to packages.
- if (ps.m_capability.getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- // Calculate transitively required packages.
- Map cycleMap = new HashMap();
- cycleMap.put(targetModule, targetModule);
- Map requireMap =
- calculateExportedAndReexportedPackages(
- ps, candidatesMap, cycleMap);
-
- // Take the flattened required package map for the current
- // module dependency and merge it into the existing map
- // of required packages.
- for (Iterator reqIter = requireMap.entrySet().iterator(); reqIter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) reqIter.next();
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
- if (rp != null)
- {
- // Merge required packages, avoiding duplicate
- // package sources and maintaining ordering.
- ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
- rp.merge(rpReq);
- }
- else
- {
- pkgMap.put(entry.getKey(), entry.getValue());
- }
- }
- }
- }
-
- return pkgMap;
- }
-
- private Map calculateRequiredPackagesResolved(IModule targetModule)
- {
-//System.out.println("calculateRequiredPackagesResolved("+targetModule+")");
- Map pkgMap = new HashMap();
-
- // Loop through target module's wires for module dependencies
- // and merge re-exported packages.
- IWire[] wires = targetModule.getWires();
- for (int i = 0; (wires != null) && (i < wires.length); i++)
- {
- // If the wire is a module dependency, then flatten it to packages.
- if (wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- // Calculate transitively required packages.
- // We can call calculateExportedAndReexportedPackagesResolved()
- // directly, since we know all dependencies have to be resolved
- // because this module itself is resolved.
- Map cycleMap = new HashMap();
- cycleMap.put(targetModule, targetModule);
- Map requireMap =
- calculateExportedAndReexportedPackagesResolved(
- wires[i].getExporter(), cycleMap);
-
- // Take the flattened required package map for the current
- // module dependency and merge it into the existing map
- // of required packages.
- for (Iterator reqIter = requireMap.entrySet().iterator(); reqIter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) reqIter.next();
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
- if (rp != null)
- {
- // Merge required packages, avoiding duplicate
- // package sources and maintaining ordering.
- ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
- rp.merge(rpReq);
- }
- else
- {
- pkgMap.put(entry.getKey(), entry.getValue());
- }
- }
- }
- }
-
- return pkgMap;
- }
-
- private Map calculateExportedAndReexportedPackages(
- PackageSource psTarget, Map candidatesMap, Map cycleMap)
- {
- return (candidatesMap.get(psTarget.m_module) == null)
- ? calculateExportedAndReexportedPackagesResolved(psTarget.m_module, cycleMap)
- : calculateExportedAndReexportedPackagesUnresolved(psTarget, candidatesMap, cycleMap);
- }
-
- private Map calculateExportedAndReexportedPackagesUnresolved(
- PackageSource psTarget, Map candidatesMap, Map cycleMap)
- {
-//System.out.println("calculateExportedAndReexportedPackagesUnresolved("+psTarget.m_module+")");
- Map pkgMap = new HashMap();
-
- if (cycleMap.get(psTarget.m_module) != null)
- {
- return pkgMap;
- }
-
- cycleMap.put(psTarget.m_module, psTarget.m_module);
-
- // Loop through all current candidates for target module's dependencies
- // and calculate the module's complete set of required packages (and
- // their associated package sources) and the complete set of required
- // packages to be re-exported.
- Map allRequiredMap = new HashMap();
- Map reexportedPkgMap = new HashMap();
- List candSetList = (List) candidatesMap.get(psTarget.m_module);
- for (int candSetIdx = 0; candSetIdx < candSetList.size(); candSetIdx++)
- {
- CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
- PackageSource ps = cs.m_candidates[cs.m_idx];
-
- // If the candidate is resolving a module dependency, then
- // flatten the required packages if they are re-exported.
- if (ps.m_capability.getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- // Determine if required packages are re-exported.
- boolean reexport = false;
- R4Directive[] dirs = ((Requirement) cs.m_requirement).getDirectives();
- for (int dirIdx = 0;
- !reexport && (dirs != null) && (dirIdx < dirs.length); dirIdx++)
- {
- if (dirs[dirIdx].getName().equals(Constants.VISIBILITY_DIRECTIVE)
- && dirs[dirIdx].getValue().equals(Constants.VISIBILITY_REEXPORT))
- {
- reexport = true;
- }
- }
-
- // Recursively calculate the required packages for the
- // current candidate.
- Map requiredMap = calculateExportedAndReexportedPackages(ps, candidatesMap, cycleMap);
-
- // Merge the candidate's exported and required packages
- // into the complete set of required packages.
- for (Iterator reqIter = requiredMap.entrySet().iterator(); reqIter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) reqIter.next();
- String pkgName = (String) entry.getKey();
-
- // Merge the current set of required packages into
- // the overall complete set of required packages.
- // We calculate all the required packages, because
- // despite the fact that some packages will be required
- // "privately" and some will be required "reexport", any
- // re-exported package sources will ultimately need to
- // be combined with privately required package sources,
- // if the required packages overlap. This is one of the
- // bad things about require-bundle behavior, it does not
- // necessarily obey the visibility rules declared in the
- // dependency.
- ResolvedPackage rp = (ResolvedPackage) allRequiredMap.get(pkgName);
- if (rp != null)
- {
- // Create the union of all package sources.
- ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
- rp.merge(rpReq);
- }
- else
- {
- // Add package to required map.
- allRequiredMap.put(pkgName, entry.getValue());
- }
-
- // Keep track of all required packages to be re-exported.
- // All re-exported packages will need to be merged into the
- // target module's package map and become part of its overall
- // export signature.
- if (reexport)
- {
- reexportedPkgMap.put(pkgName, pkgName);
- }
- }
- }
- }
-
- // For the target module we have now calculated its entire set
- // of required packages and their associated package sources in
- // allRequiredMap and have calculated all packages to be re-exported
- // in reexportedPkgMap. Add all re-exported required packages to the
- // target module's package map since they will be part of its export
- // signature.
- for (Iterator iter = reexportedPkgMap.entrySet().iterator(); iter.hasNext(); )
- {
- String pkgName = (String) ((Map.Entry) iter.next()).getKey();
- pkgMap.put(pkgName, allRequiredMap.get(pkgName));
- }
-
- // Now loop through the target module's export package capabilities and
- // add the target module as a package source for any exported packages.
- ICapability[] candCaps = psTarget.m_module.getDefinition().getCapabilities();
- for (int capIdx = 0; (candCaps != null) && (capIdx < candCaps.length); capIdx++)
- {
- if (candCaps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- String pkgName = (String)
- candCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
- rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
- rp.m_sourceList.add(new PackageSource(psTarget.m_module, candCaps[capIdx]));
- pkgMap.put(rp.m_name, rp);
- }
- }
-
- return pkgMap;
- }
-
- private Map calculateExportedAndReexportedPackagesResolved(
- IModule targetModule, Map cycleMap)
- {
-//System.out.println("calculateExportedAndRequiredPackagesResolved("+targetModule+")");
- Map pkgMap = new HashMap();
-
- if (cycleMap.get(targetModule) != null)
- {
- return pkgMap;
- }
-
- cycleMap.put(targetModule, targetModule);
-
- // Loop through all wires for the target module's module dependencies
- // and calculate the module's complete set of required packages (and
- // their associated package sources) and the complete set of required
- // packages to be re-exported.
- Map allRequiredMap = new HashMap();
- Map reexportedPkgMap = new HashMap();
- IWire[] wires = targetModule.getWires();
- for (int i = 0; (wires != null) && (i < wires.length); i++)
- {
- // If the wire is a module dependency, then flatten it to packages.
- if (wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- // Determine if required packages are re-exported.
- boolean reexport = false;
- R4Directive[] dirs = ((Requirement) wires[i].getRequirement()).getDirectives();
- for (int dirIdx = 0;
- !reexport && (dirs != null) && (dirIdx < dirs.length); dirIdx++)
- {
- if (dirs[dirIdx].getName().equals(Constants.VISIBILITY_DIRECTIVE)
- && dirs[dirIdx].getValue().equals(Constants.VISIBILITY_REEXPORT))
- {
- reexport = true;
- }
- }
-
- // Recursively calculate the required packages for the
- // wire's exporting module.
- Map requiredMap = calculateExportedAndReexportedPackagesResolved(wires[i].getExporter(), cycleMap);
-
- // Merge the wires exported and re-exported packages
- // into the complete set of required packages.
- for (Iterator reqIter = requiredMap.entrySet().iterator(); reqIter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) reqIter.next();
- String pkgName = (String) entry.getKey();
-
- // Merge the current set of required packages into
- // the overall complete set of required packages.
- // We calculate all the required packages, because
- // despite the fact that some packages will be required
- // "privately" and some will be required "reexport", any
- // re-exported package sources will ultimately need to
- // be combined with privately required package sources,
- // if the required packages overlap. This is one of the
- // bad things about require-bundle behavior, it does not
- // necessarily obey the visibility rules declared in the
- // dependency.
- ResolvedPackage rp = (ResolvedPackage) allRequiredMap.get(pkgName);
- if (rp != null)
- {
- // Create the union of all package sources.
- ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
- rp.merge(rpReq);
- }
- else
- {
- // Add package to required map.
- allRequiredMap.put(pkgName, entry.getValue());
- }
-
- // Keep track of all required packages to be re-exported.
- // All re-exported packages will need to be merged into the
- // target module's package map and become part of its overall
- // export signature.
- if (reexport)
- {
- reexportedPkgMap.put(pkgName, pkgName);
- }
- }
- }
- }
-
- // For the target module we have now calculated its entire set
- // of required packages and their associated package sources in
- // allRequiredMap and have calculated all packages to be re-exported
- // in reexportedPkgMap. Add all re-exported required packages to the
- // target module's package map since they will be part of its export
- // signature.
- for (Iterator iter = reexportedPkgMap.entrySet().iterator(); iter.hasNext(); )
- {
- String pkgName = (String) ((Map.Entry) iter.next()).getKey();
- pkgMap.put(pkgName, allRequiredMap.get(pkgName));
- }
-
- // Now loop through the target module's export package capabilities and
- // add the target module as a package source for any exported packages.
- ICapability[] caps = targetModule.getDefinition().getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
- {
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- String pkgName = (String)
- caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
- rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
- rp.m_sourceList.add(new PackageSource(targetModule, caps[i]));
- pkgMap.put(rp.m_name, rp);
- }
- }
-
- return pkgMap;
- }
-
- private Map calculateCandidateRequiredPackages(IModule module, PackageSource psTarget, Map candidatesMap)
- {
-//System.out.println("calculateCandidateRequiredPackages("+module+")");
- Map cycleMap = new HashMap();
- cycleMap.put(module, module);
- return calculateExportedAndReexportedPackages(psTarget, candidatesMap, cycleMap);
- }
-
- private void incrementCandidateConfiguration(List resolverList)
- throws ResolveException
- {
- for (int i = 0; i < resolverList.size(); i++)
- {
- List candSetList = (List) resolverList.get(i);
- for (int j = 0; j < candSetList.size(); j++)
- {
- CandidateSet cs = (CandidateSet) candSetList.get(j);
- // See if we can increment the candidate set, without overflowing
- // the candidate array bounds.
- if ((cs.m_idx + 1) < cs.m_candidates.length)
- {
- cs.m_idx++;
- return;
- }
- // If the index will overflow the candidate array bounds,
- // then set the index back to zero and try to increment
- // the next candidate.
- else
- {
- cs.m_idx = 0;
- }
- }
- }
- throw new ResolveException(
- "Unable to resolve due to constraint violation.", null, null);
- }
-
- private Map createWires(Map candidatesMap, IModule rootModule)
- {
- // Get a map of all modules and their resolved wires.
- Map resolvedModuleWireMap =
- populateWireMap(candidatesMap, rootModule, new HashMap());
- Iterator iter = resolvedModuleWireMap.entrySet().iterator();
- // Iterate over the map to mark the modules as resolved and
- // update our resolver data structures.
- while (iter.hasNext())
- {
- Map.Entry entry = (Map.Entry) iter.next();
- IModule module = (IModule) entry.getKey();
- IWire[] wires = (IWire[]) entry.getValue();
-
- // Set the module's resolved and wiring attribute.
- setResolved(module, true);
- // Only add wires attribute if some exist; export
- // only modules may not have wires.
- if (wires.length > 0)
- {
- ((ModuleImpl) module).setWires(wires);
-for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
-{
- m_logger.log(Logger.LOG_DEBUG, "WIRE: " + wires[wireIdx]);
-}
- }
-
- // At this point, we need to remove all of the resolved module's
- // capabilities from the "unresolved" package map and put them in
- // in the "resolved" package map, with the exception of any
- // package exports that are also imported. In that case we need
- // to make sure that the import actually points to the resolved
- // module and not another module. If it points to another module
- // then the capability should be ignored, since the framework
- // decided to honor the import and discard the export.
- ICapability[] caps = module.getDefinition().getCapabilities();
-
- // First remove all existing capabilities from the "unresolved" map.
- for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
- {
- if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- // Get package name.
- String pkgName = (String)
- caps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
- // Remove the module's capability for the package.
- m_unresolvedPkgIndexMap.put(
- pkgName,
- removeModuleFromArray(
- (IModule[]) m_unresolvedPkgIndexMap.get(pkgName),
- module));
- }
- }
-
- // Next create a copy of the module's capabilities so we can
- // null out any capabilities that should be ignored.
- ICapability[] capsCopy = (caps == null) ? null : new ICapability[caps.length];
- if (capsCopy != null)
- {
- System.arraycopy(caps, 0, capsCopy, 0, caps.length);
- }
- // Loop through the module's capabilities to determine which ones
- // can be ignored by seeing which ones satifies the wire requirements.
-// TODO: RB - Bug here because a requirement for a package need not overlap the
-// capability for that package and this assumes it does. This might
-// require us to introduce the notion of a substitutable capability.
- for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
- {
- // Loop through all wires to see if the current capability
- // satisfies any of the wire requirements.
- for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
- {
- // If the wire requirement is satisfied by the current capability,
- // then check to see if the wire is to the module itself. If it
- // is to another module, then null the current capability since
- // it was both providing and requiring the same capability and
- // the resolve process chose to import rather than provide that
- // capability, therefore we should ignore it.
- if (wires[wireIdx].getRequirement().isSatisfied(capsCopy[capIdx]))
- {
- if (!wires[wireIdx].getExporter().equals(module))
- {
- capsCopy[capIdx] = null;
- }
- break;
- }
- }
- }
-
- // Now loop through all capabilities and add them to the "resolved"
- // capability and package index maps, ignoring any that were nulled out.
- for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
- {
- if (capsCopy[capIdx] != null)
- {
- ICapability[] resolvedCaps = (ICapability[]) m_resolvedCapMap.get(module);
- resolvedCaps = addCapabilityToArray(resolvedCaps, capsCopy[capIdx]);
- m_resolvedCapMap.put(module, resolvedCaps);
-
- // If the capability is a package, then add the exporter module
- // of the wire to the "resolved" package index and remove it
- // from the "unresolved" package index.
- if (capsCopy[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- // Add to "resolved" package index.
- indexPackageCapability(
- m_resolvedPkgIndexMap,
- module,
- capsCopy[capIdx]);
- }
- }
- }
- }
-
-//System.out.println("UNRESOLVED INDEX:");
-//dumpPackageIndexMap(m_unresolvedPkgIndexMap);
-//System.out.println("RESOLVED INDEX:");
-//dumpPackageIndexMap(m_resolvedPkgIndexMap);
- return resolvedModuleWireMap;
- }
-
- private Map populateWireMap(Map candidatesMap, IModule importer, Map wireMap)
- {
- // If the module is already resolved or it is part of
- // a cycle, then just return the wire map.
- if (isResolved(importer) || (wireMap.get(importer) != null))
- {
- return wireMap;
- }
-
- List candSetList = (List) candidatesMap.get(importer);
- List moduleWires = new ArrayList();
- List packageWires = new ArrayList();
- IWire[] wires = new IWire[candSetList.size()];
-
- // Put the module in the wireMap with an empty wire array;
- // we do this early so we can use it to detect cycles.
- wireMap.put(importer, wires);
-
- // Loop through each candidate Set and create a wire
- // for the selected candidate for the associated import.
- for (int candSetIdx = 0; candSetIdx < candSetList.size(); candSetIdx++)
- {
- // Get the current candidate set.
- CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
-
- // Create a wire for the current candidate based on the type
- // of requirement it resolves.
- if (cs.m_requirement.getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- moduleWires.add(new R4WireModule(
- importer,
- cs.m_requirement,
- cs.m_candidates[cs.m_idx].m_module,
- cs.m_candidates[cs.m_idx].m_capability,
- calculateCandidateRequiredPackages(importer, cs.m_candidates[cs.m_idx], candidatesMap)));
- }
- else
- {
- // Add wire for imported package.
- packageWires.add(new R4Wire(
- importer,
- cs.m_requirement,
- cs.m_candidates[cs.m_idx].m_module,
- cs.m_candidates[cs.m_idx].m_capability));
- }
-
- // Create any necessary wires for the selected candidate module.
- wireMap = populateWireMap(
- candidatesMap, cs.m_candidates[cs.m_idx].m_module, wireMap);
- }
-
- packageWires.addAll(moduleWires);
- wireMap.put(importer, packageWires.toArray(wires));
-
- return wireMap;
- }
-
- //
- // Event handling methods for validation events.
- //
-
- /**
- * Adds a resolver listener to the search policy. Resolver
- * listeners are notified when a module is resolve and/or unresolved
- * by the search policy.
- * @param l the resolver listener to add.
- **/
- public void addResolverListener(ResolveListener l)
- {
- // Verify listener.
- if (l == null)
- {
- throw new IllegalArgumentException("Listener is null");
- }
-
- // Use the m_noListeners object as a lock.
- synchronized (m_emptyListeners)
- {
- // If we have no listeners, then just add the new listener.
- if (m_listeners == m_emptyListeners)
- {
- m_listeners = new ResolveListener[] { l };
- }
- // Otherwise, we need to do some array copying.
- // Notice, the old array is always valid, so if
- // the dispatch thread is in the middle of a dispatch,
- // then it has a reference to the old listener array
- // and is not affected by the new value.
- else
- {
- ResolveListener[] newList = new ResolveListener[m_listeners.length + 1];
- System.arraycopy(m_listeners, 0, newList, 0, m_listeners.length);
- newList[m_listeners.length] = l;
- m_listeners = newList;
- }
- }
- }
-
- /**
- * Removes a resolver listener to this search policy.
- * @param l the resolver listener to remove.
- **/
- public void removeResolverListener(ResolveListener l)
- {
- // Verify listener.
- if (l == null)
- {
- throw new IllegalArgumentException("Listener is null");
- }
-
- // Use the m_emptyListeners object as a lock.
- synchronized (m_emptyListeners)
- {
- // Try to find the instance in our list.
- int idx = -1;
- for (int i = 0; i < m_listeners.length; i++)
- {
- if (m_listeners[i].equals(l))
- {
- idx = i;
- break;
- }
- }
-
- // If we have the instance, then remove it.
- if (idx >= 0)
- {
- // If this is the last listener, then point to empty list.
- if (m_listeners.length == 1)
- {
- m_listeners = m_emptyListeners;
- }
- // Otherwise, we need to do some array copying.
- // Notice, the old array is always valid, so if
- // the dispatch thread is in the middle of a dispatch,
- // then it has a reference to the old listener array
- // and is not affected by the new value.
- else
- {
- ResolveListener[] newList = new ResolveListener[m_listeners.length - 1];
- System.arraycopy(m_listeners, 0, newList, 0, idx);
- if (idx < newList.length)
- {
- System.arraycopy(m_listeners, idx + 1, newList, idx,
- newList.length - idx);
- }
- m_listeners = newList;
- }
- }
- }
- }
-
- /**
- * Fires a validation event for the specified module.
- * @param module the module that was resolved.
- **/
- private void fireModuleResolved(IModule module)
- {
- // Event holder.
- ModuleEvent event = null;
-
- // Get a copy of the listener array, which is guaranteed
- // to not be null.
- ResolveListener[] listeners = m_listeners;
-
- // Loop through listeners and fire events.
- for (int i = 0; i < listeners.length; i++)
- {
- // Lazily create event.
- if (event == null)
- {
- event = new ModuleEvent(m_factory, module);
- }
- listeners[i].moduleResolved(event);
- }
- }
-
- /**
- * Fires an unresolved event for the specified module.
- * @param module the module that was unresolved.
- **/
- private void fireModuleUnresolved(IModule module)
- {
-// TODO: FRAMEWORK - Call this method where appropriate.
- // Event holder.
- ModuleEvent event = null;
-
- // Get a copy of the listener array, which is guaranteed
- // to not be null.
- ResolveListener[] listeners = m_listeners;
-
- // Loop through listeners and fire events.
- for (int i = 0; i < listeners.length; i++)
- {
- // Lazily create event.
- if (event == null)
- {
- event = new ModuleEvent(m_factory, module);
- }
- listeners[i].moduleUnresolved(event);
- }
- }
-
- //
- // ModuleListener callback methods.
- //
-
- public void moduleAdded(ModuleEvent event)
- {
- synchronized (m_factory)
- {
- // When a module is added, create an aggregated list of unresolved
- // exports to simplify later processing when resolving bundles.
- IModule module = event.getModule();
- ICapability[] caps = module.getDefinition().getCapabilities();
-
- // Add exports to unresolved package map.
- for (int i = 0; (caps != null) && (i < caps.length); i++)
- {
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- indexPackageCapability(m_unresolvedPkgIndexMap, module, caps[i]);
- }
- }
- }
- }
-
- public void moduleRemoved(ModuleEvent event)
- {
- // When a module is removed from the system, we need remove
- // its exports from the "resolved" and "unresolved" package maps,
- // remove the module's dependencies on fragments and exporters,
- // and remove the module from the module data map.
-
- // Synchronize on the module manager, since we don't want any
- // bundles to be installed or removed.
- synchronized (m_factory)
- {
- // Remove exports from package maps.
- ICapability[] caps = event.getModule().getDefinition().getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
- {
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- // Get package name.
- String pkgName = (String)
- caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
- // Remove from "unresolved" package map.
- IModule[] modules = (IModule[]) m_unresolvedPkgIndexMap.get(pkgName);
- if (modules != null)
- {
- modules = removeModuleFromArray(modules, event.getModule());
- m_unresolvedPkgIndexMap.put(pkgName, modules);
- }
-
- // Remove from "resolved" package map.
- modules = (IModule[]) m_resolvedPkgIndexMap.get(pkgName);
- if (modules != null)
- {
- modules = removeModuleFromArray(modules, event.getModule());
- m_resolvedPkgIndexMap.put(pkgName, modules);
- }
- }
- }
-
- // Set fragments to null, which will remove the module from all
- // of its dependent fragment modules.
- try
- {
- ((ModuleImpl) event.getModule()).attachFragments(null);
- }
- catch (Exception ex)
- {
- m_logger.log(Logger.LOG_ERROR, "Error detaching fragments.", ex);
- }
- // Set wires to null, which will remove the module from all
- // of its dependent modules.
- ((ModuleImpl) event.getModule()).setWires(null);
- // Remove the module from the "resolved" map.
-// TODO: RB - Maybe this can be merged with ModuleData.
- m_resolvedCapMap.remove(event.getModule());
- // Finally, remove module data.
- m_moduleDataMap.remove(event.getModule());
- }
- }
-
/**
* This is an experimental method that is likely to change or go
* away - so don't use it for now.
@@ -2818,6 +603,7 @@
* For now we assume that capabilities have been added only. We might need to
* enforce that at one point of time.
*/
+/* TODO: RESOLVER - We need to figure out what to do with this.
public void moduleRefreshed(ModuleEvent event)
{
synchronized (m_factory)
@@ -2849,422 +635,7 @@
}
}
}
-
- private void indexPackageCapability(Map map, IModule module, ICapability capability)
- {
- if (capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- String pkgName = (String)
- capability.getProperties().get(ICapability.PACKAGE_PROPERTY);
- IModule[] modules = (IModule[]) map.get(pkgName);
-
- // We want to add the module into the list of exporters
- // in sorted order (descending version and ascending bundle
- // identifier). Insert using a simple binary search algorithm.
- if (modules == null)
- {
- modules = new IModule[] { module };
- }
- else
- {
- Version version = (Version)
- capability.getProperties().get(ICapability.VERSION_PROPERTY);
- Version middleVersion = null;
- int top = 0, bottom = modules.length - 1, middle = 0;
- while (top <= bottom)
- {
- middle = (bottom - top) / 2 + top;
- middleVersion = (Version)
- getExportPackageCapability(
- modules[middle], pkgName)
- .getProperties()
- .get(ICapability.VERSION_PROPERTY);
- // Sort in reverse version order.
- int cmp = middleVersion.compareTo(version);
- if (cmp < 0)
- {
- bottom = middle - 1;
- }
- else if (cmp == 0)
- {
- // Sort further by ascending bundle ID.
- long middleId = Util.getBundleIdFromModuleId(modules[middle].getId());
- long exportId = Util.getBundleIdFromModuleId(module.getId());
- if (middleId < exportId)
- {
- top = middle + 1;
- }
- else
- {
- bottom = middle - 1;
- }
- }
- else
- {
- top = middle + 1;
- }
- }
-
- // Ignore duplicates.
- if ((top >= modules.length) || (modules[top] != module))
- {
- IModule[] newMods = new IModule[modules.length + 1];
- System.arraycopy(modules, 0, newMods, 0, top);
- System.arraycopy(modules, top, newMods, top + 1, modules.length - top);
- newMods[top] = module;
- modules = newMods;
- }
- }
-
- map.put(pkgName, modules);
- }
- }
-
- public static ICapability getExportPackageCapability(IModule m, String pkgName)
- {
- ICapability[] caps = m.getDefinition().getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
- {
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
- caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
- {
- return caps[i];
- }
- }
- return null;
- }
-
- //
- // Simple utility methods.
- //
-/*
- private static IModule[] addModuleToArray(IModule[] modules, IModule m)
- {
- // Verify that the module is not already in the array.
- for (int i = 0; (modules != null) && (i < modules.length); i++)
- {
- if (modules[i] == m)
- {
- return modules;
- }
- }
-
- if (modules != null)
- {
- IModule[] newModules = new IModule[modules.length + 1];
- System.arraycopy(modules, 0, newModules, 0, modules.length);
- newModules[modules.length] = m;
- modules = newModules;
- }
- else
- {
- modules = new IModule[] { m };
- }
-
- return modules;
- }
*/
- private static IModule[] removeModuleFromArray(IModule[] modules, IModule m)
- {
- if (modules == null)
- {
- return m_emptyModules;
- }
-
- int idx = -1;
- do
- {
- idx = -1;
- for (int i = 0; i < modules.length; i++)
- {
- if (modules[i] == m)
- {
- idx = i;
- break;
- }
- }
-
- if (idx >= 0)
- {
- // If this is the module, then point to empty list.
- if ((modules.length - 1) == 0)
- {
- modules = m_emptyModules;
- }
- // Otherwise, we need to do some array copying.
- else
- {
- IModule[] newModules = new IModule[modules.length - 1];
- System.arraycopy(modules, 0, newModules, 0, idx);
- if (idx < newModules.length)
- {
- System.arraycopy(
- modules, idx + 1, newModules, idx, newModules.length - idx);
- }
- modules = newModules;
- }
- }
- } while (idx >= 0);
- return modules;
- }
-
- private static PackageSource[] shrinkCandidateArray(PackageSource[] candidates)
- {
- if (candidates == null)
- {
- return m_emptySources;
- }
-
- // Move all non-null values to one end of the array.
- int lower = 0;
- for (int i = 0; i < candidates.length; i++)
- {
- if (candidates[i] != null)
- {
- candidates[lower++] = candidates[i];
- }
- }
-
- if (lower == 0)
- {
- return m_emptySources;
- }
-
- // Copy non-null values into a new array and return.
- PackageSource[] newCandidates= new PackageSource[lower];
- System.arraycopy(candidates, 0, newCandidates, 0, lower);
- return newCandidates;
- }
-
- private static ICapability[] addCapabilityToArray(ICapability[] caps, ICapability cap)
- {
- // Verify that the capability is not already in the array.
- for (int i = 0; (caps != null) && (i < caps.length); i++)
- {
- if (caps[i].equals(cap))
- {
- return caps;
- }
- }
-
- if (caps != null)
- {
- ICapability[] newCaps = new ICapability[caps.length + 1];
- System.arraycopy(caps, 0, newCaps, 0, caps.length);
- newCaps[caps.length] = cap;
- caps = newCaps;
- }
- else
- {
- caps = new ICapability[] { cap };
- }
-
- return caps;
- }
-
- //
- // Simple utility classes.
- //
-
- private static class ModuleData
- {
- public IModule m_module = null;
- public boolean m_resolved = false;
- public ModuleData(IModule module)
- {
- m_module = module;
- }
- }
-
- private class CandidateSet
- {
- public IModule m_module = null;
- public IRequirement m_requirement = null;
- public PackageSource[] m_candidates = null;
- public int m_idx = 0;
- public CandidateSet(IModule module, IRequirement requirement, PackageSource[] candidates)
- {
- m_module = module;
- m_requirement = requirement;
- m_candidates = candidates;
- }
- }
-
- /**
- * This utility class represents a source for a given package, where
- * the package is indicated by a particular module and the module's
- * capability associated with that package. This class also implements
- * <tt>Comparable</tt> so that two package sources can be compared based
- * on version and bundle identifiers.
- **/
- public class PackageSource implements Comparable
- {
- public IModule m_module = null;
- public ICapability m_capability = null;
-
- public PackageSource(IModule module, ICapability capability)
- {
- m_module = module;
- m_capability = capability;
- }
-
- public int compareTo(Object o)
- {
- PackageSource ps = (PackageSource) o;
-
- Version thisVersion = null;
- Version version = null;
- if (m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- thisVersion = ((Capability) m_capability).getPackageVersion();
- version = ((Capability) ps.m_capability).getPackageVersion();
- }
- else if (m_capability.getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- thisVersion = (Version)
- m_capability.getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
- version = (Version)
- ps.m_capability.getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
- }
-
- if ((thisVersion != null) && (version != null))
- {
- // Sort in reverse version order.
- int cmp = thisVersion.compareTo(version);
- if (cmp < 0)
- {
- return 1;
- }
- else if (cmp > 0)
- {
- return -1;
- }
- else
- {
- // Sort further by ascending bundle ID.
- long thisId = Util.getBundleIdFromModuleId(m_module.getId());
- long id = Util.getBundleIdFromModuleId(ps.m_module.getId());
- if (thisId < id)
- {
- return -1;
- }
- else if (thisId > id)
- {
- return 1;
- }
- return 0;
- }
- }
- else
- {
- return -1;
- }
- }
-
- public int hashCode()
- {
- final int PRIME = 31;
- int result = 1;
- result = PRIME * result + ((m_capability == null) ? 0 : m_capability.hashCode());
- result = PRIME * result + ((m_module == null) ? 0 : m_module.hashCode());
- return result;
- }
-
- public boolean equals(Object o)
- {
- if (this == o)
- {
- return true;
- }
- if (o == null)
- {
- return false;
- }
- if (getClass() != o.getClass())
- {
- return false;
- }
- PackageSource ps = (PackageSource) o;
- return (m_module.equals(ps.m_module) && (m_capability == ps.m_capability));
- }
- }
-
- /**
- * This utility class is a resolved package, which is comprised of a
- * set of <tt>PackageSource</tt>s that is calculated by the resolver
- * algorithm. A given resolved package may have a single package source,
- * as is the case with imported packages, or it may have multiple
- * package sources, as is the case with required bundles.
- **/
- protected class ResolvedPackage
- {
- public String m_name = null;
- public List m_sourceList = new ArrayList();
-
- public ResolvedPackage(String name)
- {
- m_name = name;
- }
-
- public boolean isSubset(ResolvedPackage rp)
- {
- if (m_sourceList.size() > rp.m_sourceList.size())
- {
- return false;
- }
- else if (!m_name.equals(rp.m_name))
- {
- return false;
- }
-
- // Determine if the target set of source modules is a subset.
- return rp.m_sourceList.containsAll(m_sourceList);
- }
-
- public Object clone()
- {
- ResolvedPackage rp = new ResolvedPackage(m_name);
- rp.m_sourceList.addAll(m_sourceList);
- return rp;
- }
-
- public void merge(ResolvedPackage rp)
- {
- // Merge required packages, avoiding duplicate
- // package sources and maintaining ordering.
- for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
- {
- if (!m_sourceList.contains(rp.m_sourceList.get(srcIdx)))
- {
- m_sourceList.add(rp.m_sourceList.get(srcIdx));
- }
- }
- }
-
- public String toString()
- {
- return toString("", new StringBuffer()).toString();
- }
-
- public StringBuffer toString(String padding, StringBuffer sb)
- {
- sb.append(padding);
- sb.append(m_name);
- sb.append(" from [");
- for (int i = 0; i < m_sourceList.size(); i++)
- {
- PackageSource ps = (PackageSource) m_sourceList.get(i);
- sb.append(ps.m_module);
- if ((i + 1) < m_sourceList.size())
- {
- sb.append(", ");
- }
- }
- sb.append("]");
- return sb;
- }
- }
-
//
// Diagnostics.
//
@@ -3372,6 +743,7 @@
}
*/
// Next, check to see if the package is dynamically imported by the module.
+/* TODO: RESOLVER: Need to fix this too.
IRequirement[] dynamics = module.getDefinition().getDynamicRequirements();
for (int dynIdx = 0; dynIdx < dynamics.length; dynIdx++)
{
@@ -3436,6 +808,7 @@
return sb.toString();
}
}
+*/
IRequirement pkgReq = null;
try
{
@@ -3445,8 +818,8 @@
{
// This should never happen.
}
- PackageSource[] exporters = getResolvedCandidates(pkgReq);
- exporters = (exporters.length == 0) ? getUnresolvedCandidates(pkgReq) : exporters;
+ PackageSource[] exporters = m_resolver.getResolvedCandidates(pkgReq);
+ exporters = (exporters.length == 0) ? m_resolver.getUnresolvedCandidates(pkgReq) : exporters;
if (exporters.length > 0)
{
boolean classpath = false;
@@ -3548,4 +921,4 @@
return sb.toString();
}
-}
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java
index b60967f..31c606c 100644
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java
@@ -21,8 +21,8 @@
import java.net.URL;
import java.util.*;
-import org.apache.felix.framework.searchpolicy.R4SearchPolicyCore.ResolvedPackage;
-import org.apache.felix.framework.searchpolicy.R4SearchPolicyCore.PackageSource;
+import org.apache.felix.framework.searchpolicy.PackageSource;
+import org.apache.felix.framework.searchpolicy.ResolvedPackage;
import org.apache.felix.framework.util.CompoundEnumeration;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.util.manifestparser.Capability;
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java
new file mode 100644
index 0000000..3421198
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java
@@ -0,0 +1,98 @@
+/*
+ * 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.searchpolicy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This utility class is a resolved package, which is comprised of a
+ * set of <tt>PackageSource</tt>s that is calculated by the resolver
+ * algorithm. A given resolved package may have a single package source,
+ * as is the case with imported packages, or it may have multiple
+ * package sources, as is the case with required bundles.
+ */
+class ResolvedPackage
+{
+ public String m_name = null;
+ public List m_sourceList = new ArrayList();
+
+ public ResolvedPackage(String name)
+ {
+ super();
+ m_name = name;
+ }
+
+ public boolean isSubset(ResolvedPackage rp)
+ {
+ if (m_sourceList.size() > rp.m_sourceList.size())
+ {
+ return false;
+ }
+ else if (!m_name.equals(rp.m_name))
+ {
+ return false;
+ }
+ // Determine if the target set of source modules is a subset.
+ return rp.m_sourceList.containsAll(m_sourceList);
+ }
+
+ public Object clone()
+ {
+ ResolvedPackage rp = new ResolvedPackage(m_name);
+ rp.m_sourceList.addAll(m_sourceList);
+ return rp;
+ }
+
+ public void merge(ResolvedPackage rp)
+ {
+ // Merge required packages, avoiding duplicate
+ // package sources and maintaining ordering.
+ for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
+ {
+ if (!m_sourceList.contains(rp.m_sourceList.get(srcIdx)))
+ {
+ m_sourceList.add(rp.m_sourceList.get(srcIdx));
+ }
+ }
+ }
+
+ public String toString()
+ {
+ return toString("", new StringBuffer()).toString();
+ }
+
+ public StringBuffer toString(String padding, StringBuffer sb)
+ {
+ sb.append(padding);
+ sb.append(m_name);
+ sb.append(" from [");
+ for (int i = 0; i < m_sourceList.size(); i++)
+ {
+ PackageSource ps = (PackageSource) m_sourceList.get(i);
+ sb.append(ps.m_module);
+ if ((i + 1) < m_sourceList.size())
+ {
+ sb.append(", ");
+ }
+ }
+ sb.append("]");
+ return sb;
+ }
+}
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java
new file mode 100644
index 0000000..c85efcb
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java
@@ -0,0 +1,1552 @@
+/*
+ * 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.searchpolicy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.Capability;
+import org.apache.felix.framework.util.manifestparser.R4Attribute;
+import org.apache.felix.framework.util.manifestparser.R4Directive;
+import org.apache.felix.framework.util.manifestparser.Requirement;
+import org.apache.felix.moduleloader.ICapability;
+import org.apache.felix.moduleloader.IModule;
+import org.apache.felix.moduleloader.IRequirement;
+import org.apache.felix.moduleloader.IWire;
+import org.osgi.framework.Constants;
+
+public class Resolver
+{
+ private final Logger m_logger;
+
+ // Reusable empty array.
+ private static final PackageSource[] m_emptySources = new PackageSource[0];
+
+ public Resolver(Logger logger)
+ {
+ m_logger = logger;
+ }
+
+ // Returns a map of resolved bundles where the key is the module
+ // and the value is an array of wires.
+ // TODO: RESOLVER - The caller must ensure this is not called currently;
+ // this may not be important if no state is shared.
+ public Result resolve(ResolverState state, IModule rootModule) throws ResolveException
+ {
+ // This map will be used to hold the final wires for all
+ // resolved modules, which can then be used to fire resolved
+ // events outside of the synchronized block.
+ Map fragmentMap = null;
+
+ // If the module is already resolved, then we can just return.
+ if (state.isResolved(rootModule))
+ {
+ return null;
+ }
+
+ // The root module is either a host or a fragment. If it is a host,
+ // then we want to go ahead and resolve it. If it is a fragment, then
+ // we want to select a host and resolve the host instead.
+ IModule targetFragment = null;
+// TODO: FRAGMENT - Currently we just make a single selection of the available
+// fragments or hosts and try to resolve. In case of failure, we do not
+// backtrack. We will likely want to add backtracking.
+ if (Util.isFragment(rootModule))
+ {
+ targetFragment = rootModule;
+ List hostList = state.getPotentialHosts(targetFragment);
+ if (hostList.size() == 0)
+ {
+ throw new ResolveException("Unable to find host for fragment.", targetFragment, null);
+ }
+ rootModule = (IModule) hostList.get(0);
+ }
+
+ // Get the available fragments for the host.
+ fragmentMap = state.getPotentialFragments(rootModule);
+
+ // If the resolve was for a specific fragment, then
+ // eliminate all other potential candidate fragments
+ // of the same symbolic name.
+ if (targetFragment != null)
+ {
+ fragmentMap.put(
+ state.getBundleSymbolicName(targetFragment),
+ new IModule[] { targetFragment });
+ }
+
+ // This variable maps an unresolved module to a list of candidate
+ // sets, where there is one candidate set for each requirement that
+ // must be resolved. A candidate set contains the potential canidates
+ // available to resolve the requirement and the currently selected
+ // candidate index.
+ Map candidatesMap = new HashMap();
+
+ // The first step is to populate the candidates map. This
+ // will use the target module to populate the candidates map
+ // with all potential modules that need to be resolved as a
+ // result of resolving the target module. The key of the
+ // map is a potential module to be resolved and the value is
+ // a list of candidate sets, one for each of the module's
+ // requirements, where each candidate set contains the potential
+ // candidates for resolving the requirement. Not all modules in
+ // this map will be resolved, only the target module and
+ // any candidates selected to resolve its requirements and the
+ // transitive requirements this implies.
+ populateCandidatesMap(state, candidatesMap, rootModule);
+
+ // The next step is to use the candidates map to determine if
+ // the class space for the root module is consistent. This
+ // is an iterative process that transitively walks the "uses"
+ // relationships of all packages visible from the root module
+ // checking for conflicts. If a conflict is found, it "increments"
+ // the configuration of currently selected potential candidates
+ // and tests them again. If this method returns, then it has found
+ // a consistent set of candidates; otherwise, a resolve exception
+ // is thrown if it exhausts all possible combinations and could
+ // not find a consistent class space.
+ findConsistentClassSpace(state, candidatesMap, rootModule);
+
+ // The final step is to create the wires for the root module and
+ // transitively all modules that are to be resolved from the
+ // selected candidates for resolving the root module's imports.
+ // When this call returns, each module's wiring and resolved
+ // attributes are set. The resulting wiring map is used below
+ // to fire resolved events outside of the synchronized block.
+ // The resolved module wire map maps a module to its array of
+ // wires.
+ // Get a map of all modules and their resolved wires.
+ Map resolvedModuleWireMap =
+ populateWireMap(state, candidatesMap, rootModule, new HashMap());
+ return new Result(rootModule, fragmentMap, resolvedModuleWireMap);
+ }
+
+ // TODO: RESOLVER - Fix this return type.
+ // Return candidate wire in result[0] and wire map in result[1]
+ public Object[] resolveDynamicImport(ResolverState state, IModule importer, String pkgName)
+ throws ResolveException
+ {
+ PackageSource candidate = null;
+ Map resolvedModuleWireMap = null;
+
+ // We can only search dynamic imports if the bundle
+ // doesn't import, export, nor require the package in
+ // question. Check these conditions first.
+ if (isDynamicImportAllowed(importer, pkgName))
+ {
+ // 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.
+ IRequirement[] dynamics = importer.getDefinition().getDynamicRequirements();
+ for (int dynIdx = 0; (dynamics != null) && (dynIdx < dynamics.length); dynIdx++)
+ {
+ IRequirement target =
+ createDynamicRequirement(dynamics[dynIdx], pkgName);
+ if (target != null)
+ {
+ // See if there is a candidate exporter that satisfies the
+ // constrained dynamic requirement.
+ try
+ {
+ // Get "resolved" and "unresolved" candidates and put
+ // the "resolved" candidates first.
+ PackageSource[] resolved = state.getResolvedCandidates(target);
+ PackageSource[] unresolved = state.getUnresolvedCandidates(target);
+ PackageSource[] candidates = new PackageSource[resolved.length + unresolved.length];
+ System.arraycopy(resolved, 0, candidates, 0, resolved.length);
+ System.arraycopy(unresolved, 0, candidates, resolved.length, unresolved.length);
+
+ // Take the first candidate that can resolve.
+ for (int candIdx = 0;
+ (candidate == null) && (candIdx < candidates.length);
+ candIdx++)
+ {
+ try
+ {
+ // If a map is returned, then the candidate resolved
+ // consistently with the importer.
+ resolvedModuleWireMap =
+ resolveDynamicImportCandidate(
+ state, candidates[candIdx].m_module, importer);
+ if (resolvedModuleWireMap != null)
+ {
+ candidate = candidates[candIdx];
+ }
+ }
+ catch (ResolveException ex)
+ {
+ // Ignore candidates that cannot resolve.
+ }
+ }
+
+ if (candidate != null)
+ {
+ // Create the wire and add it to the module.
+ Object[] result = new Object[2];
+ result[0] = new R4Wire(
+ importer, dynamics[dynIdx], candidate.m_module, candidate.m_capability);
+ result[1] = resolvedModuleWireMap;
+ return result;
+ }
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(Logger.LOG_ERROR, "Unable to dynamically import package.", ex);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean isDynamicImportAllowed(IModule importer, String pkgName)
+ {
+ // If any of the module exports this package, then we cannot
+ // attempt to dynamically import it.
+ ICapability[] caps = importer.getDefinition().getCapabilities();
+ for (int i = 0; (caps != null) && (i < caps.length); i++)
+ {
+ if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
+ && caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
+ {
+ return false;
+ }
+ }
+ // If any of our wires have this package, then we cannot
+ // attempt to dynamically import it.
+ IWire[] wires = importer.getWires();
+ for (int i = 0; (wires != null) && (i < wires.length); i++)
+ {
+ if (wires[i].hasPackage(pkgName))
+ {
+ return false;
+ }
+ }
+ // Ok to attempt to dynamically import the package.
+ return true;
+ }
+
+ private static IRequirement createDynamicRequirement(IRequirement dynReq, String pkgName)
+ {
+ IRequirement req = null;
+
+ // First check to see if the dynamic requirement matches the
+ // package name; this means we have to do wildcard matching.
+ String dynPkgName = ((Requirement) dynReq).getPackageName();
+ boolean wildcard = (dynPkgName.lastIndexOf(".*") >= 0);
+ dynPkgName = (wildcard)
+ ? dynPkgName.substring(0, dynPkgName.length() - 2) : dynPkgName;
+ // If the dynamic requirement matches the package name, then
+ // create a new requirement for the specific package.
+ if (dynPkgName.equals("*") ||
+ pkgName.equals(dynPkgName) ||
+ (wildcard && pkgName.startsWith(dynPkgName + ".")))
+ {
+ // Create a new requirement based on the dynamic requirement,
+ // but substitute the precise package name for which we are
+ // looking, because it is not possible to use the potentially
+ // wildcarded version in the dynamic requirement.
+ R4Directive[] dirs = ((Requirement) dynReq).getDirectives();
+ R4Attribute[] attrs = ((Requirement) dynReq).getAttributes();
+ R4Attribute[] newAttrs = new R4Attribute[attrs.length];
+ System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
+ for (int attrIdx = 0; attrIdx < newAttrs.length; attrIdx++)
+ {
+ if (newAttrs[attrIdx].getName().equals(ICapability.PACKAGE_PROPERTY))
+ {
+ newAttrs[attrIdx] = new R4Attribute(
+ ICapability.PACKAGE_PROPERTY, pkgName, false);
+ break;
+ }
+ }
+ req = new Requirement(ICapability.PACKAGE_NAMESPACE, dirs, newAttrs);
+ }
+
+ return req;
+ }
+
+ private Map resolveDynamicImportCandidate(
+ ResolverState state, IModule provider, IModule importer)
+ throws ResolveException
+ {
+ // If the provider of the dynamically imported package is not
+ // resolved, then we need to calculate the candidates to resolve
+ // it and see if there is a consistent class space for the
+ // provider. If there is no consistent class space, then a resolve
+ // exception is thrown.
+ Map candidatesMap = new HashMap();
+ if (!state.isResolved(provider))
+ {
+ populateCandidatesMap(state, candidatesMap, provider);
+ findConsistentClassSpace(state, candidatesMap, provider);
+ }
+
+ // If the provider can be successfully resolved, then verify that
+ // its class space is consistent with the existing class space of the
+ // module that instigated the dynamic import.
+ Map moduleMap = new HashMap();
+ Map importerPkgMap = getModulePackages(moduleMap, importer, candidatesMap);
+
+ // Now we need to calculate the "uses" constraints of every package
+ // accessible to the provider module based on its current candidates.
+ Map usesMap = calculateUsesConstraints(provider, moduleMap, candidatesMap);
+
+ // Verify that none of the provider's implied "uses" constraints
+ // in the uses map conflict with anything in the importing module's
+ // package map.
+ for (Iterator iter = usesMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+
+ // For the given "used" package, get that package from the
+ // importing module's package map, if present.
+ ResolvedPackage rp = (ResolvedPackage) importerPkgMap.get(entry.getKey());
+
+ // If the "used" package is also visible to the importing
+ // module, make sure there is no conflicts in the implied
+ // "uses" constraints.
+ if (rp != null)
+ {
+ // Clone the resolve package so we can modify it.
+ rp = (ResolvedPackage) rp.clone();
+
+ // Loop through all implied "uses" constraints for the current
+ // "used" package and verify that all package sources are
+ // compatible with the package source of the importing module's
+ // package map.
+ List constraintList = (List) entry.getValue();
+ for (int constIdx = 0; constIdx < constraintList.size(); constIdx++)
+ {
+ // Get a specific "uses" constraint for the current "used"
+ // package.
+ ResolvedPackage rpUses = (ResolvedPackage) constraintList.get(constIdx);
+ // Determine if the implied "uses" constraint is compatible with
+ // the improting module's package sources for the given "used"
+ // package. They are compatible if one is the subset of the other.
+ // Retain the union of the two sets if they are compatible.
+ if (rpUses.isSubset(rp))
+ {
+ // Do nothing because we already have the superset.
+ }
+ else if (rp.isSubset(rpUses))
+ {
+ // Keep the superset, i.e., the union.
+ rp.m_sourceList.clear();
+ rp.m_sourceList.addAll(rpUses.m_sourceList);
+ }
+ else
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "Constraint violation for " + importer
+ + " detected; module can see "
+ + rp + " and " + rpUses);
+ return null;
+ }
+ }
+ }
+ }
+
+ return populateWireMap(state, candidatesMap, provider, new HashMap());
+ }
+
+ private static void populateCandidatesMap(
+ ResolverState state, Map candidatesMap, IModule module)
+ throws ResolveException
+ {
+ // Detect cycles.
+ if (candidatesMap.get(module) != null)
+ {
+ return;
+ }
+
+ // List to hold the resolving candidate sets for the module's
+ // requirements.
+ List candSetList = new ArrayList();
+
+ // Even though the candidate set list is currently empty, we
+ // record it in the candidates map early so we can use it to
+ // detect cycles.
+ candidatesMap.put(module, candSetList);
+
+ // Loop through each requirement and calculate its resolving
+ // set of candidates.
+ IRequirement[] reqs = module.getDefinition().getRequirements();
+ for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
+ {
+ // Get the candidates from the "resolved" and "unresolved"
+ // package maps. The "resolved" candidates have higher priority
+ // than "unresolved" ones, so put the "resolved" candidates
+ // at the front of the list of candidates.
+ PackageSource[] resolved = state.getResolvedCandidates(reqs[reqIdx]);
+ PackageSource[] unresolved = state.getUnresolvedCandidates(reqs[reqIdx]);
+ PackageSource[] candidates = new PackageSource[resolved.length + unresolved.length];
+ System.arraycopy(resolved, 0, candidates, 0, resolved.length);
+ System.arraycopy(unresolved, 0, candidates, resolved.length, unresolved.length);
+
+ // If we have candidates, then we need to recursively populate
+ // the resolver map with each of them.
+ ResolveException rethrow = null;
+ if (candidates.length > 0)
+ {
+ for (int candIdx = 0; candIdx < candidates.length; candIdx++)
+ {
+ try
+ {
+ // Only populate the resolver map with modules that
+ // are not already resolved.
+ if (!state.isResolved(candidates[candIdx].m_module))
+ {
+ populateCandidatesMap(state, candidatesMap, candidates[candIdx].m_module);
+ }
+ }
+ catch (ResolveException ex)
+ {
+ // If we received a resolve exception, then the
+ // current candidate is not resolvable for some
+ // reason and should be removed from the list of
+ // candidates. For now, just null it.
+ candidates[candIdx] = null;
+ rethrow = ex;
+ }
+ }
+
+ // Remove any nulled candidates to create the final list
+ // of available candidates.
+ candidates = shrinkCandidateArray(candidates);
+ }
+
+ // If no candidates exist at this point, then throw a
+ // resolve exception unless the import is optional.
+ if ((candidates.length == 0) && !reqs[reqIdx].isOptional())
+ {
+ // If we have received an exception while trying to populate
+ // the resolver map, rethrow that exception since it might
+ // be useful. NOTE: This is not necessarily the "only"
+ // correct exception, since it is possible that multiple
+ // candidates were not resolvable, but it is better than
+ // nothing.
+ if (rethrow != null)
+ {
+ throw rethrow;
+ }
+ else
+ {
+ throw new ResolveException(
+ "Unable to resolve.", module, reqs[reqIdx]);
+ }
+ }
+ else if (candidates.length > 0)
+ {
+ candSetList.add(
+ new CandidateSet(module, reqs[reqIdx], candidates));
+ }
+ }
+ }
+
+ private void findConsistentClassSpace(
+ ResolverState state, Map candidatesMap, IModule rootModule)
+ throws ResolveException
+ {
+ List candidatesList = null;
+
+ // The reusable module map maps a module to a map of
+ // resolved packages that are accessible by the given
+ // module. The set of resolved packages is calculated
+ // from the current candidates of the candidates map
+ // and the module's metadata.
+ Map moduleMap = new HashMap();
+
+ // Reusable map used to test for cycles.
+ Map cycleMap = new HashMap();
+
+ // Test the current potential candidates to determine if they
+ // are consistent. Keep looping until we find a consistent
+ // set or an exception is thrown.
+ while (!isSingletonConsistent(state, rootModule, moduleMap, candidatesMap) ||
+ !isClassSpaceConsistent(rootModule, moduleMap, cycleMap, candidatesMap))
+ {
+ // The incrementCandidateConfiguration() method requires
+ // ordered access to the candidates map, so we will create
+ // a reusable list once right here.
+ if (candidatesList == null)
+ {
+ candidatesList = new ArrayList();
+ for (Iterator iter = candidatesMap.entrySet().iterator();
+ iter.hasNext(); )
+ {
+ candidatesList.add(((Map.Entry) iter.next()).getValue());
+ }
+ }
+
+ // Increment the candidate configuration so we can test again.
+ incrementCandidateConfiguration(candidatesList);
+
+ // Clear the module map.
+ moduleMap.clear();
+
+ // Clear the cycle map.
+ cycleMap.clear();
+ }
+ }
+
+ /**
+ * This methd checks to see if the target module and any of the candidate
+ * modules to resolve its dependencies violate any singleton constraints.
+ * Actually, it just creates a map of resolved singleton modules and then
+ * delegates all checking to another recursive method.
+ *
+ * @param targetModule the module that is the root of the tree of modules to check.
+ * @param moduleMap a map to cache the package space of each module.
+ * @param candidatesMap a map containing the all candidates to resolve all
+ * dependencies for all modules.
+ * @return <tt>true</tt> if all candidates are consistent with respect to singletons,
+ * <tt>false</tt> otherwise.
+ **/
+ private boolean isSingletonConsistent(
+ ResolverState state, IModule targetModule, Map moduleMap, Map candidatesMap)
+ {
+ // Create a map of all resolved singleton modules.
+ Map singletonMap = new HashMap();
+ IModule[] modules = state.getModules();
+ for (int i = 0; (modules != null) && (i < modules.length); i++)
+ {
+ if (state.isResolved(modules[i]) && isSingleton(modules[i]))
+ {
+ String symName = state.getBundleSymbolicName(modules[i]);
+ singletonMap.put(symName, symName);
+ }
+ }
+
+ return areCandidatesSingletonConsistent(
+ state, targetModule, singletonMap, moduleMap, new HashMap(), candidatesMap);
+ }
+
+ /**
+ * This method recursive checks the target module and all of its transitive
+ * dependency modules to verify that they do not violate a singleton constraint.
+ * If the target module is a singleton, then it checks that againts existing
+ * singletons. Then it checks all current unresolved candidates recursively.
+ *
+ * @param targetModule the module that is the root of the tree of modules to check.
+ * @param singletonMap the current map of singleton symbolic names.
+ * @param moduleMap a map to cache the package space of each module.
+ * @param cycleMap a map to detect cycles.
+ * @param candidatesMap a map containing the all candidates to resolve all
+ * dependencies for all modules.
+ * @return <tt>true</tt> if all candidates are consistent with respect to singletons,
+ * <tt>false</tt> otherwise.
+ **/
+ private boolean areCandidatesSingletonConsistent(
+ ResolverState state, IModule targetModule,
+ Map singletonMap, Map moduleMap, Map cycleMap, Map candidatesMap)
+ {
+ // If we are in a cycle, then assume true for now.
+ if (cycleMap.get(targetModule) != null)
+ {
+ return true;
+ }
+
+ // Record the target module in the cycle map.
+ cycleMap.put(targetModule, targetModule);
+
+ // Check to see if the targetModule violates a singleton.
+ // If not and it is a singleton, then add it to the singleton
+ // map since it will constrain other singletons.
+ String symName = state.getBundleSymbolicName(targetModule);
+ boolean isSingleton = isSingleton(targetModule);
+ if (isSingleton && singletonMap.containsKey(symName))
+ {
+ return false;
+ }
+ else if (isSingleton)
+ {
+ singletonMap.put(symName, symName);
+ }
+
+ // Get the package space of the target module.
+ Map pkgMap = null;
+ try
+ {
+ pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
+ }
+ catch (ResolveException ex)
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "Constraint violation for " + targetModule + " detected.",
+ ex);
+ return false;
+ }
+
+ // Loop through all of the target module's accessible packages and
+ // verify that all package sources are consistent.
+ for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ // Get the resolved package, which contains the set of all
+ // package sources for the given package.
+ ResolvedPackage rp = (ResolvedPackage) entry.getValue();
+ // Loop through each package source and test if it is consistent.
+ for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
+ {
+ // If the module for this package source is not resolved, then
+ // we have to see if resolving it would violate a singleton
+ // constraint.
+ PackageSource ps = (PackageSource) rp.m_sourceList.get(srcIdx);
+ if (!state.isResolved(ps.m_module))
+ {
+ return areCandidatesSingletonConsistent(state, ps.m_module, singletonMap, moduleMap, cycleMap, candidatesMap);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if the specified module is a singleton
+ * (i.e., directive singleton:=true in Bundle-SymbolicName).
+ *
+ * @param module the module to check for singleton status.
+ * @return true if the module is a singleton, false otherwise.
+ **/
+ private static boolean isSingleton(IModule module)
+ {
+ final ICapability[] modCaps = Util.getCapabilityByNamespace(
+ module, Capability.MODULE_NAMESPACE);
+ if (modCaps == null || modCaps.length == 0)
+ {
+ // this should never happen?
+ return false;
+ }
+ final R4Directive[] dirs = ((Capability) modCaps[0]).getDirectives();
+ for (int dirIdx = 0; (dirs != null) && (dirIdx < dirs.length); dirIdx++)
+ {
+ if (dirs[dirIdx].getName().equalsIgnoreCase(Constants.SINGLETON_DIRECTIVE)
+ && Boolean.valueOf(dirs[dirIdx].getValue()).booleanValue())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isClassSpaceConsistent(
+ IModule targetModule, Map moduleMap, Map cycleMap, Map candidatesMap)
+ {
+//System.out.println("isClassSpaceConsistent("+targetModule+")");
+ // If we are in a cycle, then assume true for now.
+ if (cycleMap.get(targetModule) != null)
+ {
+ return true;
+ }
+
+ // Record the target module in the cycle map.
+ cycleMap.put(targetModule, targetModule);
+
+ // Get the package map for the target module, which is a
+ // map of all packages accessible to the module and their
+ // associated package sources.
+ Map pkgMap = null;
+ try
+ {
+ pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
+ }
+ catch (ResolveException ex)
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "Constraint violation for " + targetModule + " detected.",
+ ex);
+ return false;
+ }
+
+ // Loop through all of the target module's accessible packages and
+ // verify that all package sources are consistent.
+ for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ // Get the resolved package, which contains the set of all
+ // package sources for the given package.
+ ResolvedPackage rp = (ResolvedPackage) entry.getValue();
+ // Loop through each package source and test if it is consistent.
+ for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
+ {
+ PackageSource ps = (PackageSource) rp.m_sourceList.get(srcIdx);
+ if (!isClassSpaceConsistent(ps.m_module, moduleMap, cycleMap, candidatesMap))
+ {
+ return false;
+ }
+ }
+ }
+
+ // Now we need to calculate the "uses" constraints of every package
+ // accessible to the target module based on the current candidates.
+ Map usesMap = null;
+ try
+ {
+ usesMap = calculateUsesConstraints(targetModule, moduleMap, candidatesMap);
+ }
+ catch (ResolveException ex)
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "Constraint violation for " + targetModule + " detected.",
+ ex);
+ return false;
+ }
+
+ // Verify that none of the implied "uses" constraints in the uses map
+ // conflict with anything in the target module's package map.
+ for (Iterator iter = usesMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+
+ // For the given "used" package, get that package from the
+ // target module's package map, if present.
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
+
+ // If the "used" package is also visible to the target module,
+ // make sure there is no conflicts in the implied "uses"
+ // constraints.
+ if (rp != null)
+ {
+ // Clone the resolve package so we can modify it.
+ rp = (ResolvedPackage) rp.clone();
+
+ // Loop through all implied "uses" constraints for the current
+ // "used" package and verify that all package sources are
+ // compatible with the package source of the root module's
+ // package map.
+ List constraintList = (List) entry.getValue();
+ for (int constIdx = 0; constIdx < constraintList.size(); constIdx++)
+ {
+ // Get a specific "uses" constraint for the current "used"
+ // package.
+ ResolvedPackage rpUses = (ResolvedPackage) constraintList.get(constIdx);
+ // Determine if the implied "uses" constraint is compatible with
+ // the target module's package sources for the given "used"
+ // package. They are compatible if one is the subset of the other.
+ // Retain the union of the two sets if they are compatible.
+ if (rpUses.isSubset(rp))
+ {
+ // Do nothing because we already have the superset.
+ }
+ else if (rp.isSubset(rpUses))
+ {
+ // Keep the superset, i.e., the union.
+ rp.m_sourceList.clear();
+ rp.m_sourceList.addAll(rpUses.m_sourceList);
+ }
+ else
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "Constraint violation for " + targetModule
+ + " detected; module can see "
+ + rp + " and " + rpUses);
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private static Map calculateUsesConstraints(
+ IModule targetModule, Map moduleMap, Map candidatesMap)
+ throws ResolveException
+ {
+//System.out.println("calculateUsesConstraints("+targetModule+")");
+ // Map to store calculated uses constraints. This maps a
+ // package name to a list of resolved packages, where each
+ // resolved package represents a constraint on anyone
+ // importing the given package name. This map is returned
+ // by this method.
+ Map usesMap = new HashMap();
+
+ // Re-usable map to detect cycles.
+ Map cycleMap = new HashMap();
+
+ // Get all packages accessible by the target module.
+ Map pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
+
+ // Each package accessible from the target module is potentially
+ // comprised of one or more modules, called package sources. The
+ // "uses" constraints implied by all package sources must be
+ // calculated and combined to determine the complete set of implied
+ // "uses" constraints for each package accessible by the target module.
+ for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ ResolvedPackage rp = (ResolvedPackage) entry.getValue();
+ for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
+ {
+ usesMap = calculateUsesConstraints(
+ (PackageSource) rp.m_sourceList.get(srcIdx),
+ moduleMap, usesMap, cycleMap, candidatesMap);
+ }
+ }
+ return usesMap;
+ }
+
+ private static Map calculateUsesConstraints(
+ PackageSource psTarget, Map moduleMap, Map usesMap,
+ Map cycleMap, Map candidatesMap)
+ throws ResolveException
+ {
+//System.out.println("calculateUsesConstraints2("+psTarget.m_module+")");
+ // If we are in a cycle, then return for now.
+ if (cycleMap.get(psTarget) != null)
+ {
+ return usesMap;
+ }
+
+ // Record the target package source in the cycle map.
+ cycleMap.put(psTarget, psTarget);
+
+ // Get all packages accessible from the module of the
+ // target package source.
+ Map pkgMap = getModulePackages(moduleMap, psTarget.m_module, candidatesMap);
+
+ // Get capability (i.e., package) of the target package source.
+ Capability cap = (Capability) psTarget.m_capability;
+
+ // Loop through all "used" packages of the capability.
+ for (int i = 0; i < cap.getUses().length; i++)
+ {
+ // The target package source module should have a resolved package
+ // for the "used" package in its set of accessible packages,
+ // since it claims to use it, so get the associated resolved
+ // package.
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(cap.getUses()[i]);
+
+ // In general, the resolved package should not be null,
+ // but check for safety.
+ if (rp != null)
+ {
+ // First, iterate through all package sources for the resolved
+ // package associated with the current "used" package and calculate
+ // and combine the "uses" constraints for each package source.
+ for (int srcIdx = 0; srcIdx < rp.m_sourceList.size(); srcIdx++)
+ {
+ usesMap = calculateUsesConstraints(
+ (PackageSource) rp.m_sourceList.get(srcIdx),
+ moduleMap, usesMap, cycleMap, candidatesMap);
+ }
+
+ // Then, add the resolved package for the current "used" package
+ // as a "uses" constraint too; add it to an existing constraint
+ // list if the current "used" package is already in the uses map.
+ List constraintList = (List) usesMap.get(cap.getUses()[i]);
+ if (constraintList == null)
+ {
+ constraintList = new ArrayList();
+ }
+ constraintList.add(rp);
+ usesMap.put(cap.getUses()[i], constraintList);
+ }
+ }
+
+ return usesMap;
+ }
+
+ private static Map getModulePackages(Map moduleMap, IModule module, Map candidatesMap)
+ throws ResolveException
+ {
+ Map map = (Map) moduleMap.get(module);
+
+ if (map == null)
+ {
+ map = calculateModulePackages(module, candidatesMap);
+ moduleMap.put(module, map);
+ }
+ return map;
+ }
+
+ /**
+ * <p>
+ * Calculates the module's set of accessible packages and their
+ * assocaited package sources. This method uses the current candidates
+ * for resolving the module's requirements from the candidate map
+ * to calculate the module's accessible packages.
+ * </p>
+ * @param module the module whose package map is to be calculated.
+ * @param candidatesMap the map of potential candidates for resolving
+ * the module's requirements.
+ * @return a map of the packages accessible to the specified module where
+ * the key of the map is the package name and the value of the map
+ * is a ResolvedPackage.
+ **/
+ private static Map calculateModulePackages(IModule module, Map candidatesMap)
+ throws ResolveException
+ {
+//System.out.println("calculateModulePackages("+module+")");
+ Map importedPackages = calculateImportedPackages(module, candidatesMap);
+ Map exportedPackages = calculateExportedPackages(module);
+ Map requiredPackages = calculateRequiredPackages(module, candidatesMap);
+
+ // Merge exported packages into required packages. If a package is both
+ // exported and required, then append the exported source to the end of
+ // the require package sources; otherwise just add it to the package map.
+ for (Iterator i = exportedPackages.entrySet().iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ ResolvedPackage rpReq = (ResolvedPackage) requiredPackages.get(entry.getKey());
+ if (rpReq != null)
+ {
+ // Merge exported and required packages, avoiding duplicate
+ // package sources and maintaining ordering.
+ ResolvedPackage rpExport = (ResolvedPackage) entry.getValue();
+ rpReq.merge(rpExport);
+ }
+ else
+ {
+ requiredPackages.put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ // Merge imported packages into required packages. Imports overwrite
+ // any required and/or exported package.
+ for (Iterator i = importedPackages.entrySet().iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ requiredPackages.put(entry.getKey(), entry.getValue());
+ }
+
+ return requiredPackages;
+ }
+
+ private static Map calculateImportedPackages(IModule targetModule, Map candidatesMap)
+ throws ResolveException
+ {
+ return (candidatesMap.get(targetModule) == null)
+ ? calculateImportedPackagesResolved(targetModule)
+ : calculateImportedPackagesUnresolved(targetModule, candidatesMap);
+ }
+
+ private static Map calculateImportedPackagesUnresolved(IModule targetModule, Map candidatesMap)
+ throws ResolveException
+ {
+//System.out.println("calculateImportedPackagesUnresolved("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ // Get the candidate set list to get all candidates for
+ // all of the target module's requirements.
+ List candSetList = (List) candidatesMap.get(targetModule);
+
+ // Loop through all candidate sets that represent import dependencies
+ // for the target module and add the current candidate's package source
+ // to the imported package map.
+ for (int candSetIdx = 0; (candSetList != null) && (candSetIdx < candSetList.size()); candSetIdx++)
+ {
+ CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
+ PackageSource ps = cs.m_candidates[cs.m_idx];
+
+ if (ps.m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ String pkgName = (String)
+ ps.m_capability.getProperties().get(ICapability.PACKAGE_PROPERTY);
+
+ ResolvedPackage rp = new ResolvedPackage(pkgName);
+ rp.m_sourceList.add(ps);
+ pkgMap.put(rp.m_name, rp);
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateImportedPackagesResolved(IModule targetModule)
+ throws ResolveException
+ {
+//System.out.println("calculateImportedPackagesResolved("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ // Loop through the target module's wires for package
+ // dependencies and add the resolved package source to the
+ // imported package map.
+ IWire[] wires = targetModule.getWires();
+ for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
+ {
+ if (wires[wireIdx].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ String pkgName = (String)
+ wires[wireIdx].getCapability().getProperties().get(ICapability.PACKAGE_PROPERTY);
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
+ rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+ rp.m_sourceList.add(new PackageSource(wires[wireIdx].getExporter(), wires[wireIdx].getCapability()));
+ pkgMap.put(rp.m_name, rp);
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateExportedPackages(IModule targetModule)
+ {
+//System.out.println("calculateExportedPackages("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ // Loop through the target module's capabilities that represent
+ // exported packages and add them to the exported package map.
+ ICapability[] caps = targetModule.getDefinition().getCapabilities();
+ for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+ {
+ if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ String pkgName = (String)
+ caps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
+ rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+ rp.m_sourceList.add(new PackageSource(targetModule, caps[capIdx]));
+ pkgMap.put(rp.m_name, rp);
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateRequiredPackages(IModule targetModule, Map candidatesMap)
+ {
+ return (candidatesMap.get(targetModule) == null)
+ ? calculateRequiredPackagesResolved(targetModule)
+ : calculateRequiredPackagesUnresolved(targetModule, candidatesMap);
+ }
+
+ private static Map calculateRequiredPackagesUnresolved(IModule targetModule, Map candidatesMap)
+ {
+//System.out.println("calculateRequiredPackagesUnresolved("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ // Loop through target module's candidate list for candidates
+ // for its module dependencies and merge re-exported packages.
+ List candSetList = (List) candidatesMap.get(targetModule);
+ for (int candSetIdx = 0; (candSetList != null) && (candSetIdx < candSetList.size()); candSetIdx++)
+ {
+ CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
+ PackageSource ps = cs.m_candidates[cs.m_idx];
+
+ // If the capabaility is a module dependency, then flatten it to packages.
+ if (ps.m_capability.getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ // Calculate transitively required packages.
+ Map cycleMap = new HashMap();
+ cycleMap.put(targetModule, targetModule);
+ Map requireMap =
+ calculateExportedAndReexportedPackages(
+ ps, candidatesMap, cycleMap);
+
+ // Take the flattened required package map for the current
+ // module dependency and merge it into the existing map
+ // of required packages.
+ for (Iterator reqIter = requireMap.entrySet().iterator(); reqIter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) reqIter.next();
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
+ if (rp != null)
+ {
+ // Merge required packages, avoiding duplicate
+ // package sources and maintaining ordering.
+ ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
+ rp.merge(rpReq);
+ }
+ else
+ {
+ pkgMap.put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateRequiredPackagesResolved(IModule targetModule)
+ {
+//System.out.println("calculateRequiredPackagesResolved("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ // Loop through target module's wires for module dependencies
+ // and merge re-exported packages.
+ IWire[] wires = targetModule.getWires();
+ for (int i = 0; (wires != null) && (i < wires.length); i++)
+ {
+ // If the wire is a module dependency, then flatten it to packages.
+ if (wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ // Calculate transitively required packages.
+ // We can call calculateExportedAndReexportedPackagesResolved()
+ // directly, since we know all dependencies have to be resolved
+ // because this module itself is resolved.
+ Map cycleMap = new HashMap();
+ cycleMap.put(targetModule, targetModule);
+ Map requireMap =
+ calculateExportedAndReexportedPackagesResolved(
+ wires[i].getExporter(), cycleMap);
+
+ // Take the flattened required package map for the current
+ // module dependency and merge it into the existing map
+ // of required packages.
+ for (Iterator reqIter = requireMap.entrySet().iterator(); reqIter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) reqIter.next();
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
+ if (rp != null)
+ {
+ // Merge required packages, avoiding duplicate
+ // package sources and maintaining ordering.
+ ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
+ rp.merge(rpReq);
+ }
+ else
+ {
+ pkgMap.put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateExportedAndReexportedPackages(
+ PackageSource psTarget, Map candidatesMap, Map cycleMap)
+ {
+ return (candidatesMap.get(psTarget.m_module) == null)
+ ? calculateExportedAndReexportedPackagesResolved(psTarget.m_module, cycleMap)
+ : calculateExportedAndReexportedPackagesUnresolved(psTarget, candidatesMap, cycleMap);
+ }
+
+ private static Map calculateExportedAndReexportedPackagesUnresolved(
+ PackageSource psTarget, Map candidatesMap, Map cycleMap)
+ {
+//System.out.println("calculateExportedAndReexportedPackagesUnresolved("+psTarget.m_module+")");
+ Map pkgMap = new HashMap();
+
+ if (cycleMap.get(psTarget.m_module) != null)
+ {
+ return pkgMap;
+ }
+
+ cycleMap.put(psTarget.m_module, psTarget.m_module);
+
+ // Loop through all current candidates for target module's dependencies
+ // and calculate the module's complete set of required packages (and
+ // their associated package sources) and the complete set of required
+ // packages to be re-exported.
+ Map allRequiredMap = new HashMap();
+ Map reexportedPkgMap = new HashMap();
+ List candSetList = (List) candidatesMap.get(psTarget.m_module);
+ for (int candSetIdx = 0; candSetIdx < candSetList.size(); candSetIdx++)
+ {
+ CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
+ PackageSource ps = cs.m_candidates[cs.m_idx];
+
+ // If the candidate is resolving a module dependency, then
+ // flatten the required packages if they are re-exported.
+ if (ps.m_capability.getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ // Determine if required packages are re-exported.
+ boolean reexport = false;
+ R4Directive[] dirs = ((Requirement) cs.m_requirement).getDirectives();
+ for (int dirIdx = 0;
+ !reexport && (dirs != null) && (dirIdx < dirs.length); dirIdx++)
+ {
+ if (dirs[dirIdx].getName().equals(Constants.VISIBILITY_DIRECTIVE)
+ && dirs[dirIdx].getValue().equals(Constants.VISIBILITY_REEXPORT))
+ {
+ reexport = true;
+ }
+ }
+
+ // Recursively calculate the required packages for the
+ // current candidate.
+ Map requiredMap = calculateExportedAndReexportedPackages(ps, candidatesMap, cycleMap);
+
+ // Merge the candidate's exported and required packages
+ // into the complete set of required packages.
+ for (Iterator reqIter = requiredMap.entrySet().iterator(); reqIter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) reqIter.next();
+ String pkgName = (String) entry.getKey();
+
+ // Merge the current set of required packages into
+ // the overall complete set of required packages.
+ // We calculate all the required packages, because
+ // despite the fact that some packages will be required
+ // "privately" and some will be required "reexport", any
+ // re-exported package sources will ultimately need to
+ // be combined with privately required package sources,
+ // if the required packages overlap. This is one of the
+ // bad things about require-bundle behavior, it does not
+ // necessarily obey the visibility rules declared in the
+ // dependency.
+ ResolvedPackage rp = (ResolvedPackage) allRequiredMap.get(pkgName);
+ if (rp != null)
+ {
+ // Create the union of all package sources.
+ ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
+ rp.merge(rpReq);
+ }
+ else
+ {
+ // Add package to required map.
+ allRequiredMap.put(pkgName, entry.getValue());
+ }
+
+ // Keep track of all required packages to be re-exported.
+ // All re-exported packages will need to be merged into the
+ // target module's package map and become part of its overall
+ // export signature.
+ if (reexport)
+ {
+ reexportedPkgMap.put(pkgName, pkgName);
+ }
+ }
+ }
+ }
+
+ // For the target module we have now calculated its entire set
+ // of required packages and their associated package sources in
+ // allRequiredMap and have calculated all packages to be re-exported
+ // in reexportedPkgMap. Add all re-exported required packages to the
+ // target module's package map since they will be part of its export
+ // signature.
+ for (Iterator iter = reexportedPkgMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ String pkgName = (String) ((Map.Entry) iter.next()).getKey();
+ pkgMap.put(pkgName, allRequiredMap.get(pkgName));
+ }
+
+ // Now loop through the target module's export package capabilities and
+ // add the target module as a package source for any exported packages.
+ ICapability[] candCaps = psTarget.m_module.getDefinition().getCapabilities();
+ for (int capIdx = 0; (candCaps != null) && (capIdx < candCaps.length); capIdx++)
+ {
+ if (candCaps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ String pkgName = (String)
+ candCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
+ rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+ rp.m_sourceList.add(new PackageSource(psTarget.m_module, candCaps[capIdx]));
+ pkgMap.put(rp.m_name, rp);
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateExportedAndReexportedPackagesResolved(
+ IModule targetModule, Map cycleMap)
+ {
+//System.out.println("calculateExportedAndRequiredPackagesResolved("+targetModule+")");
+ Map pkgMap = new HashMap();
+
+ if (cycleMap.get(targetModule) != null)
+ {
+ return pkgMap;
+ }
+
+ cycleMap.put(targetModule, targetModule);
+
+ // Loop through all wires for the target module's module dependencies
+ // and calculate the module's complete set of required packages (and
+ // their associated package sources) and the complete set of required
+ // packages to be re-exported.
+ Map allRequiredMap = new HashMap();
+ Map reexportedPkgMap = new HashMap();
+ IWire[] wires = targetModule.getWires();
+ for (int i = 0; (wires != null) && (i < wires.length); i++)
+ {
+ // If the wire is a module dependency, then flatten it to packages.
+ if (wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ // Determine if required packages are re-exported.
+ boolean reexport = false;
+ R4Directive[] dirs = ((Requirement) wires[i].getRequirement()).getDirectives();
+ for (int dirIdx = 0;
+ !reexport && (dirs != null) && (dirIdx < dirs.length); dirIdx++)
+ {
+ if (dirs[dirIdx].getName().equals(Constants.VISIBILITY_DIRECTIVE)
+ && dirs[dirIdx].getValue().equals(Constants.VISIBILITY_REEXPORT))
+ {
+ reexport = true;
+ }
+ }
+
+ // Recursively calculate the required packages for the
+ // wire's exporting module.
+ Map requiredMap = calculateExportedAndReexportedPackagesResolved(wires[i].getExporter(), cycleMap);
+
+ // Merge the wires exported and re-exported packages
+ // into the complete set of required packages.
+ for (Iterator reqIter = requiredMap.entrySet().iterator(); reqIter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) reqIter.next();
+ String pkgName = (String) entry.getKey();
+
+ // Merge the current set of required packages into
+ // the overall complete set of required packages.
+ // We calculate all the required packages, because
+ // despite the fact that some packages will be required
+ // "privately" and some will be required "reexport", any
+ // re-exported package sources will ultimately need to
+ // be combined with privately required package sources,
+ // if the required packages overlap. This is one of the
+ // bad things about require-bundle behavior, it does not
+ // necessarily obey the visibility rules declared in the
+ // dependency.
+ ResolvedPackage rp = (ResolvedPackage) allRequiredMap.get(pkgName);
+ if (rp != null)
+ {
+ // Create the union of all package sources.
+ ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
+ rp.merge(rpReq);
+ }
+ else
+ {
+ // Add package to required map.
+ allRequiredMap.put(pkgName, entry.getValue());
+ }
+
+ // Keep track of all required packages to be re-exported.
+ // All re-exported packages will need to be merged into the
+ // target module's package map and become part of its overall
+ // export signature.
+ if (reexport)
+ {
+ reexportedPkgMap.put(pkgName, pkgName);
+ }
+ }
+ }
+ }
+
+ // For the target module we have now calculated its entire set
+ // of required packages and their associated package sources in
+ // allRequiredMap and have calculated all packages to be re-exported
+ // in reexportedPkgMap. Add all re-exported required packages to the
+ // target module's package map since they will be part of its export
+ // signature.
+ for (Iterator iter = reexportedPkgMap.entrySet().iterator(); iter.hasNext(); )
+ {
+ String pkgName = (String) ((Map.Entry) iter.next()).getKey();
+ pkgMap.put(pkgName, allRequiredMap.get(pkgName));
+ }
+
+ // Now loop through the target module's export package capabilities and
+ // add the target module as a package source for any exported packages.
+ ICapability[] caps = targetModule.getDefinition().getCapabilities();
+ for (int i = 0; (caps != null) && (i < caps.length); i++)
+ {
+ if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ {
+ String pkgName = (String)
+ caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
+ ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
+ rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+ rp.m_sourceList.add(new PackageSource(targetModule, caps[i]));
+ pkgMap.put(rp.m_name, rp);
+ }
+ }
+
+ return pkgMap;
+ }
+
+ private static Map calculateCandidateRequiredPackages(IModule module, PackageSource psTarget, Map candidatesMap)
+ {
+//System.out.println("calculateCandidateRequiredPackages("+module+")");
+ Map cycleMap = new HashMap();
+ cycleMap.put(module, module);
+ return calculateExportedAndReexportedPackages(psTarget, candidatesMap, cycleMap);
+ }
+
+ private static void incrementCandidateConfiguration(List resolverList)
+ throws ResolveException
+ {
+ for (int i = 0; i < resolverList.size(); i++)
+ {
+ List candSetList = (List) resolverList.get(i);
+ for (int j = 0; j < candSetList.size(); j++)
+ {
+ CandidateSet cs = (CandidateSet) candSetList.get(j);
+ // See if we can increment the candidate set, without overflowing
+ // the candidate array bounds.
+ if ((cs.m_idx + 1) < cs.m_candidates.length)
+ {
+ cs.m_idx++;
+ return;
+ }
+ // If the index will overflow the candidate array bounds,
+ // then set the index back to zero and try to increment
+ // the next candidate.
+ else
+ {
+ cs.m_idx = 0;
+ }
+ }
+ }
+ throw new ResolveException(
+ "Unable to resolve due to constraint violation.", null, null);
+ }
+
+ private static Map populateWireMap(
+ ResolverState state, Map candidatesMap, IModule importer, Map wireMap)
+ {
+ // If the module is already resolved or it is part of
+ // a cycle, then just return the wire map.
+ if (state.isResolved(importer) || (wireMap.get(importer) != null))
+ {
+ return wireMap;
+ }
+
+ List candSetList = (List) candidatesMap.get(importer);
+ List moduleWires = new ArrayList();
+ List packageWires = new ArrayList();
+ IWire[] wires = new IWire[candSetList.size()];
+
+ // Put the module in the wireMap with an empty wire array;
+ // we do this early so we can use it to detect cycles.
+ wireMap.put(importer, wires);
+
+ // Loop through each candidate Set and create a wire
+ // for the selected candidate for the associated import.
+ for (int candSetIdx = 0; candSetIdx < candSetList.size(); candSetIdx++)
+ {
+ // Get the current candidate set.
+ CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
+
+ // Create a wire for the current candidate based on the type
+ // of requirement it resolves.
+ if (cs.m_requirement.getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ {
+ moduleWires.add(new R4WireModule(
+ importer,
+ cs.m_requirement,
+ cs.m_candidates[cs.m_idx].m_module,
+ cs.m_candidates[cs.m_idx].m_capability,
+ calculateCandidateRequiredPackages(importer, cs.m_candidates[cs.m_idx], candidatesMap)));
+ }
+ else
+ {
+ // Add wire for imported package.
+ packageWires.add(new R4Wire(
+ importer,
+ cs.m_requirement,
+ cs.m_candidates[cs.m_idx].m_module,
+ cs.m_candidates[cs.m_idx].m_capability));
+ }
+
+ // Create any necessary wires for the selected candidate module.
+ wireMap = populateWireMap(
+ state, candidatesMap, cs.m_candidates[cs.m_idx].m_module, wireMap);
+ }
+
+ packageWires.addAll(moduleWires);
+ wireMap.put(importer, packageWires.toArray(wires));
+
+ return wireMap;
+ }
+
+ //
+ // Utility methods.
+ //
+
+ private static PackageSource[] shrinkCandidateArray(PackageSource[] candidates)
+ {
+ if (candidates == null)
+ {
+ return m_emptySources;
+ }
+
+ // Move all non-null values to one end of the array.
+ int lower = 0;
+ for (int i = 0; i < candidates.length; i++)
+ {
+ if (candidates[i] != null)
+ {
+ candidates[lower++] = candidates[i];
+ }
+ }
+
+ if (lower == 0)
+ {
+ return m_emptySources;
+ }
+
+ // Copy non-null values into a new array and return.
+ PackageSource[] newCandidates= new PackageSource[lower];
+ System.arraycopy(candidates, 0, newCandidates, 0, lower);
+ return newCandidates;
+ }
+
+ //
+ // Inner classes.
+ //
+
+ public static interface ResolverState
+ {
+ IModule[] getModules();
+ // TODO: RESOLVER - This should be on module.
+ String getBundleSymbolicName(IModule module);
+ // TODO: RESOLVER - This should be on module.
+ boolean isResolved(IModule module);
+ Map getPotentialFragments(IModule module);
+ List getPotentialHosts(IModule module);
+ PackageSource[] getResolvedCandidates(IRequirement req);
+ PackageSource[] getUnresolvedCandidates(IRequirement req);
+ }
+
+ private static class CandidateSet
+ {
+ public IModule m_module = null;
+ public IRequirement m_requirement = null;
+ public PackageSource[] m_candidates = null;
+ public int m_idx = 0;
+ public CandidateSet(IModule module, IRequirement requirement, PackageSource[] candidates)
+ {
+ m_module = module;
+ m_requirement = requirement;
+ m_candidates = candidates;
+ }
+ }
+
+ // TODO: RESOLVER - This is a hack, we need to calcualte fragments differently.
+ public class Result
+ {
+ public final IModule m_host;
+ public final Map m_fragmentMap;
+ public final Map m_resolvedModuleWireMap;
+
+ public Result(IModule host, Map fragmentMap, Map resolvedModuleWireMap)
+ {
+ m_host = host;
+ m_fragmentMap = fragmentMap;
+ m_resolvedModuleWireMap = resolvedModuleWireMap;
+ }
+ }
+}
\ No newline at end of file