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;
     }