If a fragment bundle is uninstalled, it must be unmerged from any unresolved
hosts to avoid causing an exception.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@904631 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 587098b..9226422 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -134,6 +134,21 @@
         }
     }
 
+    /**
+     * 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.length; i++)
+        {
+            getFramework().getResolverState().unmergeFragment(m_modules[i]);
+        }
+    }
+
     synchronized void refresh() throws Exception
     {
         if (isExtension() && (getFramework().getState() != Bundle.STOPPING))
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 b1e3b92..7db8b23 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -2261,6 +2261,10 @@
             // 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
         {
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 367ef0c..8f89e6e 100644
--- a/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
+++ b/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
@@ -288,6 +288,101 @@
         }
     }
 
+    public void unmergeFragment(IModule module)
+    {
+        if (!Util.isFragment(module))
+        {
+            return;
+        }
+
+        // Get fragment list, which may be null for system bundle fragments.
+        List fragList = (List) m_fragmentMap.get(module.getSymbolicName());
+        if (fragList != null)
+        {
+            // Remove from fragment map.
+            fragList.remove(module);
+            if (fragList.size() == 0)
+            {
+                m_fragmentMap.remove(module.getSymbolicName());
+            }
+
+            // 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(module);
+            for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
+            {
+                IModule host = ((ICapability) matchingHosts.get(hostIdx)).getModule();
+                // Find any unresolved hosts into which the fragment is merged
+                // and unmerge it.
+                IModule[] fragments = ((ModuleImpl) host).getFragments();
+                for (int fragIdx = 0;
+                    !host.isResolved() && (fragments != null) && (fragIdx < fragments.length);
+                    fragIdx++)
+                {
+                    if (!fragments[fragIdx].equals(module))
+                    {
+                        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.
+                                List capList = (List) m_unresolvedPkgIndex.get(pkgName);
+                                if (capList != null)
+                                {
+                                    capList.remove(caps[i]);
+                                }
+                            }
+                        }
+
+                        // 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, caps[i]);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     private List getMatchingHosts(IModule fragment)
     {
         // Find the fragment's host requirement.