Initial refactoring to move fragment handling into the resolver. (FELIX-2858)


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