Initial refactoring to move fragment handling into the resolver. (FELIX-2858)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1073953 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index 7a677ac..99f0420 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -124,25 +124,28 @@
{
// Remove the bundle's associated modules from the resolver state
// and close them.
- for (int i = 0; i < m_modules.size(); i++)
+ for (Module m : m_modules)
{
- getFramework().getResolverState().removeModule(m_modules.get(i));
- ((ModuleImpl) m_modules.get(i)).close();
- }
- }
+ // Remove the module from the resolver state.
+ getFramework().getResolver().removeModule(m);
- /**
- * This is sort of a hacky method called after uninstalling a bundle.
- * If the bundle is a fragment, this will unmerge it from any unresolved
- * hosts. This is necessary since fragments are pre-merged into unresolved
- * hosts. If uninstalled fragments are not unmerged from unresolved hosts,
- * any attempts to subsequently resolve the host will result in an exception.
- */
- synchronized void cleanAfterUninstall()
- {
- for (int i = 0; i < m_modules.size(); i++)
- {
- getFramework().getResolverState().unmergeFragment(m_modules.get(i));
+ // Set fragments to null, which will remove the module from all
+ // of its dependent fragment modules.
+ try
+ {
+ ((ModuleImpl) m).attachFragments(null);
+ }
+ catch (Exception ex)
+ {
+ getFramework().getLogger().log(
+ m.getBundle(), Logger.LOG_ERROR, "Error detaching fragments.", ex);
+ }
+ // Set wires to null, which will remove the module from all
+ // of its dependent modules.
+ ((ModuleImpl) m).setWires(null);
+
+ // Close the module's content.
+ ((ModuleImpl) m).close();
}
}
@@ -1100,7 +1103,7 @@
{
// Since revising a module adds the module to the global
// state, we must remove it from the global state on rollback.
- getFramework().getResolverState().removeModule(m);
+ getFramework().getResolver().removeModule(m);
}
return m_archive.rollbackRevise();
}
@@ -1139,7 +1142,7 @@
{
// Now that the module is added to the bundle, we can update
// the resolver's module state.
- getFramework().getResolverState().addModule(module);
+ getFramework().getResolver().addModule(module);
}
}
diff --git a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
index 36d673a..bb57b0c 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -36,7 +36,7 @@
import java.util.NoSuchElementException;
import java.util.Set;
-import org.apache.felix.framework.Felix.FelixResolver;
+import org.apache.felix.framework.Felix.StatefulResolver;
import org.apache.felix.framework.capabilityset.Attribute;
import org.apache.felix.framework.capabilityset.Capability;
import org.apache.felix.framework.capabilityset.Directive;
@@ -199,7 +199,6 @@
{
m_capabilities = new ArrayList<Capability>(0);
m_logger.log(
- felix,
Logger.LOG_ERROR,
"Error parsing system bundle export statement: "
+ syspkgs, ex);
@@ -726,7 +725,7 @@
return null;
}
- public FelixResolver getResolver()
+ public StatefulResolver getResolver()
{
return null;
}
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 bdd8a6e..542a0fa 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -24,7 +24,6 @@
import java.security.*;
import java.util.*;
import java.util.Map.Entry;
-import org.apache.felix.framework.ModuleImpl.FragmentRequirement;
import org.apache.felix.framework.ServiceRegistry.ServiceRegistryCallbacks;
import org.apache.felix.framework.cache.BundleArchive;
import org.apache.felix.framework.cache.BundleCache;
@@ -76,6 +75,7 @@
import org.osgi.framework.hooks.service.ListenerHook;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.startlevel.StartLevel;
+import sun.org.mozilla.javascript.internal.UintMap;
public class Felix extends BundleImpl implements Framework
{
@@ -92,9 +92,8 @@
// Mutable configuration properties passed into constructor.
private final Map m_configMutableMap;
- // MODULE FACTORY.
- private final FelixResolverState m_resolverState;
- private final FelixResolver m_felixResolver;
+ // Resolver and resolver state.
+ private final StatefulResolver m_resolver;
// Lock object used to determine if an individual bundle
// lock or the global lock can be acquired.
@@ -364,10 +363,11 @@
m_bundleStreamHandler = new URLHandlersBundleStreamHandler(this);
// Create a resolver and its state.
- m_resolverState = new FelixResolverState(
- m_logger, (String) m_configMap.get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT));
- m_felixResolver = new FelixResolver(
- new ResolverImpl(m_logger), m_resolverState);
+ m_resolver = new StatefulResolver(
+ new ResolverImpl(m_logger),
+ new ResolverStateImpl(
+ m_logger,
+ (String) m_configMap.get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT)));
// Create the extension manager, which we will use as the module
// definition for creating the system bundle module.
@@ -394,14 +394,9 @@
return m_configMap;
}
- FelixResolver getResolver()
+ StatefulResolver getResolver()
{
- return m_felixResolver;
- }
-
- FelixResolverState getResolverState()
- {
- return m_resolverState;
+ return m_resolver;
}
URLStreamHandler getBundleStreamHandler()
@@ -641,7 +636,7 @@
// state to be set to RESOLVED.
try
{
- m_felixResolver.resolve(getCurrentModule());
+ m_resolver.resolve(getCurrentModule());
}
catch (ResolveException ex)
{
@@ -1989,7 +1984,7 @@
{
m_extensionManager.addExtensionBundle(this, bundle);
// TODO: REFACTOR - Perhaps we could move this into extension manager.
- m_resolverState.refreshSystemBundleModule(m_extensionManager.getModule());
+ m_resolver.addModule(m_extensionManager.getModule());
// TODO: REFACTOR - Not clear why this is here. We should look at all of these steps more closely.
setBundleStateAndNotify(bundle, Bundle.RESOLVED);
}
@@ -2374,10 +2369,6 @@
// Set state to uninstalled.
setBundleStateAndNotify(bundle, Bundle.UNINSTALLED);
bundle.setLastModified(System.currentTimeMillis());
-
- // If this bundle is a fragment, unmerge it from any
- // unresolved hosts.
- bundle.cleanAfterUninstall();
}
finally
{
@@ -2552,7 +2543,7 @@
else
{
m_extensionManager.addExtensionBundle(this, bundle);
- m_resolverState.refreshSystemBundleModule(m_extensionManager.getModule());
+ m_resolver.addModule(m_extensionManager.getModule());
}
}
catch (Throwable ex)
@@ -3135,7 +3126,7 @@
List<Attribute> attrs = new ArrayList<Attribute>(1);
attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
Requirement req = new RequirementImpl(null, Capability.PACKAGE_NAMESPACE, dirs, attrs);
- Set<Capability> exports = m_resolverState.getCandidates(null, req, false);
+ Set<Capability> exports = m_resolver.getCandidates(null, req, false);
// We only want resolved capabilities.
for (Iterator<Capability> it = exports.iterator(); it.hasNext(); )
@@ -3282,7 +3273,7 @@
attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
Requirement req =
new RequirementImpl(null, Capability.PACKAGE_NAMESPACE, dirs, attrs);
- Set<Capability> exports = m_resolverState.getCandidates(null, req, false);
+ Set<Capability> exports = m_resolver.getCandidates(null, req, false);
// We only want resolved capabilities.
for (Iterator<Capability> it = exports.iterator(); it.hasNext(); )
{
@@ -3436,13 +3427,13 @@
{
try
{
- m_felixResolver.resolve(bundle.getCurrentModule());
+ m_resolver.resolve(bundle.getCurrentModule());
}
catch (ResolveException ex)
{
if (ex.getModule() != null)
{
- Bundle b = ((ModuleImpl) ex.getModule()).getBundle();
+ Bundle b = ex.getModule().getBundle();
throw new BundleException(
"Unresolved constraint in bundle "
+ b + ": " + ex.getMessage());
@@ -3974,18 +3965,34 @@
// Miscellaneous inner classes.
//
- public class FelixResolver
+ class StatefulResolver
{
private final Resolver m_resolver;
- private final FelixResolverState m_resolverState;
+ private final ResolverStateImpl m_resolverState;
- public FelixResolver(Resolver resolver, FelixResolverState resolverState)
+ StatefulResolver(Resolver resolver, ResolverStateImpl resolverState)
{
m_resolver = resolver;
m_resolverState = resolverState;
}
- public void resolve(Module rootModule) throws ResolveException
+ void addModule(Module m)
+ {
+ m_resolverState.addModule(m);
+ }
+
+ void removeModule(Module m)
+ {
+ m_resolverState.removeModule(m);
+ }
+
+ Set<Capability> getCandidates(
+ Module reqModule, Requirement req, boolean obeyMandatory)
+ {
+ return m_resolverState.getCandidates(reqModule, req, obeyMandatory);
+ }
+
+ void resolve(Module rootModule) throws ResolveException
{
// Although there is a race condition to check the bundle state
// then lock it, we do this because we don't want to acquire the
@@ -4013,48 +4020,16 @@
return;
}
- // If the root module to resolve is a fragment, then we
- // must find a host to attach it to and resolve the host
- // instead, since the underlying resolver doesn't know
- // how to deal with fragments.
- Module newRootModule = m_resolverState.findHost(rootModule);
- if (!Util.isFragment(newRootModule))
- {
- // Check singleton status.
- m_resolverState.checkSingleton(newRootModule);
+ // Check singleton status.
+// TOOD: FRAGMENT RESOLVER - Merge singleton handling into resolver.
+// m_resolverState.checkSingleton(rootModule);
- boolean repeat;
- do
- {
- repeat = false;
- try
- {
- // Resolve the module.
- wireMap = m_resolver.resolve(m_resolverState, newRootModule);
+ // Resolve the module.
+ wireMap = m_resolver.resolve(
+ m_resolverState, rootModule, m_resolverState.getFragments());
- // Mark all modules as resolved.
- markResolvedModules(wireMap);
- }
- catch (ResolveException ex)
- {
- if ((ex.getRequirement() != null)
- && (ex.getRequirement() instanceof FragmentRequirement)
- && (rootModule !=
- ((FragmentRequirement) ex.getRequirement()).getFragment()))
- {
- m_resolverState.detachFragment(
- newRootModule,
- ((FragmentRequirement) ex.getRequirement()).getFragment());
- repeat = true;
- }
- else
- {
- throw ex;
- }
- }
- }
- while (repeat);
- }
+ // Mark all modules as resolved.
+ markResolvedModules(wireMap);
}
finally
{
@@ -4066,7 +4041,7 @@
}
}
- public Wire resolve(Module module, String pkgName) throws ResolveException
+ Wire resolve(Module module, String pkgName) throws ResolveException
{
Wire candidateWire = null;
// We cannot dynamically import if the module is not already resolved
@@ -4100,7 +4075,8 @@
}
}
- wireMap = m_resolver.resolve(m_resolverState, module, pkgName);
+ wireMap = m_resolver.resolve(
+ m_resolverState, module, pkgName, m_resolverState.getFragments());
if ((wireMap != null) && wireMap.containsKey(module))
{
@@ -4135,15 +4111,9 @@
return candidateWire;
}
- public synchronized Set<Capability> getCandidates(
- Module reqModule, Requirement req, boolean obeyMandatory)
- {
- return m_resolverState.getCandidates(reqModule, req, obeyMandatory);
- }
-
// This method duplicates a lot of logic from:
// ResolverImpl.getDynamicImportCandidates()
- public boolean isAllowedDynamicImport(Module module, String pkgName)
+ boolean isAllowedDynamicImport(Module module, String pkgName)
{
// Unresolved modules cannot dynamically import, nor can the default
// package be dynamically imported.
@@ -4196,42 +4166,136 @@
}
private void markResolvedModules(Map<Module, List<Wire>> wireMap)
+ throws ResolveException
{
+// DO THIS IN THREE PASSES:
+// 1. Aggregate fragments per host.
+// 2. Attach wires and fragments to hosts.
+// -> If fragments fail to attach, then undo.
+// 3. Mark hosts and fragments as resolved.
if (wireMap != null)
{
- Iterator<Entry<Module, List<Wire>>> iter = wireMap.entrySet().iterator();
- // Iterate over the map to mark the modules as resolved and
- // update our resolver data structures.
- while (iter.hasNext())
+ // First pass: Loop through the wire map to find the host wires
+ // for any fragments and map a host to all of its fragments.
+ Map<Module, List<Module>> hosts = new HashMap<Module, List<Module>>();
+ for (Entry<Module, List<Wire>> entry : wireMap.entrySet())
{
- Entry<Module, List<Wire>> entry = iter.next();
Module module = entry.getKey();
List<Wire> wires = entry.getValue();
- // Only add wires attribute if some exist; export
- // only modules may not have wires.
- for (int wireIdx = 0; wireIdx < wires.size(); wireIdx++)
+ if (Util.isFragment(module))
{
- m_logger.log(
- Logger.LOG_DEBUG,
- "WIRE: " + wires.get(wireIdx));
+ for (Iterator<Wire> itWires = wires.iterator(); itWires.hasNext(); )
+ {
+ Wire w = itWires.next();
+ List<Module> fragments = hosts.get(w.getExporter());
+ if (fragments == null)
+ {
+ fragments = new ArrayList<Module>();
+ hosts.put(w.getExporter(), fragments);
+ }
+ fragments.add(w.getImporter());
+ }
}
+ }
+
+ // Second pass: Loop through the wire map to set wires and attach
+ // fragments, if any.
+ for (Entry<Module, List<Wire>> entry : wireMap.entrySet())
+ {
+ Module module = entry.getKey();
+ List<Wire> wires = entry.getValue();
+
+// TODO: FRAGMENT RESOLVER - Better way to handle log level?
+ for (Iterator<Wire> itWires = wires.iterator(); itWires.hasNext(); )
+ {
+ Wire w = itWires.next();
+ if (!Util.isFragment(module))
+ {
+ m_logger.log(Logger.LOG_DEBUG, "WIRE: " + w);
+ }
+ else
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "FRAGMENT WIRE: "
+ + module + " -> hosted by -> " + w.getExporter());
+ }
+ }
+
+ // Set the module's wires.
((ModuleImpl) module).setWires(wires);
- // Resolve all attached fragments.
- List<Module> fragments = ((ModuleImpl) module).getFragments();
- for (int i = 0; (fragments != null) && (i < fragments.size()); i++)
+ // Attach fragments, if any.
+ List<Module> fragments = hosts.get(module);
+ if (fragments != null)
{
- ((ModuleImpl) fragments.get(i)).setResolved();
- // Update the state of the module's bundle to resolved as well.
- markBundleResolved(fragments.get(i));
- m_logger.log(((ModuleImpl) fragments.get(i)).getBundle(),
- Logger.LOG_DEBUG,
- "FRAGMENT WIRE: " + fragments.get(i) + " -> hosted by -> " + module);
+ try
+ {
+ ((ModuleImpl) module).attachFragments(fragments);
+ }
+ catch (Exception ex)
+ {
+ // This is a fatal error, so undo everything and
+ // throw an exception.
+ for (Entry<Module, List<Wire>> reentry : wireMap.entrySet())
+ {
+ module = reentry.getKey();
+
+ // Undo wires.
+ ((ModuleImpl) module).setWires(null);
+
+ fragments = hosts.get(module);
+ if (fragments != null)
+ {
+ try
+ {
+ // Undo fragments.
+ ((ModuleImpl) module).attachFragments(null);
+ }
+ catch (Exception ex2)
+ {
+ // We are in big trouble.
+ RuntimeException rte = new RuntimeException(
+ "Unable to clean up resolver failure.", ex2);
+ m_logger.log(
+ Logger.LOG_ERROR,
+ rte.getMessage(), ex2);
+ throw rte;
+ }
+
+ // Reindex host with no fragments.
+ m_resolverState.addModule(module);
+ }
+ }
+
+ ResolveException re = new ResolveException(
+ "Unable to attach fragments to " + module,
+ module, null);
+ re.initCause(ex);
+ m_logger.log(
+ Logger.LOG_ERROR,
+ re.getMessage(), ex);
+ throw re;
+ }
+
+ // Reindex host with attached fragments.
+ m_resolverState.addModule(module);
}
- // Update the resolver state to show the module as resolved.
+ }
+
+ // Third pass: Loop through the wire map to mark modules as resolved
+ // and update the resolver state.
+ for (Entry<Module, List<Wire>> entry : wireMap.entrySet())
+ {
+ Module module = entry.getKey();
+ // Mark module as resolved.
((ModuleImpl) module).setResolved();
- m_resolverState.moduleResolved(module);
+ // Update resolver state to remove substituted capabilities.
+ if (!Util.isFragment(module))
+ {
+ m_resolverState.removeSubstitutedCapabilities(module);
+ }
// Update the state of the module's bundle to resolved as well.
markBundleResolved(module);
}
@@ -4419,7 +4483,7 @@
}
catch (Throwable throwable)
{
- m_logger.log(Felix.this,
+ m_logger.log(
Logger.LOG_WARNING,
"Exception stopping a system bundle activator.",
throwable);
diff --git a/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java b/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
deleted file mode 100644
index ca74100..0000000
--- a/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
+++ /dev/null
@@ -1,955 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.framework;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.TreeSet;
-import org.apache.felix.framework.capabilityset.Capability;
-import org.apache.felix.framework.capabilityset.CapabilitySet;
-import org.apache.felix.framework.capabilityset.Directive;
-import org.apache.felix.framework.resolver.Module;
-import org.apache.felix.framework.capabilityset.Requirement;
-import org.apache.felix.framework.resolver.Wire;
-import org.apache.felix.framework.resolver.CandidateComparator;
-import org.apache.felix.framework.resolver.ResolveException;
-import org.apache.felix.framework.resolver.Resolver;
-import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.util.manifestparser.R4Library;
-import org.osgi.framework.BundlePermission;
-import org.osgi.framework.PackagePermission;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-
-public class FelixResolverState implements Resolver.ResolverState
-{
- private final Logger m_logger;
- // List of all modules.
- private final List<Module> m_modules;
- // Capability sets.
- private final Map<String, CapabilitySet> m_capSets;
- // Maps fragment symbolic names to list of fragment modules sorted by version.
- private final Map<String, List<Module>> m_fragmentMap = new HashMap<String, List<Module>>();
- // Maps singleton symbolic names to list of modules sorted by version.
- private final Map<String, List<Module>> m_singletons = new HashMap<String, List<Module>>();
- // Execution environment.
- private final String m_fwkExecEnvStr;
- // Parsed framework environments
- private final Set<String> m_fwkExecEnvSet;
-
- public FelixResolverState(Logger logger, String fwkExecEnvStr)
- {
- m_logger = logger;
- m_modules = new ArrayList<Module>();
- m_capSets = new HashMap<String, CapabilitySet>();
-
- m_fwkExecEnvStr = (fwkExecEnvStr != null) ? fwkExecEnvStr.trim() : null;
- m_fwkExecEnvSet = parseExecutionEnvironments(fwkExecEnvStr);
-
- List<String> indices = new ArrayList<String>();
- indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
- m_capSets.put(Capability.MODULE_NAMESPACE, new CapabilitySet(indices, true));
-
- indices = new ArrayList<String>();
- indices.add(Capability.PACKAGE_ATTR);
- m_capSets.put(Capability.PACKAGE_NAMESPACE, new CapabilitySet(indices, true));
-
- indices = new ArrayList<String>();
- indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
- m_capSets.put(Capability.HOST_NAMESPACE, new CapabilitySet(indices, true));
- }
-
- public synchronized void addModule(Module module)
- {
- if (isSingleton(module))
- {
- // Find the currently selected singleton, which is either the
- // highest version or the resolved one.
- List<Module> modules = m_singletons.get(module.getSymbolicName());
- // Get the highest version.
- Module current = ((modules != null) && !modules.isEmpty()) ? modules.get(0) : null;
- // Now check to see if there is a resolved one instead.
- for (int i = 0; (modules != null) && (i < modules.size()); i++)
- {
- if (modules.get(i).isResolved())
- {
- current = modules.get(i);
- }
- }
-
- // Index the new singleton.
- Module highest = indexModule(m_singletons, module);
- // If the currently selected singleton is not resolved and
- // the newly added singleton is a higher version, then select
- // it instead.
- if ((current != null) && !current.isResolved() && (current != highest))
- {
- if (Util.isFragment(current))
- {
- removeFragment(current);
- }
- else
- {
- removeHost(current);
- }
- }
- else if (current != null)
- {
- module = null;
- }
- }
-
- if ((module != null) && Util.isFragment(module))
- {
- addFragment(module);
- }
- else if (module != null)
- {
- addHost(module);
- }
- }
-
- public synchronized void removeModule(Module module)
- {
- // If this module is a singleton, then remove it from the
- // singleton map.
- List<Module> modules = m_singletons.get(module.getSymbolicName());
- if (modules != null)
- {
- modules.remove(module);
- if (modules.isEmpty())
- {
- m_singletons.remove(module.getSymbolicName());
- }
- }
-
- if (Util.isFragment(module))
- {
- removeFragment(module);
- }
- else
- {
- removeHost(module);
- }
- }
-
- public void detachFragment(Module host, Module fragment)
- {
- List<Module> fragments = new ArrayList<Module>(((ModuleImpl) host).getFragments());
- fragments.remove(fragment);
- removeFragment(fragment);
- try
- {
- ((ModuleImpl) host).attachFragments(fragments);
- }
- catch (Exception ex)
- {
- // Try to clean up by removing all fragments.
- try
- {
- ((ModuleImpl) host).attachFragments(null);
- }
- catch (Exception ex2)
- {
- // Ignore
- }
- m_logger.log(host.getBundle(), Logger.LOG_ERROR,
- "Serious error attaching fragments.", ex);
- }
- }
-
- public void checkSingleton(Module module)
- {
- // Check if this module is a singleton.
- List<Module> modules = m_singletons.get(module.getSymbolicName());
- if ((modules != null) && modules.contains(module))
- {
- // If it is, check if there is already a resolved singleton.
- for (Module mod : modules)
- {
- if (mod.isResolved())
- {
- throw new ResolveException(
- "Only one singleton can be resolved at a time.", null, null);
- }
- }
-
- // If not, check to see if it is the selected singleton.
- Module current = (modules.size() > 0) ? modules.get(0) : null;
- if ((current != null) && (current != module))
- {
- // If it is not the selected singleton, remove the selected
- // singleton and select the specified one instead.
- if (Util.isFragment(current))
- {
- removeFragment(current);
- }
- else
- {
- removeHost(current);
- }
- if (Util.isFragment(module))
- {
- addFragment(module);
- }
- else
- {
- addHost(module);
- }
- }
- }
- }
-
- private void addFragment(Module fragment)
- {
-// TODO: FRAGMENT - This should check to make sure that the host allows fragments.
- indexModule(m_fragmentMap, fragment);
-
- // Loop through all matching hosts seeing if we should attach the
- // new fragment. We should attach the new fragment if the existing
- // unresolved host doesn't currently have a fragment of the same
- // symbolic name attached to it or if the currently attached fragment
- // is a lower version.
- Set<Capability> hostCaps = getMatchingHostCapabilities(fragment);
- for (Capability cap : hostCaps)
- {
- Module host = cap.getModule();
-
- // Get the fragments currently attached to the host so we
- // can remove the older version of the current fragment, if any.
- List<Module> fragments = ((ModuleImpl) host).getFragments();
- Module attachedFragment = null;
- for (int fragIdx = 0;
- (fragments != null) && (attachedFragment == null) && (fragIdx < fragments.size());
- fragIdx++)
- {
- if (fragments.get(fragIdx).getSymbolicName()
- .equals(fragment.getSymbolicName()))
- {
- attachedFragment = fragments.get(fragIdx);
- }
- }
-
- if ((attachedFragment == null)
- || (attachedFragment.getVersion().compareTo(fragment.getVersion()) <= 0))
- {
- // Create a copy of the fragment list and remove the attached
- // fragment, if necessary.
- List<Module> newFragments = (fragments == null)
- ? new ArrayList<Module>()
- : new ArrayList<Module>(fragments);
- if (attachedFragment != null)
- {
- newFragments.remove(attachedFragment);
- }
-
- // Now add the new fragment in bundle ID order.
- int index = -1;
- for (int listIdx = 0;
- (index < 0) && (listIdx < newFragments.size());
- listIdx++)
- {
- Module f = newFragments.get(listIdx);
- if (fragment.getBundle().getBundleId()
- < f.getBundle().getBundleId())
- {
- index = listIdx;
- }
- }
- newFragments.add(
- (index < 0) ? newFragments.size() : index, fragment);
-
- // Remove host's existing exported packages from index.
- List<Capability> caps = host.getCapabilities();
- removeCapabilities(caps);
-
- // Attach the new fragments to the host.
- fragments = (newFragments.isEmpty()) ? null : newFragments;
- try
- {
- ((ModuleImpl) host).attachFragments(fragments);
- }
- catch (Exception ex)
- {
- // Try to clean up by removing all fragments.
- try
- {
- ((ModuleImpl) host).attachFragments(null);
- }
- catch (Exception ex2)
- {
- // Ignore
- }
- m_logger.log(host.getBundle(), Logger.LOG_ERROR,
- "Serious error attaching fragments.", ex);
- }
-
- // Reindex the host's exported packages.
- caps = host.getCapabilities();
- addCapabilities(caps);
- }
- }
- }
-
- private void removeFragment(Module fragment)
- {
- // Get fragment list, which may be null for system bundle fragments.
- List<Module> fragList = m_fragmentMap.get(fragment.getSymbolicName());
- if (fragList != null)
- {
- // Remove from fragment map.
- fragList.remove(fragment);
- if (fragList.isEmpty())
- {
- m_fragmentMap.remove(fragment.getSymbolicName());
- }
-
- // If we have any matching hosts, then attempt to remove the
- // fragment from any merged hosts still in the installed state.
- Set<Capability> hostCaps = getMatchingHostCapabilities(fragment);
- for (Capability hostCap : hostCaps)
- {
- Module host = hostCap.getModule();
-
- // Check to see if the removed fragment was actually merged with
- // the host, since it might not be if it wasn't the highest version.
- // If it was, recalculate the fragments for the host.
- List<Module> fragments = ((ModuleImpl) host).getFragments();
- if ((fragments != null) && fragments.contains(fragment))
- {
- List<Module> fragmentList = getMatchingFragments(host);
-
- // Remove host's existing exported packages from index.
- List<Capability> caps = host.getCapabilities();
- removeCapabilities(caps);
-
- // Attach the fragments to the host.
- try
- {
- ((ModuleImpl) host).attachFragments(fragmentList);
- }
- catch (Exception ex)
- {
- // Try to clean up by removing all fragments.
- try
- {
- ((ModuleImpl) host).attachFragments(null);
- }
- catch (Exception ex2)
- {
- // Ignore
- }
- m_logger.log(host.getBundle(), Logger.LOG_ERROR,
- "Serious error attaching fragments.", ex);
- }
-
- // Reindex the host's exported packages.
- caps = host.getCapabilities();
- addCapabilities(caps);
- }
- }
- }
- }
-
- private void addCapabilities(List<Capability> caps)
- {
- if (caps != null)
- {
- for (Capability cap : caps)
- {
- CapabilitySet capSet = m_capSets.get(cap.getNamespace());
- if (capSet == null)
- {
- capSet = new CapabilitySet(null, true);
- m_capSets.put(cap.getNamespace(), capSet);
- }
- capSet.addCapability(cap);
- }
- }
- }
-
- private void removeCapabilities(List<Capability> caps)
- {
- if (caps != null)
- {
- for (Capability cap : caps)
- {
- CapabilitySet capSet = m_capSets.get(cap.getNamespace());
- if (capSet != null)
- {
- capSet.removeCapability(cap);
- }
- }
- }
- }
-
- public void unmergeFragment(Module fragment)
- {
- if (!Util.isFragment(fragment))
- {
- return;
- }
-
- removeFragment(fragment);
- }
-
- private Set<Capability> getMatchingHostCapabilities(Module fragment)
- {
- // Find the fragment's host requirement.
- Requirement hostReq = getFragmentHostRequirement(fragment);
-
- // Create a list of all matching hosts for this fragment.
- SecurityManager sm = System.getSecurityManager();
- if ((sm != null) && (fragment.getSymbolicName() != null))
- {
- if (!((BundleProtectionDomain) fragment.getSecurityContext()).impliesDirect(
- new BundlePermission(fragment.getSymbolicName(), BundlePermission.FRAGMENT)))
- {
- return new HashSet<Capability>();
- }
- }
-
- Set<Capability> hostCaps =
- m_capSets.get(Capability.HOST_NAMESPACE).match(hostReq.getFilter(), true);
-
- for (Iterator<Capability> it = hostCaps.iterator(); it.hasNext(); )
- {
- Capability hostCap = it.next();
-
- // Only look at unresolved hosts, since we don't support
- // dynamic attachment of fragments.
-// TODO: FELIX3 - This is potentially too narrow, since it won't allow
-// attaching with updated modules.
- if (hostCap.getModule().isResolved()
- || ((BundleImpl) hostCap.getModule().getBundle()).isStale()
- || ((BundleImpl) hostCap.getModule().getBundle()).isRemovalPending())
- {
- it.remove();
- }
- else if ((sm != null) && (hostCap.getModule().getSymbolicName() != null))
- {
- if (!((BundleProtectionDomain) hostCap.getModule()
- .getSecurityContext()).impliesDirect(
- new BundlePermission(hostCap.getModule().getSymbolicName(),
- BundlePermission.HOST)))
- {
- it.remove();
- }
- }
- }
-
- return hostCaps;
- }
-
- private void addHost(Module host)
- {
- // When a module is added, we first need to pre-merge any potential fragments
- // into the host and then second create an aggregated list of unresolved
- // capabilities to simplify later processing when resolving bundles.
- m_modules.add(host);
- List<Capability> caps = Util.getCapabilityByNamespace(host, Capability.HOST_NAMESPACE);
- if (caps.size() > 0)
- {
- m_capSets.get(Capability.HOST_NAMESPACE).addCapability(caps.get(0));
- }
-
- //
- // First, merge applicable fragments.
- //
-
- List<Module> fragments = getMatchingFragments(host);
-
- // Attach any fragments we found for this host.
- if (fragments.size() > 0)
- {
- // Attach the fragments to the host.
- try
- {
- ((ModuleImpl) host).attachFragments(fragments);
- }
- catch (Exception ex)
- {
- // Try to clean up by removing all fragments.
- try
- {
- ((ModuleImpl) host).attachFragments(null);
- }
- catch (Exception ex2)
- {
- // Ignore
- }
- m_logger.log(host.getBundle(), Logger.LOG_ERROR,
- "Serious error attaching fragments.", ex);
- }
- }
-
- //
- // Second, index module's capabilities.
- //
-
- caps = host.getCapabilities();
-
- // Add exports to unresolved package map.
- addCapabilities(caps);
- }
-
- private void removeHost(Module host)
- {
- // We need remove the host's exports from the "resolved" and
- // "unresolved" package maps, remove its dependencies on fragments
- // and exporters, and remove it from the module list.
- m_modules.remove(host);
- List<Capability> caps = Util.getCapabilityByNamespace(host, Capability.HOST_NAMESPACE);
- if (caps.size() > 0)
- {
- m_capSets.get(Capability.HOST_NAMESPACE).removeCapability(caps.get(0));
- }
-
- // Remove exports from package maps.
- caps = host.getCapabilities();
- removeCapabilities(caps);
-
- // Set fragments to null, which will remove the module from all
- // of its dependent fragment modules.
- try
- {
- ((ModuleImpl) host).attachFragments(null);
- }
- catch (Exception ex)
- {
- m_logger.log(
- host.getBundle(), Logger.LOG_ERROR, "Error detaching fragments.", ex);
- }
- // Set wires to null, which will remove the module from all
- // of its dependent modules.
- ((ModuleImpl) host).setWires(null);
- }
-
- private List<Module> getMatchingFragments(Module host)
- {
- // Find the host capability for the current host.
- List<Capability> caps = Util.getCapabilityByNamespace(host, Capability.HOST_NAMESPACE);
- Capability hostCap = (caps.isEmpty()) ? null : caps.get(0);
-
- // If we have a host capability, then loop through all fragments trying to
- // find ones that match.
- List<Module> fragmentList = new ArrayList<Module>();
- SecurityManager sm = System.getSecurityManager();
- if ((sm != null) && (host.getSymbolicName() != null))
- {
- if (!((BundleProtectionDomain) host.getSecurityContext()).impliesDirect(
- new BundlePermission(host.getSymbolicName(), BundlePermission.HOST)))
- {
- return fragmentList;
- }
- }
- for (Iterator it = m_fragmentMap.entrySet().iterator(); (hostCap != null) && it.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) it.next();
- List fragments = (List) entry.getValue();
- Module fragment = null;
- for (int i = 0; (fragment == null) && (i < fragments.size()); i++)
- {
-// TODO: FELIX3 - This is potentially too narrow, since it won't allow
-// attaching with updated modules.
- Module f = (Module) fragments.get(i);
- if (!((BundleImpl) f.getBundle()).isStale()
- && !((BundleImpl) f.getBundle()).isRemovalPending())
- {
- fragment = f;
- }
- }
-
- if (fragment == null)
- {
- continue;
- }
-
- if ((sm != null) && (fragment.getSymbolicName() != null))
- {
- if (!((BundleProtectionDomain) fragment.getSecurityContext()).impliesDirect(
- new BundlePermission(fragment.getSymbolicName(), BundlePermission.FRAGMENT)))
- {
- continue;
- }
- }
- Requirement hostReq = getFragmentHostRequirement(fragment);
-
- // If we have a host requirement, then loop through each host and
- // see if it matches the host requirement.
- if ((hostReq != null) && CapabilitySet.matches(hostCap, hostReq.getFilter()))
- {
- // Now add the new fragment in bundle ID order.
- int index = -1;
- for (int listIdx = 0;
- (index < 0) && (listIdx < fragmentList.size());
- listIdx++)
- {
- Module existing = fragmentList.get(listIdx);
- if (fragment.getBundle().getBundleId()
- < existing.getBundle().getBundleId())
- {
- index = listIdx;
- }
- }
- fragmentList.add(
- (index < 0) ? fragmentList.size() : index, fragment);
- }
- }
-
- return fragmentList;
- }
-
- public synchronized Module findHost(Module rootModule) throws ResolveException
- {
- Module newRootModule = rootModule;
- if (Util.isFragment(rootModule))
- {
- Set<Capability> hostCaps = getMatchingHostCapabilities(rootModule);
- Module currentBestHost = null;
- for (Capability hostCap : hostCaps)
- {
- Module host = hostCap.getModule();
- if (currentBestHost == null)
- {
- currentBestHost = host;
- }
- else if (currentBestHost.getVersion().compareTo(host.getVersion()) < 0)
- {
- currentBestHost = host;
- }
- }
- newRootModule = currentBestHost;
-
- if (newRootModule == null)
- {
- throw new ResolveException(
- "Unable to find host.", rootModule, getFragmentHostRequirement(rootModule));
- }
- }
-
- return newRootModule;
- }
-
- private static Requirement getFragmentHostRequirement(Module fragment)
- {
- // Find the fragment's host requirement.
- List<Requirement> reqs = fragment.getRequirements();
- Requirement hostReq = null;
- for (int reqIdx = 0; (hostReq == null) && (reqIdx < reqs.size()); reqIdx++)
- {
- if (reqs.get(reqIdx).getNamespace().equals(Capability.HOST_NAMESPACE))
- {
- hostReq = reqs.get(reqIdx);
- }
- }
- return hostReq;
- }
-
- /**
- * This method is used for installing system bundle extensions. It actually
- * refreshes the system bundle module's capabilities in the resolver state
- * to capture additional capabilities.
- * @param module The module being refresh, which should always be the system bundle.
- **/
- synchronized void refreshSystemBundleModule(Module module)
- {
- // The system bundle module should always be resolved, so we only need
- // to update the resolved capability map.
- List<Capability> caps = module.getCapabilities();
- addCapabilities(caps);
- }
-
- public synchronized void moduleResolved(Module module)
- {
- if (module.isResolved())
- {
- // Loop through the module's package wires and determine if any
- // of them overlap any of the packages exported by the module.
- // If so, then the framework must have chosen to have the module
- // import rather than export the package, so we need to remove the
- // corresponding package capability from the package capability set.
- List<Wire> wires = module.getWires();
- List<Capability> caps = module.getCapabilities();
- for (int wireIdx = 0; (wires != null) && (wireIdx < wires.size()); wireIdx++)
- {
- Wire wire = wires.get(wireIdx);
- if (wire.getCapability().getNamespace().equals(Capability.PACKAGE_NAMESPACE))
- {
- for (int capIdx = 0;
- (caps != null) && (capIdx < caps.size());
- capIdx++)
- {
- if (caps.get(capIdx).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
- && wire.getCapability().getAttribute(Capability.PACKAGE_ATTR).getValue()
- .equals(caps.get(capIdx).getAttribute(Capability.PACKAGE_ATTR).getValue()))
- {
- m_capSets.get(Capability.PACKAGE_NAMESPACE).removeCapability(caps.get(capIdx));
- break;
- }
- }
- }
- }
- }
- }
-
- public Set<Capability> getCandidates(Module module, Requirement req, boolean obeyMandatory)
- {
- Set<Capability> result = new TreeSet<Capability>(new CandidateComparator());
-
- CapabilitySet capSet = m_capSets.get(req.getNamespace());
- if (capSet != null)
- {
- Set<Capability> matches = capSet.match(req.getFilter(), obeyMandatory);
- if (System.getSecurityManager() != null)
- {
- for (Capability cap : matches)
- {
- if (req.getNamespace().equals(Capability.PACKAGE_NAMESPACE) && (
- !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
- new PackagePermission((String) cap.getAttribute(Capability.PACKAGE_ATTR).getValue(),
- PackagePermission.EXPORTONLY)) ||
- !((module == null) ||
- ((BundleProtectionDomain) module.getSecurityContext()).impliesDirect(
- new PackagePermission((String) cap.getAttribute(Capability.PACKAGE_ATTR).getValue(),
- cap.getModule().getBundle(),PackagePermission.IMPORT))
- )))
- {
- if (module != cap.getModule())
- {
- continue;
- }
- }
- if (req.getNamespace().equals(Capability.MODULE_NAMESPACE) && (
- !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
- new BundlePermission(cap.getModule().getSymbolicName(), BundlePermission.PROVIDE)) ||
- !((module == null) ||
- ((BundleProtectionDomain) module.getSecurityContext()).impliesDirect(
- new BundlePermission(module.getSymbolicName(), BundlePermission.REQUIRE))
- )))
- {
- continue;
- }
-
- result.add(cap);
- }
- }
- else
- {
- result.addAll(matches);
- }
- }
-
- return result;
- }
-
- /**
- * Checks to see if the passed in module's required execution environment
- * is provided by the framework.
- * @param module The module whose required execution environment is to be to verified.
- * @throws ResolveException if the module's required execution environment does
- * not match the framework's supported execution environment.
- **/
- public void checkExecutionEnvironment(Module module) throws ResolveException
- {
- String bundleExecEnvStr = (String)
- module.getHeaders().get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
- if (bundleExecEnvStr != null)
- {
- bundleExecEnvStr = bundleExecEnvStr.trim();
-
- // If the bundle has specified an execution environment and the
- // framework has an execution environment specified, then we must
- // check for a match.
- if (!bundleExecEnvStr.equals("")
- && (m_fwkExecEnvStr != null)
- && (m_fwkExecEnvStr.length() > 0))
- {
- StringTokenizer tokens = new StringTokenizer(bundleExecEnvStr, ",");
- boolean found = false;
- while (tokens.hasMoreTokens() && !found)
- {
- if (m_fwkExecEnvSet.contains(tokens.nextToken().trim()))
- {
- found = true;
- }
- }
- if (!found)
- {
- throw new ResolveException(
- "Execution environment not supported: "
- + bundleExecEnvStr, module, null);
- }
- }
- }
- }
-
- public void checkNativeLibraries(Module module) throws ResolveException
- {
- // Next, try to resolve any native code, since the module is
- // not resolvable if its native code cannot be loaded.
- List<R4Library> libs = module.getNativeLibraries();
- if (libs != null)
- {
- String msg = null;
- // Verify that all native libraries exist in advance; this will
- // throw an exception if the native library does not exist.
- for (int libIdx = 0; (msg == null) && (libIdx < libs.size()); libIdx++)
- {
- String entryName = libs.get(libIdx).getEntryName();
- if (entryName != null)
- {
- if (!module.getContent().hasEntry(entryName))
- {
- msg = "Native library does not exist: " + entryName;
- }
- }
- }
- // If we have a zero-length native library array, then
- // this means no native library class could be selected
- // so we should fail to resolve.
- if (libs.isEmpty())
- {
- msg = "No matching native libraries found.";
- }
- if (msg != null)
- {
- throw new ResolveException(msg, module, null);
- }
- }
- }
-
- //
- // Utility methods.
- //
-
- /**
- * Updates the framework wide execution environment string and a cached Set of
- * execution environment tokens from the comma delimited list specified by the
- * system variable 'org.osgi.framework.executionenvironment'.
- * @param fwkExecEnvStr Comma delimited string of provided execution environments
- * @return the parsed set of execution environments
- **/
- private static Set<String> parseExecutionEnvironments(String fwkExecEnvStr)
- {
- Set<String> newSet = new HashSet<String>();
- if (fwkExecEnvStr != null)
- {
- StringTokenizer tokens = new StringTokenizer(fwkExecEnvStr, ",");
- while (tokens.hasMoreTokens())
- {
- newSet.add(tokens.nextToken().trim());
- }
- }
- return newSet;
- }
-
- /**
- * 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(Module module)
- {
- final List<Capability> modCaps =
- Util.getCapabilityByNamespace(
- module, Capability.MODULE_NAMESPACE);
- if (modCaps == null || modCaps.isEmpty())
- {
- return false;
- }
- final List<Directive> dirs = modCaps.get(0).getDirectives();
- for (int dirIdx = 0; (dirs != null) && (dirIdx < dirs.size()); dirIdx++)
- {
- if (dirs.get(dirIdx).getName().equalsIgnoreCase(Constants.SINGLETON_DIRECTIVE)
- && Boolean.valueOf((String) dirs.get(dirIdx).getValue()))
- {
- return true;
- }
- }
- return false;
- }
-
- private static Module indexModule(Map<String, List<Module>> map, Module module)
- {
- List<Module> modules = map.get(module.getSymbolicName());
-
- // 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 ArrayList<Module>();
- modules.add(module);
- }
- else
- {
- Version version = module.getVersion();
- int top = 0, bottom = modules.size() - 1;
- while (top <= bottom)
- {
- int middle = (bottom - top) / 2 + top;
- Version middleVersion = modules.get(middle).getVersion();
- // 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 = modules.get(middle).getBundle().getBundleId();
- long exportId = module.getBundle().getBundleId();
- if (middleId < exportId)
- {
- top = middle + 1;
- }
- else
- {
- bottom = middle - 1;
- }
- }
- else
- {
- top = middle + 1;
- }
- }
-
- // Ignore duplicates.
- if ((top >= modules.size()) || (modules.get(top) != module))
- {
- modules.add(top, module);
- }
- }
-
- map.put(module.getSymbolicName(), modules);
-
- return modules.get(0);
- }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
index 22f9bf6..d94b8ed 100644
--- a/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
@@ -38,8 +38,10 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
-import org.apache.felix.framework.Felix.FelixResolver;
+import org.apache.felix.framework.Felix.StatefulResolver;
import org.apache.felix.framework.cache.JarContent;
import org.apache.felix.framework.capabilityset.Attribute;
import org.apache.felix.framework.capabilityset.Capability;
@@ -72,7 +74,7 @@
{
private final Logger m_logger;
private final Map m_configMap;
- private final FelixResolver m_resolver;
+ private final StatefulResolver m_resolver;
private final String m_id;
private final Content m_content;
private final Map m_headerMap;
@@ -201,7 +203,7 @@
}
public ModuleImpl(
- Logger logger, Map configMap, FelixResolver resolver,
+ Logger logger, Map configMap, StatefulResolver resolver,
Bundle bundle, String id, Map headerMap, Content content,
URLStreamHandler streamHandler, String[] bootPkgs,
boolean[] bootPkgWildcards)
@@ -473,10 +475,20 @@
public synchronized void setWires(List<Wire> wires)
{
+ // This not only sets the wires for the module, but it also records
+ // the dependencies this module has on other modules (i.e., the provider
+ // end of the wire) to simplify bookkeeping.
+
+ // Fragments don't depend on other their hosts, so we just record the
+ // wires for informational purposes.
+// TODO: FRAGMENT RESOLVER - It is possible for a fragment to get more wires
+// later if it is attached to more hosts. We need to deal with that.
+ boolean isFragment = Util.isFragment(this);
+
// Remove module from old wire modules' dependencies,
// since we are no longer dependent on any the moduels
// from the old wires.
- for (int i = 0; (m_wires != null) && (i < m_wires.size()); i++)
+ for (int i = 0; !isFragment && (m_wires != null) && (i < m_wires.size()); i++)
{
if (m_wires.get(i).getCapability().getNamespace().equals(Capability.MODULE_NAMESPACE))
{
@@ -491,7 +503,7 @@
m_wires = wires;
// Add ourself as a dependent to the new wires' modules.
- for (int i = 0; (m_wires != null) && (i < m_wires.size()); i++)
+ for (int i = 0; !isFragment && (m_wires != null) && (i < m_wires.size()); i++)
{
if (m_wires.get(i).getCapability().getNamespace().equals(Capability.MODULE_NAMESPACE))
{
@@ -514,6 +526,27 @@
m_isResolved = true;
}
+
+ public synchronized void setSecurityContext(Object securityContext)
+ {
+ m_protectionDomain = (ProtectionDomain) securityContext;
+ }
+
+ public synchronized Object getSecurityContext()
+ {
+ return m_protectionDomain;
+ }
+
+ // TODO: FRAGMENT RESOLVER - Technically, this is only necessary for fragments.
+ // When we refactoring for the new R4.3 framework API, we'll have to see
+ // if this is still necessary, since the new BundleWirings API will give
+ // us another way to detect it.
+ public boolean isRemovalPending()
+ {
+ return (m_bundle.getState() == Bundle.UNINSTALLED)
+ || (this != ((BundleImpl) m_bundle).getCurrentModule());
+ }
+
//
// Content access methods.
//
@@ -623,7 +656,7 @@
// If there is nothing on the class path, then include
// "." by default, as per the spec.
- if (localContentList.size() == 0)
+ if (localContentList.isEmpty())
{
localContentList.add(content);
}
@@ -1156,6 +1189,25 @@
((ModuleImpl) m_fragments.get(i)).removeDependentHost(this);
}
+ // Close previous fragment contents.
+ for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
+ {
+ m_fragmentContents[i].close();
+ }
+ m_fragmentContents = null;
+
+ // Close the old content path, since we'll need to recalculate it for
+ // for the added (or removed) fragments.
+ for (int i = 0; (m_contentPath != null) && (i < m_contentPath.length); i++)
+ {
+ // Don't close this module's content, if it is on the content path.
+ if (m_content != m_contentPath[i])
+ {
+ m_contentPath[i].close();
+ }
+ }
+ m_contentPath = null;
+
// Remove cached capabilities and requirements.
m_cachedCapabilities = null;
m_cachedRequirements = null;
@@ -1164,48 +1216,37 @@
// Update the dependencies on the new fragments.
m_fragments = fragments;
- // We need to add ourself as a dependent of each fragment
- // module. We also need to create an array of fragment contents
- // to attach to our content loader.
+ // We need to sort the fragments and add ourself as a dependent of each one.
+ // We also need to create an array of fragment contents to attach to our
+ // content path.
if (m_fragments != null)
{
- Content[] fragmentContents = new Content[m_fragments.size()];
+ // Sort fragments according to ID order, if necessary.
+ // Note that this sort order isn't 100% correct since
+ // it uses a string, but it is likely close enough and
+ // avoids having to create more objects.
+ if (m_fragments.size() > 1)
+ {
+ SortedMap<String, Module> sorted = new TreeMap<String, Module>();
+ for (Module f : m_fragments)
+ {
+ sorted.put(f.getId(), f);
+ }
+ m_fragments = new ArrayList(sorted.values());
+ }
+ m_fragmentContents = new Content[m_fragments.size()];
for (int i = 0; (m_fragments != null) && (i < m_fragments.size()); i++)
{
((ModuleImpl) m_fragments.get(i)).addDependentHost(this);
- fragmentContents[i] =
+ m_fragmentContents[i] =
m_fragments.get(i).getContent()
.getEntryAsContent(FelixConstants.CLASS_PATH_DOT);
}
- // Now attach the fragment contents to our content loader.
- attachFragmentContents(fragmentContents);
+ // Recalculate the content path for the new fragments.
+ m_contentPath = initializeContentPath();
}
}
- // This must be called holding the object lock.
- private void attachFragmentContents(Content[] fragmentContents)
- throws Exception
- {
- // Close existing fragment contents.
- if (m_fragmentContents != null)
- {
- for (int i = 0; i < m_fragmentContents.length; i++)
- {
- m_fragmentContents[i].close();
- }
- }
- m_fragmentContents = fragmentContents;
-
- if (m_contentPath != null)
- {
- for (int i = 0; i < m_contentPath.length; i++)
- {
- m_contentPath[i].close();
- }
- }
- m_contentPath = initializeContentPath();
- }
-
public synchronized List<Module> getDependentHosts()
{
return m_dependentHosts;
@@ -1277,23 +1318,15 @@
{
m_contentPath[i].close();
}
+ m_contentPath = null;
for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
{
m_fragmentContents[i].close();
}
+ m_fragmentContents = null;
m_classLoader = null;
}
- public synchronized void setSecurityContext(Object securityContext)
- {
- m_protectionDomain = (ProtectionDomain) securityContext;
- }
-
- public synchronized Object getSecurityContext()
- {
- return m_protectionDomain;
- }
-
public String toString()
{
return m_id;
@@ -2150,7 +2183,7 @@
}
private static String diagnoseClassLoadError(
- FelixResolver resolver, ModuleImpl module, String name)
+ StatefulResolver resolver, ModuleImpl module, String name)
{
// We will try to do some diagnostics here to help the developer
// deal with this exception.
diff --git a/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java b/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java
new file mode 100644
index 0000000..7143fb6
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.framework.capabilityset.Requirement;
+import org.apache.felix.framework.resolver.CandidateComparator;
+import org.apache.felix.framework.resolver.Module;
+import org.apache.felix.framework.resolver.ResolveException;
+import org.apache.felix.framework.resolver.Resolver;
+import org.apache.felix.framework.resolver.Wire;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.R4Library;
+import org.osgi.framework.BundlePermission;
+import org.osgi.framework.Constants;
+import org.osgi.framework.PackagePermission;
+
+class ResolverStateImpl implements Resolver.ResolverState
+{
+ private final Logger m_logger;
+ // Set of all modules.
+ private final Set<Module> m_modules;
+ // Set of all fragments.
+ private final Set<Module> m_fragments;
+ // Capability sets.
+ private final Map<String, CapabilitySet> m_capSets;
+ // Execution environment.
+ private final String m_fwkExecEnvStr;
+ // Parsed framework environments
+ private final Set<String> m_fwkExecEnvSet;
+
+ ResolverStateImpl(Logger logger, String fwkExecEnvStr)
+ {
+ m_logger = logger;
+ m_modules = new HashSet<Module>();
+ m_fragments = new HashSet<Module>();
+ m_capSets = new HashMap<String, CapabilitySet>();
+
+ m_fwkExecEnvStr = (fwkExecEnvStr != null) ? fwkExecEnvStr.trim() : null;
+ m_fwkExecEnvSet = parseExecutionEnvironments(fwkExecEnvStr);
+
+ List<String> indices = new ArrayList<String>();
+ indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+ m_capSets.put(Capability.MODULE_NAMESPACE, new CapabilitySet(indices, true));
+
+ indices = new ArrayList<String>();
+ indices.add(Capability.PACKAGE_ATTR);
+ m_capSets.put(Capability.PACKAGE_NAMESPACE, new CapabilitySet(indices, true));
+
+ indices = new ArrayList<String>();
+ indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+ m_capSets.put(Capability.HOST_NAMESPACE, new CapabilitySet(indices, true));
+ }
+
+ synchronized void addModule(Module m)
+ {
+ m_modules.add(m);
+ List<Capability> caps = m.getCapabilities();
+ if (caps != null)
+ {
+ for (Capability cap : caps)
+ {
+ CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+ if (capSet == null)
+ {
+ capSet = new CapabilitySet(null, true);
+ m_capSets.put(cap.getNamespace(), capSet);
+ }
+ capSet.addCapability(cap);
+ }
+ }
+
+ if (Util.isFragment(m))
+ {
+ m_fragments.add(m);
+ }
+ }
+
+ synchronized void removeModule(Module m)
+ {
+ m_modules.remove(m);
+ List<Capability> caps = m.getCapabilities();
+ if (caps != null)
+ {
+ for (Capability cap : caps)
+ {
+ CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+ if (capSet != null)
+ {
+ capSet.removeCapability(cap);
+ }
+ }
+ }
+
+ if (Util.isFragment(m))
+ {
+ m_fragments.remove(m);
+ }
+ }
+
+ synchronized Set<Module> getFragments()
+ {
+ return new HashSet(m_fragments);
+ }
+
+ synchronized void removeSubstitutedCapabilities(Module module)
+ {
+ if (module.isResolved())
+ {
+ // Loop through the module's package wires and determine if any
+ // of them overlap any of the packages exported by the module.
+ // If so, then the framework must have chosen to have the module
+ // import rather than export the package, so we need to remove the
+ // corresponding package capability from the package capability set.
+ List<Wire> wires = module.getWires();
+ List<Capability> caps = module.getCapabilities();
+ for (int wireIdx = 0; (wires != null) && (wireIdx < wires.size()); wireIdx++)
+ {
+ Wire wire = wires.get(wireIdx);
+ if (wire.getCapability().getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ for (int capIdx = 0;
+ (caps != null) && (capIdx < caps.size());
+ capIdx++)
+ {
+ if (caps.get(capIdx).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
+ && wire.getCapability().getAttribute(Capability.PACKAGE_ATTR).getValue()
+ .equals(caps.get(capIdx).getAttribute(Capability.PACKAGE_ATTR).getValue()))
+ {
+ m_capSets.get(Capability.PACKAGE_NAMESPACE).removeCapability(caps.get(capIdx));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // ResolverState methods.
+ //
+
+ public synchronized SortedSet<Capability> getCandidates(
+ Module module, Requirement req, boolean obeyMandatory)
+ {
+ SortedSet<Capability> result = new TreeSet<Capability>(new CandidateComparator());
+
+ CapabilitySet capSet = m_capSets.get(req.getNamespace());
+ if (capSet != null)
+ {
+ Set<Capability> matches = capSet.match(req.getFilter(), obeyMandatory);
+ for (Capability cap : matches)
+ {
+ if (System.getSecurityManager() != null)
+ {
+ if (req.getNamespace().equals(Capability.PACKAGE_NAMESPACE) && (
+ !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
+ new PackagePermission((String) cap.getAttribute(Capability.PACKAGE_ATTR).getValue(),
+ PackagePermission.EXPORTONLY)) ||
+ !((module == null) ||
+ ((BundleProtectionDomain) module.getSecurityContext()).impliesDirect(
+ new PackagePermission((String) cap.getAttribute(Capability.PACKAGE_ATTR).getValue(),
+ cap.getModule().getBundle(),PackagePermission.IMPORT))
+ )))
+ {
+ if (module != cap.getModule())
+ {
+ continue;
+ }
+ }
+ else if (req.getNamespace().equals(Capability.MODULE_NAMESPACE) && (
+ !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
+ new BundlePermission(cap.getModule().getSymbolicName(), BundlePermission.PROVIDE)) ||
+ !((module == null) ||
+ ((BundleProtectionDomain) module.getSecurityContext()).impliesDirect(
+ new BundlePermission(module.getSymbolicName(), BundlePermission.REQUIRE))
+ )))
+ {
+ continue;
+ }
+ }
+ else if (req.getNamespace().equals(Capability.HOST_NAMESPACE)
+ && cap.getModule().isResolved())
+ {
+ continue;
+ }
+
+ result.add(cap);
+ }
+ }
+
+ return result;
+ }
+
+ public void checkExecutionEnvironment(Module module) throws ResolveException
+ {
+ String bundleExecEnvStr = (String)
+ module.getHeaders().get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
+ if (bundleExecEnvStr != null)
+ {
+ bundleExecEnvStr = bundleExecEnvStr.trim();
+
+ // If the bundle has specified an execution environment and the
+ // framework has an execution environment specified, then we must
+ // check for a match.
+ if (!bundleExecEnvStr.equals("")
+ && (m_fwkExecEnvStr != null)
+ && (m_fwkExecEnvStr.length() > 0))
+ {
+ StringTokenizer tokens = new StringTokenizer(bundleExecEnvStr, ",");
+ boolean found = false;
+ while (tokens.hasMoreTokens() && !found)
+ {
+ if (m_fwkExecEnvSet.contains(tokens.nextToken().trim()))
+ {
+ found = true;
+ }
+ }
+ if (!found)
+ {
+ throw new ResolveException(
+ "Execution environment not supported: "
+ + bundleExecEnvStr, module, null);
+ }
+ }
+ }
+ }
+
+ public void checkNativeLibraries(Module module) throws ResolveException
+ {
+ // Next, try to resolve any native code, since the module is
+ // not resolvable if its native code cannot be loaded.
+ List<R4Library> libs = module.getNativeLibraries();
+ if (libs != null)
+ {
+ String msg = null;
+ // Verify that all native libraries exist in advance; this will
+ // throw an exception if the native library does not exist.
+ for (int libIdx = 0; (msg == null) && (libIdx < libs.size()); libIdx++)
+ {
+ String entryName = libs.get(libIdx).getEntryName();
+ if (entryName != null)
+ {
+ if (!module.getContent().hasEntry(entryName))
+ {
+ msg = "Native library does not exist: " + entryName;
+ }
+ }
+ }
+ // If we have a zero-length native library array, then
+ // this means no native library class could be selected
+ // so we should fail to resolve.
+ if (libs.isEmpty())
+ {
+ msg = "No matching native libraries found.";
+ }
+ if (msg != null)
+ {
+ throw new ResolveException(msg, module, null);
+ }
+ }
+ }
+
+ //
+ // Utility methods.
+ //
+
+ /**
+ * Updates the framework wide execution environment string and a cached Set of
+ * execution environment tokens from the comma delimited list specified by the
+ * system variable 'org.osgi.framework.executionenvironment'.
+ * @param fwkExecEnvStr Comma delimited string of provided execution environments
+ * @return the parsed set of execution environments
+ **/
+ private static Set<String> parseExecutionEnvironments(String fwkExecEnvStr)
+ {
+ Set<String> newSet = new HashSet<String>();
+ if (fwkExecEnvStr != null)
+ {
+ StringTokenizer tokens = new StringTokenizer(fwkExecEnvStr, ",");
+ while (tokens.hasMoreTokens())
+ {
+ newSet.add(tokens.nextToken().trim());
+ }
+ }
+ return newSet;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
index e718b49..c8957f9 100644
--- a/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
@@ -36,7 +36,7 @@
public class CapabilitySet
{
private final Map<String, Map<Object, Set<Capability>>> m_indices;
- private final Set<Capability> m_capList = new HashSet<Capability>();
+ private final Set<Capability> m_capSet = new HashSet<Capability>();
private final static SecureAction m_secureAction = new SecureAction();
public CapabilitySet(List<String> indexProps, boolean caseSensitive)
@@ -52,7 +52,7 @@
public void addCapability(Capability cap)
{
- m_capList.add(cap);
+ m_capSet.add(cap);
// Index capability.
for (Entry<String, Map<Object, Set<Capability>>> entry : m_indices.entrySet())
@@ -98,7 +98,7 @@
public void removeCapability(Capability cap)
{
- if (m_capList.remove(cap))
+ if (m_capSet.remove(cap))
{
for (Entry<String, Map<Object, Set<Capability>>> entry : m_indices.entrySet())
{
@@ -146,7 +146,7 @@
public Set<Capability> match(SimpleFilter sf, boolean obeyMandatory)
{
- Set<Capability> matches = match(m_capList, sf);
+ Set<Capability> matches = match(m_capSet, sf);
return (obeyMandatory)
? matchMandatory(matches, sf)
: matches;
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java b/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
new file mode 100644
index 0000000..9f336a5
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
@@ -0,0 +1,495 @@
+/*
+ * 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.resolver;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.Requirement;
+import org.osgi.framework.Version;
+
+public class Candidates
+{
+ private final Module m_root;
+
+ // Maps a capability to requirements that match it.
+ private final Map<Capability, Set<Requirement>> m_dependentMap;
+ // Maps a requirement to the capability it matches.
+ private final Map<Requirement, SortedSet<Capability>> m_candidateMap;
+ // Maps a module to a map containing its potential fragments; the
+ // fragment map maps a fragment symbolic name to a map that maps
+ // a version to a list of fragments matching that symbolic name
+ // and version.
+ private final Map<Module, Map<String, Map<Version, List<Module>>>> m_hostFragments;
+ // Maps a module to its associated wrapped module; this only happens
+ // when a module being resolved has fragments to attach to it.
+ private final Map<Module, WrappedModule> m_allWrappedHosts;
+
+ /**
+ * Private copy constructor used by the copy() method.
+ * @param root the root module for the resolve.
+ * @param dependentMap the capability dependency map.
+ * @param candidateMap the requirement candidate map.
+ * @param hostFragments the fragment map.
+ * @param wrappedHosts the wrapped hosts map.
+ **/
+ private Candidates(
+ Module root,
+ Map<Capability, Set<Requirement>> dependentMap,
+ Map<Requirement, SortedSet<Capability>> candidateMap,
+ Map<Module, Map<String, Map<Version, List<Module>>>> hostFragments,
+ Map<Module, WrappedModule> wrappedHosts)
+ {
+ m_root = root;
+ m_dependentMap = dependentMap;
+ m_candidateMap = candidateMap;
+ m_hostFragments = hostFragments;
+ m_allWrappedHosts = wrappedHosts;
+ }
+
+ /**
+ * Constructs a new object with the specified root module. The root module
+ * is used to determine if the resolves fails when manipulating the candidates.
+ * For example, it may be necessary to remove an unselected fragment, which
+ * can cause a ripple effect all the way to the root module. If that happens
+ * then the resolve fails.
+ * @param root the root module for the resolve.
+ **/
+ public Candidates(Module root)
+ {
+ m_root = root;
+ m_dependentMap = new HashMap<Capability, Set<Requirement>>();
+ m_candidateMap = new HashMap<Requirement, SortedSet<Capability>>();
+ m_hostFragments = new HashMap<Module, Map<String, Map<Version, List<Module>>>>();
+ m_allWrappedHosts = new HashMap<Module, WrappedModule>();
+ }
+
+ /**
+ * Adds a requirement and its matching candidates to the internal data
+ * structure. This method assumes it owns the data being passed in and
+ * does not make a copy. It takes the data and processes, such as calculating
+ * which requirements depend on which capabilities and recording any fragments
+ * it finds for future merging.
+ * @param req the requirement to add.
+ * @param candidates the candidates matching the requirement.
+ **/
+ public void add(Requirement req, SortedSet<Capability> candidates)
+ {
+ boolean isFragment = req.getNamespace().equals(Capability.HOST_NAMESPACE);
+
+ // Record the candidates.
+ m_candidateMap.put(req, candidates);
+ // Add the requirement as a dependent on the candidates.
+ for (Capability cap : candidates)
+ {
+ Set<Requirement> dependents = m_dependentMap.get(cap);
+ if (dependents == null)
+ {
+ dependents = new HashSet<Requirement>();
+ m_dependentMap.put(cap, dependents);
+ }
+ dependents.add(req);
+ // Keep track of hosts and associated fragments.
+ if (isFragment)
+ {
+ Map<String, Map<Version, List<Module>>> fragments =
+ m_hostFragments.get(cap.getModule());
+ if (fragments == null)
+ {
+ fragments = new HashMap<String, Map<Version, List<Module>>>();
+ m_hostFragments.put(cap.getModule(), fragments);
+ }
+ Map<Version, List<Module>> fragmentVersions =
+ fragments.get(req.getModule().getSymbolicName());
+ if (fragmentVersions == null)
+ {
+ fragmentVersions = new TreeMap<Version, List<Module>>(Collections.reverseOrder());
+ fragments.put(req.getModule().getSymbolicName(), fragmentVersions);
+ }
+ List<Module> actual = fragmentVersions.get(req.getModule().getVersion());
+ if (actual == null)
+ {
+ actual = new ArrayList<Module>();
+ fragmentVersions.put(req.getModule().getVersion(), actual);
+ }
+ actual.add(req.getModule());
+ }
+ }
+ }
+
+ /**
+ * Adds requirements and candidates in bulk. The outer map is not retained
+ * by this method, but the inner data structures are, so they should not
+ * be further modified by the caller.
+ * @param candidates the bulk requirements and candidates to add.
+ **/
+ public void add(Map<Requirement, SortedSet<Capability>> candidates)
+ {
+ for (Entry<Requirement, SortedSet<Capability>> entry : candidates.entrySet())
+ {
+ add(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Returns the wrapped module associated with the given module. If the module
+ * was not wrapped, then the module itself is returned. This is really only
+ * needed to determine if the root modules of the resolve have been wrapped.
+ * @param m the module whose wrapper is desired.
+ * @return the wrapper module or the module itself if it was not wrapped.
+ **/
+ public Module getWrappedHost(Module m)
+ {
+ Module wrapped = m_allWrappedHosts.get(m);
+ return (wrapped == null) ? m : wrapped;
+ }
+
+ /**
+ * Gets the candidates associated with a given requirement.
+ * @param req the requirement whose candidates are desired.
+ * @return the matching candidates or null.
+ **/
+ public SortedSet<Capability> getCandidates(Requirement req)
+ {
+ return m_candidateMap.get(req);
+ }
+
+ /**
+ * Gets the complete candidate map. This is only used in the dynamic
+ * import case, since the requirement is unknown and it just needs the
+ * one and only requirement in the map. (This could probably be handled
+ * differently or better, but it is sufficient for now.)
+ * @return The entire candidate map.
+ **/
+ public Map<Requirement, SortedSet<Capability>> getCandidateMap()
+ {
+ return m_candidateMap;
+ }
+
+ /**
+ * Merges fragments into their hosts. It does this by wrapping all host
+ * modules and attaching their selected fragments, removing all unselected
+ * fragment modules, and replacing all occurrences of the original fragments
+ * in the internal data structures with the wrapped host modules instead.
+ * Thus, fragment capabilities and requirements are merged into the appropriate
+ * host and the candidates for the fragment now become candidates for the host.
+ * Likewise, any module depending on a fragment now depend on the host. Note
+ * that this process is sort of like multiplication, since one fragment that
+ * can attach to two hosts effectively gets multiplied across the two hosts.
+ * So, any modules being satisfied by the fragment will end up having the
+ * two hosts as potential candidates, rather than the single fragment.
+ **/
+ public void mergeFragments()
+ {
+ // This method performs the following steps:
+ // 1. Select the fragments to attach to a given host.
+ // 2. Wrap hosts and attach fragments.
+ // 3. Remove any unselected fragments. This is necessary because
+ // other modules may depend on the capabilities of unselected
+ // fragments, so we need to remove the unselected fragments and
+ // any module that depends on them, which could ultimately cause
+ // the entire resolve to fail.
+ // 4. Replace all fragments with any host it was merged into
+ // (effectively multiplying it).
+ // * This includes setting candidates for attached fragment
+ // requirements as well as replacing fragment capabilities
+ // with host's attached fragment capabilities.
+
+ // Steps 1 and 2
+ List<WrappedModule> wrappedHosts = new ArrayList<WrappedModule>();
+ List<Module> unselectedFragments = new ArrayList<Module>();
+ for (Entry<Module, Map<String, Map<Version, List<Module>>>> entry :
+ m_hostFragments.entrySet())
+ {
+ // Step 1
+ List<Module> selectedFragments = new ArrayList<Module>();
+ Module host = entry.getKey();
+ Map<String, Map<Version, List<Module>>> fragments = entry.getValue();
+ for (Entry<String, Map<Version, List<Module>>> fragEntry : fragments.entrySet())
+ {
+ boolean isFirst = true;
+ for (Entry<Version, List<Module>> versionEntry : fragEntry.getValue().entrySet())
+ {
+ for (Module m : versionEntry.getValue())
+ {
+ if (isFirst && !m.isRemovalPending())
+ {
+ selectedFragments.add(m);
+ isFirst = false;
+ }
+ else
+ {
+ unselectedFragments.add(m);
+ }
+ }
+ }
+ }
+
+ // Step 2
+ WrappedModule wrappedHost = new WrappedModule(host, selectedFragments);
+ wrappedHosts.add(wrappedHost);
+ m_allWrappedHosts.put(host, wrappedHost);
+ }
+
+ // Step 3
+ for (Module m : unselectedFragments)
+ {
+ unselectFragment(m);
+ }
+
+ // Step 4
+ for (WrappedModule wrappedHost : wrappedHosts)
+ {
+ // Replaces capabilities from fragments with the capabilities
+ // from the merged host.
+ for (Capability c : wrappedHost.getCapabilities())
+ {
+ Set<Requirement> dependents =
+ m_dependentMap.get(((WrappedCapability) c).getWrappedCapability());
+ if (dependents != null)
+ {
+ for (Requirement r : dependents)
+ {
+ Set<Capability> cands = m_candidateMap.get(r);
+ cands.remove(((WrappedCapability) c).getWrappedCapability());
+ cands.add(c);
+ }
+ }
+ }
+
+ // Copies candidates for fragment requirements to the host.
+ // This doesn't record the reverse dependency, but that
+ // information should not be needed at this point anymore.
+ for (Requirement r : wrappedHost.getRequirements())
+ {
+ SortedSet<Capability> cands =
+ m_candidateMap.get(((WrappedRequirement) r).getWrappedRequirement());
+ if (cands != null)
+ {
+ m_candidateMap.put(r, new TreeSet<Capability>(cands));
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a fragment from the internal data structures if it wasn't selected.
+ * This process may cause other modules to become unresolved if they depended
+ * on fragment capabilities and there is no other candidate.
+ * @param fragment the fragment to remove.
+ * @throws ResolveException if removing the fragment caused the resolve to fail.
+ **/
+ private void unselectFragment(Module fragment) throws ResolveException
+ {
+ Set<Module> unresolvedModules = new HashSet<Module>();
+ remove(fragment, unresolvedModules);
+ while (!unresolvedModules.isEmpty())
+ {
+ Iterator<Module> it = unresolvedModules.iterator();
+ fragment = it.next();
+ it.remove();
+ remove(fragment, unresolvedModules);
+ }
+ }
+
+ /**
+ * Removes the specified module from the internal data structures, which
+ * involves removing its requirements and its capabilities. This may cause
+ * other modules to become unresolved as a result.
+ * @param m the module to remove.
+ * @param unresolvedModules a list to containing any additional modules that
+ * that became unresolved as a result of removing this module and will
+ * also need to be removed.
+ * @throws ResolveException if removing the module caused the resolve to fail.
+ **/
+ private void remove(Module m, Set<Module> unresolvedModules) throws ResolveException
+ {
+ for (Requirement r : m.getRequirements())
+ {
+ remove(r);
+ }
+
+ for (Capability c : m.getCapabilities())
+ {
+ remove(c, unresolvedModules);
+ }
+ }
+
+ /**
+ * Removes a requirement from the internal data structures.
+ * @param req the requirement to remove.
+ **/
+ private void remove(Requirement req)
+ {
+ boolean isFragment = req.getNamespace().equals(Capability.HOST_NAMESPACE);
+
+ SortedSet<Capability> candidates = m_candidateMap.remove(req);
+ if (candidates != null)
+ {
+ for (Capability cap : candidates)
+ {
+ Set<Requirement> dependents = m_dependentMap.get(cap);
+ if (dependents != null)
+ {
+ dependents.remove(req);
+ }
+
+ if (isFragment)
+ {
+ Map<String, Map<Version, List<Module>>> fragments =
+ m_hostFragments.get(cap.getModule());
+ if (fragments != null)
+ {
+ Map<Version, List<Module>> fragmentVersions =
+ fragments.get(req.getModule().getSymbolicName());
+ if (fragmentVersions != null)
+ {
+ List<Module> actual = fragmentVersions.get(req.getModule().getVersion());
+ if (actual != null)
+ {
+ actual.remove(req.getModule());
+ if (actual.isEmpty())
+ {
+ fragmentVersions.remove(req.getModule().getVersion());
+ if (fragmentVersions.isEmpty())
+ {
+ fragments.remove(req.getModule().getSymbolicName());
+ if (fragments.isEmpty())
+ {
+ m_hostFragments.remove(cap.getModule());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a capability from the internal data structures. This may cause
+ * other modules to become unresolved as a result.
+ * @param c the capability to remove.
+ * @param unresolvedModules a list to containing any additional modules that
+ * that became unresolved as a result of removing this module and will
+ * also need to be removed.
+ * @throws ResolveException if removing the module caused the resolve to fail.
+ **/
+ private void remove(Capability c, Set<Module> unresolvedModules) throws ResolveException
+ {
+ Set<Requirement> dependents = m_dependentMap.remove(c);
+ if (dependents != null)
+ {
+ for (Requirement r : dependents)
+ {
+ SortedSet<Capability> candidates = m_candidateMap.get(r);
+ candidates.remove(c);
+ if (candidates.isEmpty())
+ {
+ m_candidateMap.remove(r);
+ if (!r.isOptional())
+ {
+ if (m_root.equals(r.getModule()))
+ {
+ String msg = "Unable to resolve " + m_root
+ + ": missing requirement " + r;
+ ResolveException ex = new ResolveException(msg, m_root, r);
+ throw ex;
+ }
+ unresolvedModules.add(r.getModule());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a copy of the Candidates object. This is used for creating
+ * permutations when package space conflicts are discovered.
+ * @return copy of this Candidates object.
+ **/
+ public Candidates copy()
+ {
+ Map<Capability, Set<Requirement>> dependentMap =
+ new HashMap<Capability, Set<Requirement>>();
+ for (Entry<Capability, Set<Requirement>> entry : m_dependentMap.entrySet())
+ {
+ Set<Requirement> dependents = new HashSet<Requirement>(entry.getValue());
+ dependentMap.put(entry.getKey(), dependents);
+ }
+
+ Map<Requirement, SortedSet<Capability>> candidateMap =
+ new HashMap<Requirement, SortedSet<Capability>>();
+ for (Entry<Requirement, SortedSet<Capability>> entry : m_candidateMap.entrySet())
+ {
+ SortedSet<Capability> candidates = new TreeSet<Capability>(entry.getValue());
+ candidateMap.put(entry.getKey(), candidates);
+ }
+
+ return new Candidates(
+ m_root, dependentMap, candidateMap, m_hostFragments, m_allWrappedHosts);
+ }
+
+ public void dump()
+ {
+ // Create set of all modules from requirements.
+ Set<Module> modules = new HashSet();
+ for (Entry<Requirement, SortedSet<Capability>> entry
+ : m_candidateMap.entrySet())
+ {
+ modules.add(entry.getKey().getModule());
+ }
+ // Now dump the modules.
+ System.out.println("=== BEGIN CANDIDATE MAP ===");
+ for (Module module : modules)
+ {
+ System.out.println(" " + module
+ + " (" + (module.isResolved() ? "RESOLVED)" : "UNRESOLVED)"));
+ for (Requirement req : module.getRequirements())
+ {
+ Set<Capability> candidates = m_candidateMap.get(req);
+ if ((candidates != null) && (candidates.size() > 0))
+ {
+ System.out.println(" " + req + ": " + candidates);
+ }
+ }
+ for (Requirement req : module.getDynamicRequirements())
+ {
+ Set<Capability> candidates = m_candidateMap.get(req);
+ if ((candidates != null) && (candidates.size() > 0))
+ {
+ System.out.println(" " + req + ": " + candidates);
+ }
+ }
+ }
+ System.out.println("=== END CANDIDATE MAP ===");
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/Module.java b/framework/src/main/java/org/apache/felix/framework/resolver/Module.java
index 695bfd3..7d9f84f 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/Module.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Module.java
@@ -52,6 +52,11 @@
List<Wire> getWires();
boolean isResolved();
Object getSecurityContext();
+ // TODO: FRAGMENT RESOLVER - Technically, this is only necessary for fragments.
+ // When we refactoring for the new R4.3 framework API, we'll have to see
+ // if this is still necessary, since the new BundleWirings API will give
+ // us another way to detect it.
+ boolean isRemovalPending();
// Content access methods.
Content getContent();
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java b/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
index d6f2c24..2d8f9e0 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
@@ -21,17 +21,20 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
import org.apache.felix.framework.capabilityset.Capability;
import org.apache.felix.framework.capabilityset.Requirement;
public interface Resolver
{
- Map<Module, List<Wire>> resolve(ResolverState state, Module module);
- Map<Module, List<Wire>> resolve(ResolverState state, Module module, String pkgName);
+ Map<Module, List<Wire>> resolve(ResolverState state, Module module, Set<Module> fragments);
+ Map<Module, List<Wire>> resolve(
+ ResolverState state, Module module, String pkgName, Set<Module> fragments);
public static interface ResolverState
{
- Set<Capability> getCandidates(Module module, Requirement req, boolean obeyMandatory);
+ SortedSet<Capability> getCandidates(
+ Module module, Requirement req, boolean obeyMandatory);
void checkExecutionEnvironment(Module module) throws ResolveException;
void checkNativeLibraries(Module module) throws ResolveException;
}
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
index 7c7e409..9034ccb 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
@@ -27,7 +27,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.TreeSet;
+import java.util.SortedSet;
import org.apache.felix.framework.Logger;
import org.apache.felix.framework.capabilityset.Attribute;
import org.apache.felix.framework.capabilityset.Capability;
@@ -44,90 +44,152 @@
// Holds candidate permutations based on permutating "uses" chains.
// These permutations are given higher priority.
- private final List<Map<Requirement, Set<Capability>>> m_usesPermutations =
- new ArrayList<Map<Requirement, Set<Capability>>>();
+ private final List<Candidates> m_usesPermutations = new ArrayList<Candidates>();
// Holds candidate permutations based on permutating requirement candidates.
// These permutations represent backtracking on previous decisions.
- private final List<Map<Requirement, Set<Capability>>> m_importPermutations =
- new ArrayList<Map<Requirement, Set<Capability>>>();
+ private final List<Candidates> m_importPermutations = new ArrayList<Candidates>();
public ResolverImpl(Logger logger)
{
m_logger = logger;
}
- public Map<Module, List<Wire>> resolve(ResolverState state, Module module)
+ public Map<Module, List<Wire>> resolve(
+ ResolverState state, Module module, Set<Module> fragments)
{
Map<Module, List<Wire>> wireMap = new HashMap<Module, List<Wire>>();
-
Map<Module, Packages> modulePkgMap = new HashMap<Module, Packages>();
if (!module.isResolved())
{
- try
+ boolean retryFragments;
+ do
{
- Map<Requirement, Set<Capability>> candidateMap =
- new HashMap<Requirement, Set<Capability>>();
+ retryFragments = false;
- populateCandidates(
- state, module, candidateMap, new HashMap<Module, Object>());
- m_usesPermutations.add(candidateMap);
-
- ResolveException rethrow = null;
-
- do
+ try
{
- rethrow = null;
+ Candidates allCandidates = new Candidates(module);
- modulePkgMap.clear();
- m_packageSourcesCache.clear();
+ // Populate all candidates.
+ Map<Module, Object> resultCache = new HashMap<Module, Object>();
+ populateCandidates(
+ state, module, allCandidates, resultCache);
- candidateMap = (m_usesPermutations.size() > 0)
- ? m_usesPermutations.remove(0)
- : m_importPermutations.remove(0);
-//dumpCandidateMap(candidateMap);
+ // Try to populate optional fragments.
+ for (Module fragment : fragments)
+ {
+ try
+ {
+ populateCandidates(state, fragment, allCandidates, resultCache);
+ }
+ catch (ResolveException ex)
+ {
+ // Ignore, since fragments are optional.
+ }
+ }
- calculatePackageSpaces(
- module, candidateMap, modulePkgMap,
- new HashMap(), new HashSet());
+ // Merge any fragments into hosts.
+ allCandidates.mergeFragments();
+
+ // Record the initial candidate permutation.
+ m_usesPermutations.add(allCandidates);
+
+ ResolveException rethrow = null;
+
+ // If the requested module is a fragment, then
+ // ultimately we will verify the host.
+ Requirement hostReq = getHostRequirement(module);
+ Module target = module;
+
+ do
+ {
+ rethrow = null;
+
+ modulePkgMap.clear();
+ m_packageSourcesCache.clear();
+
+ allCandidates = (m_usesPermutations.size() > 0)
+ ? m_usesPermutations.remove(0)
+ : m_importPermutations.remove(0);
+//allCandidates.dump();
+
+ // If we are resolving a fragment, then we
+ // actually want to verify its host.
+ if (hostReq != null)
+ {
+ target = allCandidates.getCandidates(hostReq)
+ .iterator().next().getModule();
+ }
+
+ calculatePackageSpaces(
+ allCandidates.getWrappedHost(target), allCandidates, modulePkgMap,
+ new HashMap(), new HashSet());
//System.out.println("+++ PACKAGE SPACES START +++");
//dumpModulePkgMap(modulePkgMap);
//System.out.println("+++ PACKAGE SPACES END +++");
- try
- {
- checkPackageSpaceConsistency(
- false, module, candidateMap, modulePkgMap, new HashMap());
+ try
+ {
+ checkPackageSpaceConsistency(
+ false, allCandidates.getWrappedHost(target),
+ allCandidates, modulePkgMap, new HashMap());
+ }
+ catch (ResolveException ex)
+ {
+ rethrow = ex;
+ }
}
- catch (ResolveException ex)
+ while ((rethrow != null)
+ && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
+
+ // If there is a resolve exception, then determine if an
+ // optionally resolved module is to blame (typically a fragment).
+ // If so, then remove the optionally resolved module and try
+ // again; otherwise, rethrow the resolve exception.
+ if (rethrow != null)
{
- rethrow = ex;
+ Module faultyModule = getActualModule(rethrow.getModule());
+ if (rethrow.getRequirement() instanceof WrappedRequirement)
+ {
+ faultyModule =
+ ((WrappedRequirement) rethrow.getRequirement())
+ .getWrappedRequirement().getModule();
+ }
+ if (fragments.remove(faultyModule))
+ {
+ retryFragments = true;
+ }
+ else
+ {
+ throw rethrow;
+ }
+ }
+ // If there is no exception to rethrow, then this was a clean
+ // resolve, so populate the wire map.
+ else
+ {
+ wireMap =
+ populateWireMap(
+ allCandidates.getWrappedHost(target),
+ modulePkgMap, wireMap, allCandidates);
}
}
- while ((rethrow != null)
- && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
-
- if (rethrow != null)
+ finally
{
- throw rethrow;
+ // Always clear the state.
+ m_usesPermutations.clear();
+ m_importPermutations.clear();
}
-
- wireMap =
- populateWireMap(module, modulePkgMap, wireMap,
- candidateMap);
}
- finally
- {
- // Always clear the state.
- m_usesPermutations.clear();
- m_importPermutations.clear();
- }
+ while (retryFragments);
}
return wireMap;
}
- public Map<Module, List<Wire>> resolve(ResolverState state, Module module, String pkgName)
+ public Map<Module, List<Wire>> resolve(
+ ResolverState state, Module module, String pkgName, Set<Module> fragments)
{
// We can only create a dynamic import if the following
// conditions are met:
@@ -138,71 +200,157 @@
// 5. The package in question matches a dynamic import of the bundle.
// The following call checks all of these conditions and returns
// the associated dynamic import and matching capabilities.
- Map<Requirement, Set<Capability>> candidateMap =
+ Candidates allCandidates =
getDynamicImportCandidates(state, module, pkgName);
- if (candidateMap != null)
+ if (allCandidates != null)
{
- try
+ Map<Module, List<Wire>> wireMap = new HashMap<Module, List<Wire>>();
+ Map<Module, Packages> modulePkgMap = new HashMap<Module, Packages>();
+
+ boolean retryFragments;
+ do
{
- Map<Module, List<Wire>> wireMap = new HashMap();
- Map<Module, Packages> modulePkgMap = new HashMap();
+ retryFragments = false;
- populateDynamicCandidates(state, module, candidateMap);
- m_usesPermutations.add(candidateMap);
-
- ResolveException rethrow = null;
-
- do
+ try
{
- rethrow = null;
+ // Populate all candidates.
+ Map<Module, Object> resultCache = new HashMap<Module, Object>();
+ populateDynamicCandidates(state, module, allCandidates, resultCache);
- modulePkgMap.clear();
-
- candidateMap = (m_usesPermutations.size() > 0)
- ? m_usesPermutations.remove(0)
- : m_importPermutations.remove(0);
-
- calculatePackageSpaces(
- module, candidateMap, modulePkgMap,
- new HashMap(), new HashSet());
-
- try
+ // Try to populate optional fragments.
+ for (Module fragment : fragments)
{
- checkPackageSpaceConsistency(
- true, module, candidateMap, modulePkgMap, new HashMap());
+ try
+ {
+ populateCandidates(state, fragment, allCandidates, resultCache);
+ }
+ catch (ResolveException ex)
+ {
+ // Ignore, since fragments are optional.
+ }
}
- catch (ResolveException ex)
+
+ // Merge any fragments into hosts.
+ allCandidates.mergeFragments();
+
+ // Record the initial candidate permutation.
+ m_usesPermutations.add(allCandidates);
+
+ ResolveException rethrow = null;
+
+ // If the requested module is a fragment, then
+ // ultimately we will verify the host.
+ Requirement hostReq = getHostRequirement(module);
+ Module target = module;
+
+ do
{
- rethrow = ex;
- }
- }
- while ((rethrow != null)
- && ((m_usesPermutations.size() > 0)
- || (m_importPermutations.size() > 0)));
+ rethrow = null;
- if (rethrow != null)
- {
- throw rethrow;
- }
+ modulePkgMap.clear();
+ m_packageSourcesCache.clear();
+ allCandidates = (m_usesPermutations.size() > 0)
+ ? m_usesPermutations.remove(0)
+ : m_importPermutations.remove(0);
+//allCandidates.dump();
+
+ // For a dynamic import, the instigating module
+ // will never be a fragment since fragments never
+ // execute code, so we don't need to check for
+ // this case like we do for a normal resolve.
+
+ calculatePackageSpaces(
+ allCandidates.getWrappedHost(target), allCandidates, modulePkgMap,
+ new HashMap(), new HashSet());
+//System.out.println("+++ PACKAGE SPACES START +++");
//dumpModulePkgMap(modulePkgMap);
- wireMap = populateDynamicWireMap(
- module, pkgName, modulePkgMap, wireMap, candidateMap);
+//System.out.println("+++ PACKAGE SPACES END +++");
- return wireMap;
+ try
+ {
+ checkPackageSpaceConsistency(
+ false, allCandidates.getWrappedHost(target),
+ allCandidates, modulePkgMap, new HashMap());
+ }
+ catch (ResolveException ex)
+ {
+ rethrow = ex;
+ }
+ }
+ while ((rethrow != null)
+ && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
+
+ // If there is a resolve exception, then determine if an
+ // optionally resolved module is to blame (typically a fragment).
+ // If so, then remove the optionally resolved module and try
+ // again; otherwise, rethrow the resolve exception.
+ if (rethrow != null)
+ {
+ Module faultyModule = getActualModule(rethrow.getModule());
+ if (rethrow.getRequirement() instanceof WrappedRequirement)
+ {
+ faultyModule =
+ ((WrappedRequirement) rethrow.getRequirement())
+ .getWrappedRequirement().getModule();
+ }
+ if (fragments.remove(faultyModule))
+ {
+ retryFragments = true;
+ }
+ else
+ {
+ throw rethrow;
+ }
+ }
+ // If there is no exception to rethrow, then this was a clean
+ // resolve, so populate the wire map.
+ else
+ {
+ wireMap = populateDynamicWireMap(
+ module, pkgName, modulePkgMap, wireMap, allCandidates);
+ return wireMap;
+ }
+ }
+ finally
+ {
+ // Always clear the state.
+ m_usesPermutations.clear();
+ m_importPermutations.clear();
+ }
}
- finally
- {
- // Always clear the state.
- m_usesPermutations.clear();
- m_importPermutations.clear();
- }
+ while (retryFragments);
}
return null;
}
- private static Map<Requirement, Set<Capability>> getDynamicImportCandidates(
+ private static Capability getHostCapability(Module m)
+ {
+ for (Capability c : m.getCapabilities())
+ {
+ if (c.getNamespace().equals(Capability.HOST_NAMESPACE))
+ {
+ return c;
+ }
+ }
+ return null;
+ }
+
+ private static Requirement getHostRequirement(Module m)
+ {
+ for (Requirement r : m.getRequirements())
+ {
+ if (r.getNamespace().equals(Capability.HOST_NAMESPACE))
+ {
+ return r;
+ }
+ }
+ return null;
+ }
+
+ private static Candidates getDynamicImportCandidates(
ResolverState state, Module module, String pkgName)
{
// Unresolved modules cannot dynamically import, nor can the default
@@ -250,7 +398,7 @@
attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
Requirement req = new RequirementImpl(
module, Capability.PACKAGE_NAMESPACE, dirs, attrs);
- Set<Capability> candidates = state.getCandidates(module, req, false);
+ SortedSet<Capability> candidates = state.getCandidates(module, req, false);
// First find a dynamic requirement that matches the capabilities.
Requirement dynReq = null;
@@ -289,9 +437,9 @@
if (candidates.size() > 0)
{
- Map<Requirement, Set<Capability>> candidateMap = new HashMap();
- candidateMap.put(dynReq, candidates);
- return candidateMap;
+ Candidates allCandidates = new Candidates(module);
+ allCandidates.add(dynReq, candidates);
+ return allCandidates;
}
return null;
@@ -299,8 +447,7 @@
// TODO: FELIX3 - Modify to not be recursive.
private void populateCandidates(
- ResolverState state, Module module,
- Map<Requirement, Set<Capability>> candidateMap,
+ ResolverState state, Module module, Candidates allCandidates,
Map<Module, Object> resultCache)
{
// Determine if we've already calculated this module's candidates.
@@ -323,7 +470,7 @@
// Keeps track of the candidates we've already calculated for the
// current module's requirements.
- Map<Requirement, Set<Capability>> localCandidateMap = null;
+ Map<Requirement, SortedSet<Capability>> localCandidateMap = null;
// Keeps track of the current module's requirements for which we
// haven't yet found candidates.
@@ -390,7 +537,7 @@
// Get satisfying candidates and populate their candidates if necessary.
ResolveException rethrow = null;
- Set<Capability> candidates = state.getCandidates(module, req, true);
+ SortedSet<Capability> candidates = state.getCandidates(module, req, true);
for (Iterator<Capability> itCandCap = candidates.iterator(); itCandCap.hasNext(); )
{
Capability candCap = itCandCap.next();
@@ -410,7 +557,7 @@
try
{
populateCandidates(state, candCap.getModule(),
- candidateMap, resultCache);
+ allCandidates, resultCache);
}
catch (ResolveException ex)
{
@@ -438,11 +585,6 @@
}
rethrow = new ResolveException(msg, module, req);
resultCache.put(module, rethrow);
- m_logger.log(
- module.getBundle(),
- Logger.LOG_DEBUG,
- "No viable candidates",
- rethrow);
throw rethrow;
}
// If we actually have candidates for the requirement, then
@@ -467,22 +609,23 @@
// Merge local candidate map into global candidate map.
if (localCandidateMap.size() > 0)
{
- candidateMap.putAll(localCandidateMap);
+ allCandidates.add(localCandidateMap);
}
}
}
private void populateDynamicCandidates(
- ResolverState state, Module module,
- Map<Requirement, Set<Capability>> candidateMap)
+ ResolverState state, Module module, Candidates allCandidates,
+ Map<Module, Object> resultCache)
{
// There should be one entry in the candidate map, which are the
// the candidates for the matching dynamic requirement. Get the
// matching candidates and populate their candidates if necessary.
ResolveException rethrow = null;
- Entry<Requirement, Set<Capability>> entry = candidateMap.entrySet().iterator().next();
+ Entry<Requirement, SortedSet<Capability>> entry =
+ allCandidates.getCandidateMap().entrySet().iterator().next();
Requirement dynReq = entry.getKey();
- Set<Capability> candidates = entry.getValue();
+ SortedSet<Capability> candidates = entry.getValue();
for (Iterator<Capability> itCandCap = candidates.iterator(); itCandCap.hasNext(); )
{
Capability candCap = itCandCap.next();
@@ -491,7 +634,7 @@
try
{
populateCandidates(state, candCap.getModule(),
- candidateMap, new HashMap<Module, Object>());
+ allCandidates, resultCache);
}
catch (ResolveException ex)
{
@@ -506,7 +649,6 @@
if (candidates.isEmpty())
{
- candidateMap.remove(dynReq);
if (rethrow == null)
{
rethrow = new ResolveException("Dynamic import failed.", module, dynReq);
@@ -517,7 +659,7 @@
private void calculatePackageSpaces(
Module module,
- Map<Requirement, Set<Capability>> candidateMap,
+ Candidates allCandidates,
Map<Module, Packages> modulePkgMap,
Map<Capability, List<Module>> usesCycleMap,
Set<Module> cycle)
@@ -548,7 +690,7 @@
for (Requirement req : module.getDynamicRequirements())
{
// Get the candidates for the current requirement.
- Set<Capability> candCaps = candidateMap.get(req);
+ SortedSet<Capability> candCaps = allCandidates.getCandidates(req);
// Optional requirements may not have any candidates.
if (candCaps == null)
{
@@ -569,7 +711,7 @@
for (Requirement req : module.getRequirements())
{
// Get the candidates for the current requirement.
- Set<Capability> candCaps = candidateMap.get(req);
+ SortedSet<Capability> candCaps = allCandidates.getCandidates(req);
// Optional requirements may not have any candidates.
if (candCaps == null)
{
@@ -583,7 +725,7 @@
}
// First, add all exported packages to the target module's package space.
- calculateExportedPackages(module, candidateMap, modulePkgMap);
+ calculateExportedPackages(module, allCandidates, modulePkgMap);
Packages modulePkgs = modulePkgMap.get(module);
// Second, add all imported packages to the target module's package space.
@@ -591,15 +733,15 @@
{
Requirement req = reqs.get(i);
Capability cap = caps.get(i);
- calculateExportedPackages(cap.getModule(), candidateMap, modulePkgMap);
- mergeCandidatePackages(module, req, cap, modulePkgMap, candidateMap);
+ calculateExportedPackages(cap.getModule(), allCandidates, modulePkgMap);
+ mergeCandidatePackages(module, req, cap, modulePkgMap, allCandidates);
}
// Third, have all candidates to calculate their package spaces.
for (int i = 0; i < caps.size(); i++)
{
calculatePackageSpaces(
- caps.get(i).getModule(), candidateMap, modulePkgMap,
+ caps.get(i).getModule(), allCandidates, modulePkgMap,
usesCycleMap, cycle);
}
@@ -630,7 +772,7 @@
blame.m_cap,
blameReqs,
modulePkgMap,
- candidateMap,
+ allCandidates,
usesCycleMap);
}
}
@@ -648,7 +790,7 @@
blame.m_cap,
blameReqs,
modulePkgMap,
- candidateMap,
+ allCandidates,
usesCycleMap);
}
}
@@ -657,7 +799,8 @@
private void mergeCandidatePackages(
Module current, Requirement currentReq, Capability candCap,
- Map<Module, Packages> modulePkgMap, Map<Requirement, Set<Capability>> candidateMap)
+ Map<Module, Packages> modulePkgMap,
+ Candidates allCandidates)
{
if (candCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
{
@@ -668,7 +811,7 @@
{
// TODO: FELIX3 - THIS NEXT LINE IS A HACK. IMPROVE HOW/WHEN WE CALCULATE EXPORTS.
calculateExportedPackages(
- candCap.getModule(), candidateMap, modulePkgMap);
+ candCap.getModule(), allCandidates, modulePkgMap);
// Get the candidate's package space to determine which packages
// will be visible to the current module.
@@ -694,14 +837,14 @@
{
Directive dir = req.getDirective(Constants.VISIBILITY_DIRECTIVE);
if ((dir != null) && dir.getValue().equals(Constants.VISIBILITY_REEXPORT)
- && (candidateMap.get(req) != null))
+ && (allCandidates.getCandidates(req) != null))
{
mergeCandidatePackages(
current,
currentReq,
- candidateMap.get(req).iterator().next(),
+ allCandidates.getCandidates(req).iterator().next(),
modulePkgMap,
- candidateMap);
+ allCandidates);
}
}
}
@@ -760,8 +903,9 @@
private void mergeUses(
Module current, Packages currentPkgs,
- Capability mergeCap, List<Requirement> blameReqs, Map<Module, Packages> modulePkgMap,
- Map<Requirement, Set<Capability>> candidateMap,
+ Capability mergeCap, List<Requirement> blameReqs,
+ Map<Module, Packages> modulePkgMap,
+ Candidates allCandidates,
Map<Capability, List<Module>> cycleMap)
{
if (!mergeCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
@@ -821,14 +965,14 @@
List<Requirement> blameReqs2 = new ArrayList(blameReqs);
blameReqs2.add(blame.m_reqs.get(blame.m_reqs.size() - 1));
usedCaps.add(new Blame(blame.m_cap, blameReqs2));
- mergeUses(current, currentPkgs, blame.m_cap, blameReqs2,
- modulePkgMap, candidateMap, cycleMap);
+ mergeUses(current, currentPkgs, blame.m_cap, blameReqs2,
+ modulePkgMap, allCandidates, cycleMap);
}
else
{
usedCaps.add(new Blame(blame.m_cap, blameReqs));
mergeUses(current, currentPkgs, blame.m_cap, blameReqs,
- modulePkgMap, candidateMap, cycleMap);
+ modulePkgMap, allCandidates, cycleMap);
}
}
}
@@ -836,8 +980,9 @@
}
private void checkPackageSpaceConsistency(
- boolean isDynamicImport, Module module,
- Map<Requirement, Set<Capability>> candidateMap,
+ boolean isDynamicImport,
+ Module module,
+ Candidates allCandidates,
Map<Module, Packages> modulePkgMap,
Map<Module, Object> resultCache)
{
@@ -853,7 +998,7 @@
Packages pkgs = modulePkgMap.get(module);
ResolveException rethrow = null;
- Map<Requirement, Set<Capability>> permutation = null;
+ Candidates permutation = null;
Set<Requirement> mutated = null;
// Check for conflicting imports from fragments.
@@ -871,9 +1016,9 @@
else if (!sourceBlame.m_cap.getModule().equals(blame.m_cap.getModule()))
{
// Try to permutate the conflicting requirement.
- permutate(candidateMap, blame.m_reqs.get(0), m_importPermutations);
+ permutate(allCandidates, blame.m_reqs.get(0), m_importPermutations);
// Try to permutate the source requirement.
- permutate(candidateMap, sourceBlame.m_reqs.get(0), m_importPermutations);
+ permutate(allCandidates, sourceBlame.m_reqs.get(0), m_importPermutations);
// Report conflict.
ResolveException ex = new ResolveException(
"Unable to resolve module "
@@ -920,7 +1065,7 @@
// that conflict with existing selected candidates.
permutation = (permutation != null)
? permutation
- : copyCandidateMap(candidateMap);
+ : allCandidates.copy();
rethrow = (rethrow != null)
? rethrow
: new ResolveException(
@@ -956,7 +1101,7 @@
// See if we can permutate the candidates for blamed
// requirement; there may be no candidates if the module
// associated with the requirement is already resolved.
- Set<Capability> candidates = permutation.get(req);
+ SortedSet<Capability> candidates = permutation.getCandidates(req);
if ((candidates != null) && (candidates.size() > 1))
{
mutated.add(req);
@@ -985,7 +1130,7 @@
}
}
- // Check if there are any conflicts with imported packages.
+ // Check if there are any uses conflicts with imported packages.
for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
{
for (Blame importBlame : entry.getValue())
@@ -1003,7 +1148,7 @@
// that conflict with existing selected candidates.
permutation = (permutation != null)
? permutation
- : copyCandidateMap(candidateMap);
+ : allCandidates.copy();
rethrow = (rethrow != null)
? rethrow
: new ResolveException(
@@ -1044,7 +1189,7 @@
// See if we can permutate the candidates for blamed
// requirement; there may be no candidates if the module
// associated with the requirement is already resolved.
- Set<Capability> candidates = permutation.get(req);
+ SortedSet<Capability> candidates = permutation.getCandidates(req);
if ((candidates != null) && (candidates.size() > 1))
{
mutated.add(req);
@@ -1082,7 +1227,7 @@
// with existing import decisions, we may end up trying
// to permutate the same import a lot of times, so we should
// try to check if that the case and only permutate it once.
- permutateIfNeeded(candidateMap, req, m_importPermutations);
+ permutateIfNeeded(allCandidates, req, m_importPermutations);
}
m_logger.log(
@@ -1111,8 +1256,8 @@
try
{
checkPackageSpaceConsistency(
- false, importBlame.m_cap.getModule(), candidateMap,
- modulePkgMap, resultCache);
+ false, importBlame.m_cap.getModule(),
+ allCandidates, modulePkgMap, resultCache);
}
catch (ResolveException ex)
{
@@ -1123,7 +1268,7 @@
if (permCount == (m_usesPermutations.size() + m_importPermutations.size()))
{
Requirement req = importBlame.m_reqs.get(0);
- permutate(candidateMap, req, m_importPermutations);
+ permutate(allCandidates, req, m_importPermutations);
}
throw ex;
}
@@ -1133,14 +1278,13 @@
}
private static void permutate(
- Map<Requirement, Set<Capability>> candidateMap, Requirement req,
- List<Map<Requirement, Set<Capability>>> permutations)
+ Candidates allCandidates, Requirement req, List<Candidates> permutations)
{
- Set<Capability> candidates = candidateMap.get(req);
+ SortedSet<Capability> candidates = allCandidates.getCandidates(req);
if (candidates.size() > 1)
{
- Map<Requirement, Set<Capability>> perm = copyCandidateMap(candidateMap);
- candidates = perm.get(req);
+ Candidates perm = allCandidates.copy();
+ candidates = perm.getCandidates(req);
Iterator it = candidates.iterator();
it.next();
it.remove();
@@ -1149,10 +1293,9 @@
}
private static void permutateIfNeeded(
- Map<Requirement, Set<Capability>> candidateMap, Requirement req,
- List<Map<Requirement, Set<Capability>>> permutations)
+ Candidates allCandidates, Requirement req, List<Candidates> permutations)
{
- Set<Capability> candidates = candidateMap.get(req);
+ SortedSet<Capability> candidates = allCandidates.getCandidates(req);
if (candidates.size() > 1)
{
// Check existing permutations to make sure we haven't
@@ -1162,9 +1305,9 @@
// initial candidate for the requirement in question,
// then it has already been permutated.
boolean permutated = false;
- for (Map<Requirement, Set<Capability>> existingPerm : permutations)
+ for (Candidates existingPerm : permutations)
{
- Set<Capability> existingPermCands = existingPerm.get(req);
+ Set<Capability> existingPermCands = existingPerm.getCandidates(req);
if (!existingPermCands.iterator().next().equals(candidates.iterator().next()))
{
permutated = true;
@@ -1174,14 +1317,14 @@
// import, do so now.
if (!permutated)
{
- permutate(candidateMap, req, permutations);
+ permutate(allCandidates, req, permutations);
}
}
}
private static void calculateExportedPackages(
Module module,
- Map<Requirement, Set<Capability>> candidateMap,
+ Candidates allCandidates,
Map<Module, Packages> modulePkgMap)
{
Packages packages = modulePkgMap.get(module);
@@ -1225,7 +1368,7 @@
{
if (req.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
{
- Set<Capability> cands = candidateMap.get(req);
+ Set<Capability> cands = allCandidates.getCandidates(req);
if ((cands != null) && !cands.isEmpty())
{
String pkgName = (String) cands.iterator().next()
@@ -1331,60 +1474,77 @@
return sources;
}
- private static Map<Requirement, Set<Capability>> copyCandidateMap(
- Map<Requirement, Set<Capability>> candidateMap)
+ private static Module getActualModule(Module m)
{
- Map<Requirement, Set<Capability>> copy =
- new HashMap<Requirement, Set<Capability>>();
- for (Entry<Requirement, Set<Capability>> entry : candidateMap.entrySet())
+ if (m instanceof WrappedModule)
{
- Set<Capability> candidates = new TreeSet(new CandidateComparator());
- candidates.addAll(entry.getValue());
- copy.put(entry.getKey(), candidates);
+ return ((WrappedModule) m).getWrappedModule();
}
- return copy;
+ return m;
+ }
+
+ private static Capability getActualCapability(Capability c)
+ {
+ if (c instanceof WrappedCapability)
+ {
+ return ((WrappedCapability) c).getWrappedCapability();
+ }
+ return c;
+ }
+
+ private static Requirement getActualRequirement(Requirement r)
+ {
+ if (r instanceof WrappedRequirement)
+ {
+ return ((WrappedRequirement) r).getWrappedRequirement();
+ }
+ return r;
}
private static Map<Module, List<Wire>> populateWireMap(
Module module, Map<Module, Packages> modulePkgMap,
- Map<Module, List<Wire>> wireMap, Map<Requirement, Set<Capability>> candidateMap)
+ Map<Module, List<Wire>> wireMap,
+ Candidates allCandidates)
{
- if (!module.isResolved() && !wireMap.containsKey(module))
+ Module unwrappedModule = getActualModule(module);
+ if (!unwrappedModule.isResolved() && !wireMap.containsKey(unwrappedModule))
{
- wireMap.put(module, (List<Wire>) Collections.EMPTY_LIST);
+ wireMap.put(unwrappedModule, (List<Wire>) Collections.EMPTY_LIST);
List<Wire> packageWires = new ArrayList<Wire>();
List<Wire> moduleWires = new ArrayList<Wire>();
for (Requirement req : module.getRequirements())
{
- Set<Capability> cands = candidateMap.get(req);
+ SortedSet<Capability> cands = allCandidates.getCandidates(req);
if ((cands != null) && (cands.size() > 0))
{
Capability cand = cands.iterator().next();
if (!cand.getModule().isResolved())
{
populateWireMap(cand.getModule(),
- modulePkgMap, wireMap, candidateMap);
+ modulePkgMap, wireMap, allCandidates);
}
// Ignore modules that import themselves.
if (req.getNamespace().equals(Capability.PACKAGE_NAMESPACE)
&& !module.equals(cand.getModule()))
{
packageWires.add(
- new WireImpl(module,
- req,
- cand.getModule(),
- cand));
+ new WireImpl(
+ unwrappedModule,
+ getActualRequirement(req),
+ getActualModule(cand.getModule()),
+ getActualCapability(cand)));
}
else if (req.getNamespace().equals(Capability.MODULE_NAMESPACE))
{
Packages candPkgs = modulePkgMap.get(cand.getModule());
moduleWires.add(
- new WireModuleImpl(module,
- req,
- cand.getModule(),
- cand,
+ new WireModuleImpl(
+ unwrappedModule,
+ getActualRequirement(req),
+ getActualModule(cand.getModule()),
+ getActualCapability(cand),
candPkgs.getExportedAndReexportedPackages()));
}
}
@@ -1392,7 +1552,28 @@
// Combine wires with module wires last.
packageWires.addAll(moduleWires);
- wireMap.put(module, packageWires);
+ wireMap.put(unwrappedModule, packageWires);
+
+ // Add host wire for any fragments.
+ if (module instanceof WrappedModule)
+ {
+ List<Module> fragments = ((WrappedModule) module).getFragments();
+ for (Module fragment : fragments)
+ {
+ List<Wire> hostWires = wireMap.get(fragment);
+ if (hostWires == null)
+ {
+ hostWires = new ArrayList<Wire>();
+ wireMap.put(fragment, hostWires);
+ }
+ hostWires.add(
+ new WireHostImpl(
+ getActualModule(fragment),
+ getHostRequirement(fragment),
+ unwrappedModule,
+ getHostCapability(unwrappedModule)));
+ }
+ }
}
return wireMap;
@@ -1400,7 +1581,7 @@
private static Map<Module, List<Wire>> populateDynamicWireMap(
Module module, String pkgName, Map<Module, Packages> modulePkgMap,
- Map<Module, List<Wire>> wireMap, Map<Requirement, Set<Capability>> candidateMap)
+ Map<Module, List<Wire>> wireMap, Candidates allCandidates)
{
wireMap.put(module, (List<Wire>) Collections.EMPTY_LIST);
@@ -1419,7 +1600,7 @@
if (!blame.m_cap.getModule().isResolved())
{
populateWireMap(blame.m_cap.getModule(), modulePkgMap, wireMap,
- candidateMap);
+ allCandidates);
}
List<Attribute> attrs = new ArrayList();
@@ -1446,40 +1627,6 @@
return wireMap;
}
- private static void dumpCandidateMap(Map<Requirement, Set<Capability>> candidateMap)
- {
- // Create set of all modules from requirements.
- Set<Module> modules = new HashSet();
- for (Entry<Requirement, Set<Capability>> entry : candidateMap.entrySet())
- {
- modules.add(entry.getKey().getModule());
- }
- // Now dump the modules.
- System.out.println("=== BEGIN CANDIDATE MAP ===");
- for (Module module : modules)
- {
- System.out.println(" " + module
- + " (" + (module.isResolved() ? "RESOLVED)" : "UNRESOLVED)"));
- for (Requirement req : module.getRequirements())
- {
- Set<Capability> candidates = candidateMap.get(req);
- if ((candidates != null) && (candidates.size() > 0))
- {
- System.out.println(" " + req + ": " + candidates);
- }
- }
- for (Requirement req : module.getDynamicRequirements())
- {
- Set<Capability> candidates = candidateMap.get(req);
- if ((candidates != null) && (candidates.size() > 0))
- {
- System.out.println(" " + req + ": " + candidates);
- }
- }
- }
- System.out.println("=== END CANDIDATE MAP ===");
- }
-
private static void dumpModulePkgMap(Map<Module, Packages> modulePkgMap)
{
System.out.println("+++MODULE PKG MAP+++");
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/WireHostImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/WireHostImpl.java
new file mode 100644
index 0000000..ca188a2
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/WireHostImpl.java
@@ -0,0 +1,87 @@
+/*
+ * 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.resolver;
+
+import java.net.URL;
+import java.util.Enumeration;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.Requirement;
+
+class WireHostImpl implements Wire
+{
+ private final Module m_importer;
+ private final Requirement m_req;
+ private final Module m_exporter;
+ private final Capability m_cap;
+
+ public WireHostImpl(Module importer, Requirement ip, Module exporter, Capability ep)
+ {
+ m_importer = importer;
+ m_req = ip;
+ m_exporter = exporter;
+ m_cap = ep;
+ }
+
+ public Module getImporter()
+ {
+ return m_importer;
+ }
+
+ public Requirement getRequirement()
+ {
+ return m_req;
+ }
+
+ public Module getExporter()
+ {
+ return m_exporter;
+ }
+
+ public Capability getCapability()
+ {
+ return m_cap;
+ }
+
+ public String toString()
+ {
+ return m_importer
+ + " -> hosted by -> "
+ + m_exporter;
+ }
+
+ public boolean hasPackage(String pkgName)
+ {
+ return false;
+ }
+
+ public Class getClass(String name) throws ClassNotFoundException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public URL getResource(String name) throws ResourceNotFoundException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Enumeration getResources(String name) throws ResourceNotFoundException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/WireImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/WireImpl.java
index 7e3076d..f2476d1 100755
--- a/framework/src/main/java/org/apache/felix/framework/resolver/WireImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/WireImpl.java
@@ -25,6 +25,7 @@
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.util.manifestparser.CapabilityImpl;
+// TODO: FRAGMENT-RESOLVER - This should probably be package private.
public class WireImpl implements Wire
{
private final Module m_importer;
@@ -62,7 +63,10 @@
public String toString()
{
- return m_req + " -> " + "[" + m_exporter + "]";
+ return "[" + m_importer + "] "
+ + m_req.getNamespace() + "; "
+ + m_req.getFilter() + " -> "
+ + "[" + m_exporter + "]";
}
/* (non-Javadoc)
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/WireModuleImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/WireModuleImpl.java
index 124c45c..00ef370 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/WireModuleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/WireModuleImpl.java
@@ -25,6 +25,7 @@
import org.apache.felix.framework.capabilityset.Requirement;
import org.apache.felix.framework.util.Util;
+// TODO: FRAGMENT-RESOLVER - This should probably be package private.
public class WireModuleImpl implements Wire
{
private final Module m_importer;
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/WrappedCapability.java b/framework/src/main/java/org/apache/felix/framework/resolver/WrappedCapability.java
new file mode 100644
index 0000000..29f5d00
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/WrappedCapability.java
@@ -0,0 +1,90 @@
+/*
+ * 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.resolver;
+
+import java.util.List;
+import org.apache.felix.framework.capabilityset.Attribute;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.Directive;
+
+class WrappedCapability implements Capability
+{
+ private final Module m_module;
+ private final Capability m_cap;
+
+ public WrappedCapability(Module module, Capability cap)
+ {
+ m_module = module;
+ m_cap = cap;
+ }
+
+ public Capability getWrappedCapability()
+ {
+ return m_cap;
+ }
+
+ public Module getModule()
+ {
+ return m_module;
+ }
+
+ public String getNamespace()
+ {
+ return m_cap.getNamespace();
+ }
+
+ public Directive getDirective(String name)
+ {
+ return m_cap.getDirective(name);
+ }
+
+ public List<Directive> getDirectives()
+ {
+ return m_cap.getDirectives();
+ }
+
+ public Attribute getAttribute(String name)
+ {
+ return m_cap.getAttribute(name);
+ }
+
+ public List<Attribute> getAttributes()
+ {
+ return m_cap.getAttributes();
+ }
+
+ public List<String> getUses()
+ {
+ return m_cap.getUses();
+ }
+
+ public String toString()
+ {
+ if (m_module == null)
+ {
+ return getAttributes().toString();
+ }
+ if (getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ return "[" + m_module + "] "
+ + getNamespace() + "; " + getAttribute(Capability.PACKAGE_ATTR);
+ }
+ return "[" + m_module + "] " + getNamespace() + "; " + getAttributes();
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/WrappedModule.java b/framework/src/main/java/org/apache/felix/framework/resolver/WrappedModule.java
new file mode 100644
index 0000000..133ebc3
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/WrappedModule.java
@@ -0,0 +1,241 @@
+/*
+ * 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.resolver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.Requirement;
+import org.apache.felix.framework.util.manifestparser.R4Library;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+class WrappedModule implements Module
+{
+ private final String m_id;
+ private final Module m_module;
+ private final List<Module> m_fragments;
+ private List<Capability> m_cachedCapabilities = null;
+ private List<Requirement> m_cachedRequirements = null;
+
+ public WrappedModule(Module module, List<Module> fragments)
+ {
+ m_id = module.getId() + " [" + super.hashCode() + "]";
+ m_module = module;
+ m_fragments = fragments;
+ }
+
+ public Module getWrappedModule()
+ {
+ return m_module;
+ }
+
+ public List<Module> getFragments()
+ {
+ return m_fragments;
+ }
+
+ public String getId()
+ {
+ return m_id;
+ }
+
+ public List<Capability> getCapabilities()
+ {
+ if (m_cachedCapabilities == null)
+ {
+ List<Capability> capList = new ArrayList<Capability>();
+
+ // Wrap host capabilities.
+ List<Capability> caps = m_module.getCapabilities();
+ for (int capIdx = 0;
+ (caps != null) && (capIdx < caps.size());
+ capIdx++)
+ {
+ capList.add(
+ new WrappedCapability(this, caps.get(capIdx)));
+ }
+
+ // Wrap fragment capabilities.
+ for (int fragIdx = 0;
+ (m_fragments != null) && (fragIdx < m_fragments.size());
+ fragIdx++)
+ {
+ caps = m_fragments.get(fragIdx).getCapabilities();
+ for (int capIdx = 0;
+ (caps != null) && (capIdx < caps.size());
+ capIdx++)
+ {
+ if (caps.get(capIdx).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ capList.add(
+ new WrappedCapability(this, caps.get(capIdx)));
+ }
+ }
+ }
+ m_cachedCapabilities = Collections.unmodifiableList(capList);
+ }
+ return m_cachedCapabilities;
+ }
+
+ public List<Requirement> getRequirements()
+ {
+ if (m_cachedRequirements == null)
+ {
+ List<Requirement> reqList = new ArrayList<Requirement>();
+
+ // Wrap host requirements.
+ List<Requirement> reqs = m_module.getRequirements();
+ for (int reqIdx = 0;
+ (reqs != null) && (reqIdx < reqs.size());
+ reqIdx++)
+ {
+ reqList.add(
+ new WrappedRequirement(this, reqs.get(reqIdx)));
+ }
+
+ // Wrap fragment requirements.
+ for (int fragIdx = 0;
+ (m_fragments != null) && (fragIdx < m_fragments.size());
+ fragIdx++)
+ {
+ reqs = m_fragments.get(fragIdx).getRequirements();
+ for (int reqIdx = 0;
+ (reqs != null) && (reqIdx < reqs.size());
+ reqIdx++)
+ {
+ if (reqs.get(reqIdx).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
+ || reqs.get(reqIdx).getNamespace().equals(Capability.MODULE_NAMESPACE))
+ {
+ reqList.add(
+ new WrappedRequirement(this, reqs.get(reqIdx)));
+ }
+ }
+ }
+ m_cachedRequirements = Collections.unmodifiableList(reqList);
+ }
+ return m_cachedRequirements;
+ }
+
+ public Map getHeaders()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public boolean isExtension()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public String getSymbolicName()
+ {
+ return m_module.getSymbolicName();
+ }
+
+ public Version getVersion()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public List<Requirement> getDynamicRequirements()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public List<R4Library> getNativeLibraries()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public int getDeclaredActivationPolicy()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Bundle getBundle()
+ {
+ return m_module.getBundle();
+ }
+
+ public List<Wire> getWires()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public boolean isResolved()
+ {
+ return false;
+ }
+
+ public Object getSecurityContext()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public boolean isRemovalPending()
+ {
+ return m_module.isRemovalPending();
+ }
+
+ public Content getContent()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Class getClassByDelegation(String name) throws ClassNotFoundException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public URL getResourceByDelegation(String name)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Enumeration getResourcesByDelegation(String name)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public URL getEntry(String name)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public boolean hasInputStream(int index, String urlPath) throws IOException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public InputStream getInputStream(int index, String urlPath) throws IOException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public URL getLocalURL(int index, String urlPath)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/WrappedRequirement.java b/framework/src/main/java/org/apache/felix/framework/resolver/WrappedRequirement.java
new file mode 100644
index 0000000..a188ce1
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/WrappedRequirement.java
@@ -0,0 +1,76 @@
+/*
+ * 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.resolver;
+
+import java.util.List;
+import org.apache.felix.framework.capabilityset.Directive;
+import org.apache.felix.framework.capabilityset.Requirement;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
+
+class WrappedRequirement implements Requirement
+{
+ private final Module m_module;
+ private final Requirement m_req;
+
+ public WrappedRequirement(Module module, Requirement req)
+ {
+ m_module = module;
+ m_req = req;
+ }
+
+ public Requirement getWrappedRequirement()
+ {
+ return m_req;
+ }
+
+ public Module getModule()
+ {
+ return m_module;
+ }
+
+ public String getNamespace()
+ {
+ return m_req.getNamespace();
+ }
+
+ public SimpleFilter getFilter()
+ {
+ return m_req.getFilter();
+ }
+
+ public boolean isOptional()
+ {
+ return m_req.isOptional();
+ }
+
+ public Directive getDirective(String name)
+ {
+ return m_req.getDirective(name);
+ }
+
+ public List<Directive> getDirectives()
+ {
+ return m_req.getDirectives();
+ }
+
+ public String toString()
+ {
+ return "[" + m_module + "] " + getNamespace() + "; " + getFilter().toString();
+ }
+}
\ No newline at end of file