Fragments are now merged into hosts at bundle install time, no longer
at resolve time. (FELIX-1534)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@831063 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 9f224d2..877da9c 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -3855,20 +3855,11 @@
                         }
                     }
 
-                    // Before trying to resolve, tell the resolver state to
-                    // merge all fragments into hosts, which may result in the
-                    // rootModule changing if the root is a fragment.
-                    IModule newRootModule;
-                    try
-                    {
-                        newRootModule = m_resolverState.mergeFragments(rootModule);
-                    }
-                    catch (Exception ex)
-                    {
-                        ex.printStackTrace();
-                        throw new ResolveException("Unable to merge fragments", rootModule, null);
-                    }
-
+                    // 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.
+                    IModule newRootModule = m_resolverState.findHost(rootModule);
                     if (!Util.isFragment(newRootModule))
                     {
                         // Resolve the module.
@@ -3915,23 +3906,6 @@
                         }
                     }
 
-                    // Before trying to resolve, tell the resolver state to
-                    // merge all fragments into their hosts.
-// TODO: FRAGMENT - We need to rethink how we do fragment merging...probably merging
-//       as bundles are installed would be better.
-                    if (Resolver.isDynamicImportAllowed(importer, pkgName))
-                    {
-                        try
-                        {
-                            m_resolverState.mergeFragments(null);
-                        }
-                        catch (Exception ex)
-                        {
-                            ex.printStackTrace();
-                            throw new ResolveException("Unable to merge fragments", importer, null);
-                        }
-                    }
-
                     Object[] result = m_resolver.resolveDynamicImport(m_resolverState, importer, pkgName);
                     if (result != null)
                     {
diff --git a/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java b/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
index 74acda0..058261e 100644
--- a/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
+++ b/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
@@ -24,6 +24,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import org.apache.felix.framework.searchpolicy.ResolveException;
 import org.apache.felix.framework.searchpolicy.Resolver;
 import org.apache.felix.framework.util.Util;
 import org.apache.felix.framework.util.manifestparser.R4Attribute;
@@ -42,7 +43,7 @@
     private final Logger m_logger;
     // List of all modules.
     private final List m_moduleList = new ArrayList();
-    // Map of fragment symbolic names to array of modules sorted version.
+    // Map of fragment symbolic names to array of fragment modules sorted by version.
     private final Map m_fragmentMap = new HashMap();
     // Maps a package name to an array of exporting capabilities.
     private final Map m_unresolvedPkgIndex = new HashMap();
@@ -60,116 +61,277 @@
         m_logger = logger;
     }
 
-    public synchronized IModule mergeFragments(IModule rootModule) throws Exception
+    public synchronized void addModule(IModule module)
+    {
+        if (Util.isFragment(module))
+        {
+            addFragment(module);
+        }
+        else
+        {
+            addHost(module);
+        }
+
+//System.out.println("UNRESOLVED PACKAGES:");
+//dumpPackageIndexMap(m_unresolvedPkgIndexMap);
+//System.out.println("RESOLVED PACKAGES:");
+//dumpPackageIndexMap(m_resolvedPkgIndexMap);
+    }
+
+    public synchronized void removeModule(IModule module)
+    {
+        if (Util.isFragment(module))
+        {
+            removeFragment(module);
+        }
+        else
+        {
+            removeHost(module);
+        }
+    }
+
+    private void addFragment(IModule fragment)
     {
 // TODO: FRAGMENT - This should check to make sure that the host allows fragments.
-        IModule newRootModule = rootModule;
-        for (int hostIdx = 0; hostIdx < m_moduleList.size(); hostIdx++)
+        IModule bestFragment = indexFragment(m_fragmentMap, fragment);
+
+        // If the newly added fragment is the highest version for
+        // its given symbolic name, then try to merge it to any
+        // matching unresolved hosts and remove the previous highest
+        // version of the fragment.
+        if (bestFragment == fragment)
         {
-            IModule host = (IModule) m_moduleList.get(hostIdx);
-            if (!host.isResolved() && !Util.isFragment(host))
+
+            // If we have any matching hosts, then merge the new fragment while
+            // removing any older version of the new fragment. Also remove host's
+            // existing capabilities from the package index and reindex its new
+            // ones after attaching the fragment.
+            List matchingHosts = getMatchingHosts(fragment);
+            for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
             {
-                ICapability[] caps = host.getCapabilities();
-                ICapability hostCap = null;
-                for (int capIdx = 0; capIdx < caps.length; capIdx++)
+                IModule host = ((ICapability) matchingHosts.get(hostIdx)).getModule();
+
+                // Get the fragments currently attached to the host so we
+                // can remove the older version of the current fragment, if any.
+                IModule[] fragments = ((ModuleImpl) host).getFragments();
+                List fragmentList = new ArrayList();
+                for (int fragIdx = 0;
+                    (fragments != null) && (fragIdx < fragments.length);
+                    fragIdx++)
                 {
-                    if (caps[capIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
+                    if (!fragments[fragIdx].getSymbolicName().equals(
+                        bestFragment.getSymbolicName()))
                     {
-                        hostCap = caps[capIdx];
-                        break;
+                        fragmentList.add(fragments[fragIdx]);
                     }
                 }
 
-                // If there is no host capability in the current module,
-                // then just ignore it.
-                if (hostCap == null)
+                // Now add the new fragment in bundle ID order.
+                int index = -1;
+                for (int listIdx = 0;
+                    (index < 0) && (listIdx < fragmentList.size());
+                    listIdx++)
                 {
-                    continue;
-                }
-
-                // Need to remove any previously attached, but not resolved fragments.
-// TODO: FRAGMENT - We need to rethink how we do fragment merging...probably merging
-//       as bundles are installed would be better.
-                ((ModuleImpl) host).attachFragments(null);
-
-                // Fragments are grouped by symbolic name and descending version.
-                // Attach the first matching fragment from each group if possible,
-                // since only one version of a given fragment may attach to a host.
-                List fragmentList = new ArrayList();
-                for (Iterator it = m_fragmentMap.entrySet().iterator(); it.hasNext(); )
-                {
-                    Map.Entry entry = (Map.Entry) it.next();
-                    IModule[] fragments = (IModule[]) entry.getValue();
-                    done: for (int fragIdx = 0; fragIdx < fragments.length; fragIdx++)
+                    IModule f = (IModule) fragmentList.get(listIdx);
+                    if (bestFragment.getBundle().getBundleId()
+                        < f.getBundle().getBundleId())
                     {
-                        IRequirement[] reqs = fragments[fragIdx].getRequirements();
-                        for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
+                        index = listIdx;
+                    }
+                }
+                fragmentList.add(
+                    (index < 0) ? fragmentList.size() : index, bestFragment);
+
+                // Remove host's existing exported packages from index.
+                ICapability[] caps = host.getCapabilities();
+                for (int i = 0; (caps != null) && (i < caps.length); i++)
+                {
+                    if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+                    {
+                        // Get package name.
+                        String pkgName = (String)
+                            caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
+                        // Remove from "unresolved" package map.
+                        IModule[] modules = (IModule[]) m_unresolvedPkgIndex.get(pkgName);
+                        if (modules != null)
                         {
-                            if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE)
-                                && reqs[reqIdx].isSatisfied(hostCap)
-                                && !((BundleImpl) fragments[fragIdx].getBundle()).isStale()
-                                && !((BundleImpl) fragments[fragIdx].getBundle()).isRemovalPending())
-                            {
-                                // Fragments are attached in bundle ID order.
-                                int index = -1;
-                                for (int listIdx = 0;
-                                    (index < 0) && (listIdx < fragmentList.size());
-                                    listIdx++)
-                                {
-                                    if (fragments[fragIdx].getBundle().getBundleId()
-                                        < ((IModule) fragmentList.get(listIdx)).getBundle().getBundleId())
-                                    {
-                                        index = listIdx;
-                                    }
-                                }
-                                fragmentList.add(
-                                    (index < 0) ? fragmentList.size() : index,
-                                    fragments[fragIdx]);
-                                break done;
-                            }
+                            modules = removeModuleFromArray(modules, fragment);
+                            m_unresolvedPkgIndex.put(pkgName, modules);
                         }
                     }
                 }
 
-                if (fragmentList.size() > 0)
+                // Check if fragment conflicts with existing metadata.
+                checkForConflicts(host, fragmentList);
+
+                // Attach the fragments to the host.
+                fragments = (fragmentList.size() == 0)
+                    ? null
+                    : (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
+                try
                 {
+                    ((ModuleImpl) host).attachFragments(fragments);
+                }
+                catch (Exception ex)
+                {
+                    // Try to clean up by removing all fragments.
+                    try
+                    {
+                        ((ModuleImpl) host).attachFragments(null);
+                    }
+                    catch (Exception ex2)
+                    {
+                    }
+                    m_logger.log(Logger.LOG_ERROR,
+                        "Serious error attaching fragments.", ex);
+                }
+
+                // Reindex the host's exported packages.
+                caps = host.getCapabilities();
+                for (int i = 0; (caps != null) && (i < caps.length); i++)
+                {
+                    if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+                    {
+                        indexPackageCapability(m_unresolvedPkgIndex, host, caps[i]);
+                    }
+                }
+            }
+        }
+    }
+
+    private void removeFragment(IModule fragment)
+    {
+        // If module is a fragment, then remove from fragment map.
+        IModule[] fragments = (IModule[]) m_fragmentMap.get(fragment.getSymbolicName());
+        fragments = removeModuleFromArray(fragments, fragment);
+        if (fragments.length == 0)
+        {
+            m_fragmentMap.remove(fragment.getSymbolicName());
+        }
+        else
+        {
+            m_fragmentMap.put(fragment.getSymbolicName(), fragments);
+        }
+
+        // If we have any matching hosts, then remove  fragment while
+        // removing any older version of the new fragment. Also remove host's
+        // existing capabilities from the package index and reindex its new
+        // ones after attaching the fragment.
+        List matchingHosts = getMatchingHosts(fragment);
+        for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
+        {
+            IModule host = ((ICapability) matchingHosts.get(hostIdx)).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.
+            fragments = ((ModuleImpl) host).getFragments();
+            for (int fragIdx = 0; (fragments != null) && (fragIdx < fragments.length); fragIdx++)
+            {
+                if (!fragments[fragIdx].equals(fragment))
+                {
+                    List fragmentList = getMatchingFragments(host);
+
+                    // Remove host's existing exported packages from index.
+                    ICapability[] caps = host.getCapabilities();
+                    for (int i = 0; (caps != null) && (i < caps.length); i++)
+                    {
+                        if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+                        {
+                            // Get package name.
+                            String pkgName = (String)
+                                caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
+                            // Remove from "unresolved" package map.
+                            IModule[] modules = (IModule[]) m_unresolvedPkgIndex.get(pkgName);
+                            if (modules != null)
+                            {
+                                modules = removeModuleFromArray(modules, fragment);
+                                m_unresolvedPkgIndex.put(pkgName, modules);
+                            }
+                        }
+                    }
+
                     // Check if fragment conflicts with existing metadata.
                     checkForConflicts(host, fragmentList);
 
                     // Attach the fragments to the host.
-                    IModule[] fragments = (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
-                    ((ModuleImpl) host).attachFragments(fragments);
-
-                    for (int fragIdx = 0; fragIdx < fragments.length; fragIdx++)
+                    fragments = (fragmentList.size() == 0)
+                        ? null
+                        : (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
+                    try
                     {
-                        // Check to see if the root module is actually a fragment,
-                        // if so then we want to return the host for resolving.
-                        if (rootModule == fragments[fragIdx])
+                        ((ModuleImpl) host).attachFragments(fragments);
+                    }
+                    catch (Exception ex)
+                    {
+                        // Try to clean up by removing all fragments.
+                        try
                         {
-                            newRootModule = host;
+                            ((ModuleImpl) host).attachFragments(null);
                         }
-
-                        // Add each fragment capabililty to the resolver state
-                        // data structures.
-                        ICapability[] fragCaps = fragments[fragIdx].getCapabilities();
-                        for (int capIdx = 0; (fragCaps != null) && (capIdx < fragCaps.length); capIdx++)
+                        catch (Exception ex2)
                         {
-                            if (fragCaps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
-                            {
-                                indexPackageCapability(
-                                    m_unresolvedPkgIndex, host, fragCaps[capIdx]);
-                            }
+                        }
+                        m_logger.log(Logger.LOG_ERROR,
+                            "Serious error attaching fragments.", ex);
+                    }
+
+                    // Reindex the host's exported packages.
+                    caps = host.getCapabilities();
+                    for (int i = 0; (caps != null) && (i < caps.length); i++)
+                    {
+                        if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+                        {
+                            indexPackageCapability(m_unresolvedPkgIndex, host, caps[i]);
                         }
                     }
                 }
             }
         }
+    }
 
-        return newRootModule;
+    private List getMatchingHosts(IModule fragment)
+    {
+        // Find the fragment's host requirement.
+        IRequirement hostReq = getFragmentHostRequirement(fragment);
+
+        // Create a list of all matching hosts for this fragment.
+        List matchingHosts = new ArrayList();
+        for (int hostIdx = 0; (hostReq != null) && (hostIdx < m_moduleList.size()); hostIdx++)
+        {
+            IModule host = (IModule) m_moduleList.get(hostIdx);
+            // Only look at unresolved hosts, since we don't support
+            // dynamic attachment of fragments.
+            if (host.isResolved()
+                || ((BundleImpl) host.getBundle()).isStale()
+                || ((BundleImpl) host.getBundle()).isRemovalPending())
+            {
+                continue;
+            }
+
+            // Find the host capability for the current host.
+            ICapability hostCap = Util.getSatisfyingCapability(host, hostReq);
+
+            // If there is no host capability in the current module,
+            // then just ignore it.
+            if (hostCap == null)
+            {
+                continue;
+            }
+
+            matchingHosts.add(hostCap);
+        }
+
+        return matchingHosts;
     }
 
     private void checkForConflicts(IModule host, List fragmentList)
     {
+        if ((fragmentList == null) || (fragmentList.size() == 0))
+        {
+            return;
+        }
+
         // Verify the fragments do not have conflicting imports.
         // For now, just check for duplicate imports, but in the
         // future we might want to make this more fine grained.
@@ -211,10 +373,12 @@
                     || reqs[reqIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
                 {
                     String targetName = ((Requirement) reqs[reqIdx]).getTargetName();
-                    Map mergedReqMap = (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
-                        ? ipMerged : rbMerged;
-                    Map fragmentReqMap = (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
-                        ? ipFragment : rbFragment;
+                    Map mergedReqMap =
+                        (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+                            ? ipMerged : rbMerged;
+                    Map fragmentReqMap =
+                        (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+                            ? ipFragment : rbFragment;
                     Object[] existing = (Object[]) mergedReqMap.get(targetName);
                     if (existing == null)
                     {
@@ -359,114 +523,213 @@
         return false;
     }
 
-    private void addFragment(IModule module)
+    private void addHost(IModule host)
     {
-        indexFragment(m_fragmentMap, module);
-//        System.out.println("+++ BEGIN FRAGMENT DUMP");
-//        dumpModuleIndexMap(m_fragmentMap);
-//        System.out.println("+++ END FRAGMENT DUMP");
-    }
+        // 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_moduleList.add(host);
 
-    public synchronized void addModule(IModule module)
-    {
-        if (Util.isFragment(module))
+        //
+        // First, merge applicable fragments.
+        //
+
+        List fragmentList = getMatchingFragments(host);
+
+        // Attach any fragments we found for this host.
+        if (fragmentList.size() > 0)
         {
-            addFragment(module);
-        }
-        else
-        {
-            // When a module is added, create an aggregated list of unresolved
-            // exports to simplify later processing when resolving bundles.
-            m_moduleList.add(module);
+            // Check if fragment conflicts with existing metadata.
+            checkForConflicts(host, fragmentList);
 
-            ICapability[] caps = module.getCapabilities();
-
-            // Add exports to unresolved package map.
-            for (int i = 0; (caps != null) && (i < caps.length); i++)
-            {
-                if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
-                {
-                    indexPackageCapability(m_unresolvedPkgIndex, module, caps[i]);
-                }
-            }
-        }
-
-//System.out.println("UNRESOLVED PACKAGES:");
-//dumpPackageIndexMap(m_unresolvedPkgIndexMap);
-//System.out.println("RESOLVED PACKAGES:");
-//dumpPackageIndexMap(m_resolvedPkgIndexMap);
-    }
-
-    public synchronized void removeModule(IModule module)
-    {
-        // Depending on whether the module is a fragment or not,
-        // we need to do different things.
-
-        // If module is a fragment, then remove from fragment map.
-        if (Util.isFragment(module))
-        {
-            IModule[] fragments = (IModule[]) m_fragmentMap.get(module.getSymbolicName());
-            fragments = removeModuleFromArray(fragments, module);
-            if (fragments.length == 0)
-            {
-                m_fragmentMap.remove(module.getSymbolicName());
-            }
-            else
-            {
-                m_fragmentMap.put(module.getSymbolicName(), fragments);
-            }
-        }
-        // If it is not a fragment, then we need remove its exports
-        // from the "resolved" and "unresolved" package maps, remove
-        // the module's dependencies on fragments and exporters,
-        // and remove the module from the module list.
-        else
-        {
-            m_moduleList.remove(module);
-
-            // Remove exports from package maps.
-            ICapability[] caps = module.getCapabilities();
-            for (int i = 0; (caps != null) && (i < caps.length); i++)
-            {
-                if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
-                {
-                    // Get package name.
-                    String pkgName = (String)
-                        caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
-                    // Remove from "unresolved" package map.
-                    IModule[] modules = (IModule[]) m_unresolvedPkgIndex.get(pkgName);
-                    if (modules != null)
-                    {
-                        modules = removeModuleFromArray(modules, module);
-                        m_unresolvedPkgIndex.put(pkgName, modules);
-                    }
-
-                    // Remove from "resolved" package map.
-                    modules = (IModule[]) m_resolvedPkgIndex.get(pkgName);
-                    if (modules != null)
-                    {
-                        modules = removeModuleFromArray(modules, module);
-                        m_resolvedPkgIndex.put(pkgName, modules);
-                    }
-                }
-            }
-
-            // Remove the module from the "resolved" map.
-            m_resolvedCapMap.remove(module);
-            // Set fragments to null, which will remove the module from all
-            // of its dependent fragment modules.
+            // Attach the fragments to the host.
+            IModule[] fragments =
+                (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
             try
             {
-                ((ModuleImpl) module).attachFragments(null);
+                ((ModuleImpl) host).attachFragments(fragments);
             }
             catch (Exception ex)
             {
-                m_logger.log(Logger.LOG_ERROR, "Error detaching fragments.", ex);
+                // Try to clean up by removing all fragments.
+                try
+                {
+                    ((ModuleImpl) host).attachFragments(null);
+                }
+                catch (Exception ex2)
+                {
+                }
+                m_logger.log(Logger.LOG_ERROR,
+                    "Serious error attaching fragments.", ex);
             }
-            // Set wires to null, which will remove the module from all
-            // of its dependent modules.
-            ((ModuleImpl) module).setWires(null);
         }
+
+        //
+        // Second, index module's capabilities.
+        //
+
+        ICapability[] caps = host.getCapabilities();
+
+        // Add exports to unresolved package map.
+        for (int i = 0; (caps != null) && (i < caps.length); i++)
+        {
+            if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+            {
+                indexPackageCapability(m_unresolvedPkgIndex, host, caps[i]);
+            }
+        }
+    }
+
+    private void removeHost(IModule 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_moduleList.remove(host);
+
+        // Remove exports from package maps.
+        ICapability[] caps = host.getCapabilities();
+        for (int i = 0; (caps != null) && (i < caps.length); i++)
+        {
+            if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+            {
+                // Get package name.
+                String pkgName = (String)
+                    caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
+                // Remove from "unresolved" package map.
+                IModule[] modules = (IModule[]) m_unresolvedPkgIndex.get(pkgName);
+                if (modules != null)
+                {
+                    modules = removeModuleFromArray(modules, host);
+                    m_unresolvedPkgIndex.put(pkgName, modules);
+                }
+
+                // Remove from "resolved" package map.
+                modules = (IModule[]) m_resolvedPkgIndex.get(pkgName);
+                if (modules != null)
+                {
+                    modules = removeModuleFromArray(modules, host);
+                    m_resolvedPkgIndex.put(pkgName, modules);
+                }
+            }
+        }
+
+        // Remove the module from the "resolved" map.
+        m_resolvedCapMap.remove(host);
+        // 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(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 getMatchingFragments(IModule host)
+    {
+        // Find the host capability for the current host.
+        ICapability[] caps = Util.getCapabilityByNamespace(host, ICapability.HOST_NAMESPACE);
+        ICapability hostCap = (caps.length == 0) ? null : caps[0];
+
+        // If we have a host capability, then loop through all fragments trying to
+        // find ones that match.
+        List fragmentList = new ArrayList();
+        for (Iterator it = m_fragmentMap.entrySet().iterator(); (hostCap != null) && it.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) it.next();
+            IModule[] fragments = ((IModule[]) entry.getValue());
+            IModule fragment = null;
+            for (int i = 0; (fragment == null) && (i < fragments.length); i++)
+            {
+                if (!((BundleImpl) fragments[i].getBundle()).isStale()
+                    && !((BundleImpl) fragments[i].getBundle()).isRemovalPending())
+                {
+                    fragment = fragments[i];
+                }
+            }
+
+            if (fragment == null)
+            {
+                continue;
+            }
+
+            IRequirement 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) && hostReq.isSatisfied(hostCap))
+            {
+                // Now add the new fragment in bundle ID order.
+                int index = -1;
+                for (int listIdx = 0;
+                    (index < 0) && (listIdx < fragmentList.size());
+                    listIdx++)
+                {
+                    IModule existing = (IModule) fragmentList.get(listIdx);
+                    if (fragment.getBundle().getBundleId()
+                        < existing.getBundle().getBundleId())
+                    {
+                        index = listIdx;
+                    }
+                }
+                fragmentList.add(
+                    (index < 0) ? fragmentList.size() : index, fragment);
+            }
+        }
+
+        return fragmentList;
+    }
+
+    public synchronized IModule findHost(IModule rootModule) throws ResolveException
+    {
+        IModule newRootModule = rootModule;
+        if (Util.isFragment(rootModule))
+        {
+            List matchingHosts = getMatchingHosts(rootModule);
+            IModule currentBestHost = null;
+            for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
+            {
+                IModule host = ((ICapability) matchingHosts.get(hostIdx)).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 IRequirement getFragmentHostRequirement(IModule fragment)
+    {
+        // Find the fragment's host requirement.
+        IRequirement[] reqs = fragment.getRequirements();
+        IRequirement hostReq = null;
+        for (int reqIdx = 0; (hostReq == null) && (reqIdx < reqs.length); reqIdx++)
+        {
+            if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
+            {
+                hostReq = reqs[reqIdx];
+            }
+        }
+        return hostReq;
     }
 
     /**
@@ -819,7 +1082,7 @@
         }
     }
 
-    private void indexFragment(Map map, IModule module)
+    private IModule indexFragment(Map map, IModule module)
     {
         IModule[] modules = (IModule[]) map.get(module.getSymbolicName());
 
@@ -878,6 +1141,8 @@
         }
 
         map.put(module.getSymbolicName(), modules);
+
+        return modules[0];
     }
 
     private static IModule[] removeModuleFromArray(IModule[] modules, IModule m)