You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2011/02/23 22:30:43 UTC
svn commit: r1073953 [1/2] - in
/felix/trunk/framework/src/main/java/org/apache/felix/framework: ./
capabilityset/ resolver/
Author: rickhall
Date: Wed Feb 23 21:30:42 2011
New Revision: 1073953
URL: http://svn.apache.org/viewvc?rev=1073953&view=rev
Log:
Initial refactoring to move fragment handling into the resolver. (FELIX-2858)
Added:
felix/trunk/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/WireHostImpl.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/WrappedCapability.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/WrappedModule.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/WrappedRequirement.java
Removed:
felix/trunk/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
Modified:
felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Module.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/WireImpl.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/WireModuleImpl.java
Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java?rev=1073953&r1=1073952&r2=1073953&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java Wed Feb 23 21:30:42 2011
@@ -124,25 +124,28 @@ class BundleImpl implements Bundle
{
// 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 @@ class BundleImpl implements Bundle
{
// 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 @@ class BundleImpl implements Bundle
{
// 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);
}
}
Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java?rev=1073953&r1=1073952&r2=1073953&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java Wed Feb 23 21:30:42 2011
@@ -36,7 +36,7 @@ import java.util.Map;
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 @@ class ExtensionManager extends URLStream
{
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 @@ class ExtensionManager extends URLStream
return null;
}
- public FelixResolver getResolver()
+ public StatefulResolver getResolver()
{
return null;
}
Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java?rev=1073953&r1=1073952&r2=1073953&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java Wed Feb 23 21:30:42 2011
@@ -24,7 +24,6 @@ import java.net.*;
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.
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 @@ public class Felix extends BundleImpl im
// 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 @@ public class Felix extends BundleImpl im
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 @@ public class Felix extends BundleImpl im
return m_configMap;
}
- FelixResolver getResolver()
+ StatefulResolver getResolver()
{
- return m_felixResolver;
- }
-
- FelixResolverState getResolverState()
- {
- return m_resolverState;
+ return m_resolver;
}
URLStreamHandler getBundleStreamHandler()
@@ -641,7 +636,7 @@ public class Felix extends BundleImpl im
// state to be set to RESOLVED.
try
{
- m_felixResolver.resolve(getCurrentModule());
+ m_resolver.resolve(getCurrentModule());
}
catch (ResolveException ex)
{
@@ -1989,7 +1984,7 @@ public class Felix extends BundleImpl im
{
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 @@ public class Felix extends BundleImpl im
// 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 @@ public class Felix extends BundleImpl im
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 @@ public class Felix extends BundleImpl im
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 @@ public class Felix extends BundleImpl im
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 @@ public class Felix extends BundleImpl im
{
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 @@ public class Felix extends BundleImpl im
// 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 @@ public class Felix extends BundleImpl im
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);
-
- boolean repeat;
- do
- {
- repeat = false;
- try
- {
- // Resolve the module.
- wireMap = m_resolver.resolve(m_resolverState, newRootModule);
+ // Check singleton status.
+// TOOD: FRAGMENT RESOLVER - Merge singleton handling into resolver.
+// m_resolverState.checkSingleton(rootModule);
+
+ // 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 class Felix extends BundleImpl im
}
}
- 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 @@ public class Felix extends BundleImpl im
}
}
- 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 @@ public class Felix extends BundleImpl im
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 @@ public class Felix extends BundleImpl im
}
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 @@ public class Felix extends BundleImpl im
}
catch (Throwable throwable)
{
- m_logger.log(Felix.this,
+ m_logger.log(
Logger.LOG_WARNING,
"Exception stopping a system bundle activator.",
throwable);
Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java?rev=1073953&r1=1073952&r2=1073953&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java Wed Feb 23 21:30:42 2011
@@ -38,8 +38,10 @@ import java.util.HashSet;
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 @@ public class ModuleImpl implements Modul
{
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 class ModuleImpl implements Modul
}
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 class ModuleImpl implements Modul
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 @@ public class ModuleImpl implements Modul
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 @@ public class ModuleImpl implements Modul
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 @@ public class ModuleImpl implements Modul
// 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 @@ public class ModuleImpl implements Modul
((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 @@ public class ModuleImpl implements Modul
// 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 @@ public class ModuleImpl implements Modul
{
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 @@ public class ModuleImpl implements Modul
}
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.
Added: felix/trunk/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java?rev=1073953&view=auto
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java (added)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java Wed Feb 23 21:30:42 2011
@@ -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
Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java?rev=1073953&r1=1073952&r2=1073953&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java Wed Feb 23 21:30:42 2011
@@ -36,7 +36,7 @@ import org.apache.felix.framework.util.S
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 class CapabilitySet
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 class CapabilitySet
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 class CapabilitySet
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;
Added: felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java?rev=1073953&view=auto
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java (added)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java Wed Feb 23 21:30:42 2011
@@ -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
Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Module.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Module.java?rev=1073953&r1=1073952&r2=1073953&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Module.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Module.java Wed Feb 23 21:30:42 2011
@@ -52,6 +52,11 @@ public interface Module
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();
Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java?rev=1073953&r1=1073952&r2=1073953&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java Wed Feb 23 21:30:42 2011
@@ -21,17 +21,20 @@ package org.apache.felix.framework.resol
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;
}