Initial patch to address fragment support. The included patch is only the
first step and is very minimal; specifically, it makes it possible to use
fragments to extend the host bundle's class path and nothing more. It
is not intended for real use yet, since the fragments are not hooked into
the update/refresh mechanism of Package Admin. (FELIX-29)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@662133 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 6bd3b80..521b46f 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -113,11 +113,11 @@
 
     /**
      * Creates a new Felix framework instance with a default logger.
-     * 
+     *
      * @param configMutableMap An map for obtaining configuration properties,
      *        may be <tt>null</tt>.
      * @param activatorList A list of System Bundle activators.
-     * 
+     *
      * @see #Felix(Logger, Map, List)
      */
     public Felix(Map configMutableMap, List activatorList)
@@ -570,7 +570,7 @@
     {
         return true;
     }
-    
+
     Object getSignerMatcher()
     {
         return null;
@@ -717,7 +717,7 @@
             archives = null;
         }
 
-        // create the system bundle that is responsible for providing specific 
+        // create the system bundle that is responsible for providing specific
         // container related services.
         IContentLoader cl = m_extensionManager;
         cl.setSearchPolicy(
@@ -741,9 +741,9 @@
         // 2) install all bundles from cache (will start extension bundles)
         // 3) start the system bundle (will start custom activators)
         // 4) start the other bundles via the start level
-        // it is important to keep this order because bundles are installed 
-        // from added activators in step 3 and extension bundles are started 
-        // in 2 and need the stuff from 1. 
+        // it is important to keep this order because bundles are installed
+        // from added activators in step 3 and extension bundles are started
+        // in 2 and need the stuff from 1.
         m_installedBundleMap.put(
             m_systemBundleInfo.getLocation(), this);
         m_installedBundleIndex.put(new Long(0), this);
@@ -755,7 +755,7 @@
         // then activate it.
         m_systemBundleInfo.setBundleContext(
             new BundleContextImpl(m_logger, this, this));
-        
+
         FelixBundle bundle = null;
 
         // Now install all cached bundles.
@@ -828,7 +828,7 @@
             }
         }
 
-        // Now that the cached bundles are reloaded, 
+        // Now that the cached bundles are reloaded,
         // activating all custom framework activators.
         try
         {
@@ -845,8 +845,8 @@
                     "Unresolved package in System Bundle:"
                     + ex.getRequirement());
             }
-            
-            Felix.m_secureAction.startActivator(m_systemBundleInfo.getActivator(), 
+
+            Felix.m_secureAction.startActivator(m_systemBundleInfo.getActivator(),
                 m_systemBundleInfo.getBundleContext());
         }
         catch (Throwable ex)
@@ -867,7 +867,7 @@
         try
         {
             StartLevel sl = (StartLevel) getService(
-                getBundle(0),getServiceReferences((FelixBundle) getBundle(0), 
+                getBundle(0),getServiceReferences((FelixBundle) getBundle(0),
                 StartLevel.class.getName(), null, true)[0]);
             if (sl instanceof StartLevelImpl)
             {
@@ -1457,8 +1457,8 @@
             {
                 return (obj instanceof java.security.Permission)
                     ? impliesBundlePermission(
-                    (BundleProtectionDomain) 
-                    bundle.getInfo().getProtectionDomain(), 
+                    (BundleProtectionDomain)
+                    bundle.getInfo().getProtectionDomain(),
                     (java.security.Permission) obj, true)
                     : false;
             }
@@ -1666,7 +1666,7 @@
         // to import the necessary packages.
         if (System.getSecurityManager() != null)
         {
-            BundleProtectionDomain pd = (BundleProtectionDomain) 
+            BundleProtectionDomain pd = (BundleProtectionDomain)
                 bundle.getInfo().getProtectionDomain();
 
             IRequirement[] imports =
@@ -1813,7 +1813,7 @@
                         info.getBundleId(),
                         archive.getRevisionCount() - 1,
                         info.getCurrentHeader(),
-                        (bundle.getInfo().isExtension() || 
+                        (bundle.getInfo().isExtension() ||
                         m_extensionManager.isExtensionBundle(
                             bundle.getInfo().getCurrentHeader())));
 
@@ -2317,7 +2317,7 @@
                     archive.getRevisionCount() - 1).getManifestHeader())));
 
                 verifyExecutionEnvironment(bundle);
-                
+
                 checkFragment(bundle);
 
                 addSecurity(bundle);
@@ -2464,7 +2464,7 @@
         if (fragmentHost != null)
         {
             m_logger.log(Logger.LOG_WARNING, "Bundle " + bundle.getBundleId()
-                + " is a fragment bundle. Fragment bundles are not yet supported!");
+                + " is a fragment bundle; fragment are only partially supported!");
         }
     }
 
@@ -2872,7 +2872,7 @@
             {
                 return m_systemBundleInfo.getArchive().getDataFile(s);
             }
-            
+
             return m_cache.getArchive(
                 bundle.getBundleId()).getDataFile(s);
         }
@@ -3347,7 +3347,7 @@
         // ever be one revision at this point, create the module for
         // the current revision to be safe.
         IModule module = createModule(
-            archive.getId(), archive.getRevisionCount() - 1, headerMap, 
+            archive.getId(), archive.getRevisionCount() - 1, headerMap,
             isExtension);
 
         // Finally, create an return the bundle info.
@@ -3932,7 +3932,7 @@
             {
                 try
                 {
-                    Felix.m_secureAction.stopActivator((BundleActivator) 
+                    Felix.m_secureAction.stopActivator((BundleActivator)
                         m_activatorList.get(i), getInfo().getBundleContext());
                 }
                 catch (Throwable throwable)
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java b/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
index c4d2301..f82f4fc 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
@@ -28,7 +28,6 @@
 {
     private IContent m_content = null;
     private String m_rootPath = null;
-    private boolean m_opened = false;
 
     public ContentDirectoryContent(IContent content, String path)
     {
@@ -38,12 +37,6 @@
             ? path + "/" : path;
     }
 
-    public void open()
-    {
-        m_content.open();
-        m_opened = true;
-    }
-
     public synchronized void close()
     {
         // We do not actually close the associated content
@@ -51,16 +44,10 @@
         // we assume that this will be close manually by
         // the owner of that content.
         m_content = null;
-        m_opened = false;
     }
 
     public synchronized boolean hasEntry(String name) throws IllegalStateException
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("ContentDirectoryContent is not open");
-        }
-
         if ((name.length() > 0) && (name.charAt(0) == '/'))
         {
             name = name.substring(1);
@@ -71,21 +58,11 @@
 
     public synchronized Enumeration getEntries()
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("ContentDirectoryContent is not open");
-        }
-
         return new EntriesEnumeration(m_content.getEntries(), m_rootPath);
     }
 
     public synchronized byte[] getEntryAsBytes(String name) throws IllegalStateException
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("ContentDirectoryContent is not open");
-        }
-
         if ((name.length() > 0) && (name.charAt(0) == '/'))
         {
             name = name.substring(1);
@@ -97,11 +74,6 @@
     public synchronized InputStream getEntryAsStream(String name)
         throws IllegalStateException, IOException
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("ContentDirectoryContent is not open");
-        }
-
         if ((name.length() > 0) && (name.charAt(0) == '/'))
         {
             name = name.substring(1);
@@ -112,11 +84,6 @@
 
     public IContent getEntryAsContent(String name)
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("ContentDirectoryContent is not open");
-        }
-
         if ((name.length() > 0) && (name.charAt(0) == '/'))
         {
             name = name.substring(1);
@@ -127,11 +94,6 @@
 
     public String getEntryAsNativeLibrary(String name)
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("ContentDirectoryContent is not open");
-        }
-
         if ((name.length() > 0) && (name.charAt(0) == '/'))
         {
             name = name.substring(1);
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
index feed684..c7be028 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
@@ -22,6 +22,7 @@
 import java.io.*;
 import java.util.*;
 import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.FelixConstants;
 
 public class DirectoryContent implements IContent
 {
@@ -33,7 +34,6 @@
     private Object m_revisionLock;
     private File m_rootDir;
     private File m_dir;
-    private boolean m_opened = false;
 
     public DirectoryContent(Logger logger, Object revisionLock, File rootDir, File dir)
     {
@@ -43,23 +43,13 @@
         m_dir = dir;
     }
 
-    public void open()
+    public void close()
     {
-        m_opened = true;
-    }
-
-    public synchronized void close()
-    {
-        m_opened = false;
+        // Nothing to clean up.
     }
 
     public synchronized boolean hasEntry(String name) throws IllegalStateException
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("DirectoryContent is not open");
-        }
-
         if ((name.length() > 0) && (name.charAt(0) == '/'))
         {
             name = name.substring(1);
@@ -70,11 +60,6 @@
 
     public synchronized Enumeration getEntries()
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("JarContent is not open");
-        }
-
         // Wrap entries enumeration to filter non-matching entries.
         Enumeration e = new EntriesEnumeration(m_dir);
 
@@ -84,11 +69,6 @@
 
     public synchronized byte[] getEntryAsBytes(String name) throws IllegalStateException
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("DirectoryContent is not open");
-        }
-
         if ((name.length() > 0) && (name.charAt(0) == '/'))
         {
             name = name.substring(1);
@@ -137,11 +117,6 @@
     public synchronized InputStream getEntryAsStream(String name)
         throws IllegalStateException, IOException
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("DirectoryContent is not open");
-        }
-
         if ((name.length() > 0) && (name.charAt(0) == '/'))
         {
             name = name.substring(1);
@@ -152,9 +127,11 @@
 
     public synchronized IContent getEntryAsContent(String entryName)
     {
-        if (!m_opened)
+        // If the entry name refers to the content itself, then
+        // just return it immediately.
+        if (entryName.equals(FelixConstants.CLASS_PATH_DOT))
         {
-            throw new IllegalStateException("DirectoryContent is not open");
+            return new DirectoryContent(m_logger, m_revisionLock, m_rootDir, m_dir);
         }
 
         // Remove any leading slash, since all bundle class path
@@ -202,11 +179,6 @@
 // TODO: This will need to consider security.
     public synchronized String getEntryAsNativeLibrary(String name)
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("DirectoryContent is not open");
-        }
-
         return BundleCache.getSecureAction().getAbsolutePath(new File(m_rootDir, name));
     }
 
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java b/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
index ba317a8..40be45a 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
@@ -26,6 +26,7 @@
 import java.util.Enumeration;
 import java.util.zip.ZipEntry;
 import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.FelixConstants;
 import org.apache.felix.framework.util.JarFileX;
 import org.apache.felix.moduleloader.IContent;
 
@@ -43,7 +44,6 @@
     private JarFileX m_jarFile = null;
     // TODO: CACHE - It would be nice to eventually remove this legacy flag.
     private final boolean m_legacy;
-    private boolean m_opened = false;
 
     public JarContent(Logger logger, Object revisionLock, File rootDir, File file)
     {
@@ -79,11 +79,6 @@
         }
     }
 
-    public synchronized void open()
-    {
-        m_opened = true;
-    }
-
     public synchronized void close()
     {
         try
@@ -101,16 +96,10 @@
         }
 
         m_jarFile = null;
-        m_opened = false;
     }
 
     public synchronized boolean hasEntry(String name) throws IllegalStateException
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("JarContent is not open");
-        }
-
         // Open JAR file if not already opened.
         if (m_jarFile == null)
         {
@@ -143,11 +132,6 @@
 
     public synchronized Enumeration getEntries()
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("JarContent is not open");
-        }
-
         // Open JAR file if not already opened.
         if (m_jarFile == null)
         {
@@ -173,11 +157,6 @@
 
     public synchronized byte[] getEntryAsBytes(String name) throws IllegalStateException
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("JarContent is not open");
-        }
-
         // Open JAR file if not already opened.
         if (m_jarFile == null)
         {
@@ -249,11 +228,6 @@
     public synchronized InputStream getEntryAsStream(String name)
         throws IllegalStateException, IOException
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("JarContent is not open");
-        }
-
         // Open JAR file if not already opened.
         if (m_jarFile == null)
         {
@@ -296,11 +270,6 @@
 
     public synchronized IContent getEntryAsContent(String entryName)
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("JarContent is not open");
-        }
-
         // Open JAR file if not already opened.
         if (m_jarFile == null)
         {
@@ -318,6 +287,13 @@
 
         }
 
+        // If the entry name refers to the content itself, then
+        // just return it immediately.
+        if (entryName.equals(FelixConstants.CLASS_PATH_DOT))
+        {
+            return new JarContent(m_logger, m_revisionLock, m_rootDir, m_file, m_legacy);
+        }
+
         // Remove any leading slash.
         entryName = (entryName.startsWith("/")) ? entryName.substring(1) : entryName;
 
@@ -400,11 +376,6 @@
 // TODO: This will need to consider security.
     public synchronized String getEntryAsNativeLibrary(String name)
     {
-        if (!m_opened)
-        {
-            throw new IllegalStateException("JarContent is not open");
-        }
-
         // Open JAR file if not already opened.
         if (m_jarFile == null)
         {
@@ -488,7 +459,7 @@
         return "JAR " + m_file.getPath();
     }
 
-    public File getFile()
+    public synchronized File getFile()
     {
         return m_file;
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentLoaderImpl.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentLoaderImpl.java
index ff51d9f..ff12f9d 100644
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentLoaderImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentLoaderImpl.java
@@ -38,12 +38,13 @@
 
 public class ContentLoaderImpl implements IContentLoader
 {
-    private Logger m_logger = null;
-    private IContent m_content = null;
-    private IContent[] m_contentPath = null;
+    private final Logger m_logger;
+    private final IContent m_content;
+    private IContent[] m_contentPath;
+    private IContent[] m_fragments = null;
     private ISearchPolicy m_searchPolicy = null;
     private IURLPolicy m_urlPolicy = null;
-    private ContentClassLoader m_classLoader = null;
+    private ContentClassLoader m_classLoader;
     private ProtectionDomain m_protectionDomain = null;
     private static SecureAction m_secureAction = new SecureAction();
 
@@ -58,25 +59,7 @@
         return m_logger;
     }
 
-    public void open()
-    {
-        m_content.open();
-        try
-        {
-            initializeContentPath();
-        }
-        catch (Exception ex)
-        {
-            m_logger.log(Logger.LOG_ERROR, "Unable to initialize content path.", ex);
-        }
-
-        for (int i = 0; (m_contentPath != null) && (i < m_contentPath.length); i++)
-        {
-            m_contentPath[i].open();
-        }
-    }
-
-    public void close()
+    public synchronized void close()
     {
         m_content.close();
         for (int i = 0; (m_contentPath != null) && (i < m_contentPath.length); i++)
@@ -90,27 +73,57 @@
         return m_content;
     }
 
-    public IContent[] getClassPath()
+    public synchronized IContent[] getClassPath()
     {
+        if (m_contentPath == null)
+        {
+            try
+            {
+                m_contentPath = initializeContentPath();
+            }
+            catch (Exception ex)
+            {
+                m_logger.log(Logger.LOG_ERROR, "Unable to get module class path.", ex);
+            }
+        }
         return m_contentPath;
     }
 
-    public void setSearchPolicy(ISearchPolicy searchPolicy)
+    public synchronized void setFragmentContents(IContent[] contents) throws Exception
+    {
+        m_fragments = contents;
+// TODO: FRAGMENT - If this is not null, then we probably need to close
+//       the existing contents; this might happen when a resource is loaded
+//       from a bundle that could not be resolved.
+if (m_contentPath != null)
+{
+m_logger.log(Logger.LOG_DEBUG, "(FRAGMENT) CONTENT PATH SHOULD BE NULL = " + m_contentPath);
+}
+        m_contentPath = initializeContentPath();
+String msg = "(FRAGMENT) HOST CLASSPATH = ";
+for (int i = 0; i < m_contentPath.length; i++)
+{
+    msg += (m_contentPath[i].toString() + " ");
+}
+m_logger.log(Logger.LOG_DEBUG, msg);
+    }
+
+    public synchronized void setSearchPolicy(ISearchPolicy searchPolicy)
     {
         m_searchPolicy = searchPolicy;
     }
 
-    public ISearchPolicy getSearchPolicy()
+    public synchronized ISearchPolicy getSearchPolicy()
     {
         return m_searchPolicy;
     }
 
-    public void setURLPolicy(IURLPolicy urlPolicy)
+    public synchronized void setURLPolicy(IURLPolicy urlPolicy)
     {
         m_urlPolicy = urlPolicy;
     }
 
-    public IURLPolicy getURLPolicy()
+    public synchronized IURLPolicy getURLPolicy()
     {
         return m_urlPolicy;
     }
@@ -164,11 +177,12 @@
         }
 
         // Check the module class path.
+        IContent[] contentPath = getClassPath();
         for (int i = 0;
             (url == null) &&
-            (i < getClassPath().length); i++)
+            (i < contentPath.length); i++)
         {
-            if (getClassPath()[i].hasEntry(name))
+            if (contentPath[i].hasEntry(name))
             {
                 url = getURLPolicy().createURL(i + 1, name);
             }
@@ -200,9 +214,10 @@
             }
 
             // Check the module class path.
-            for (int i = 0; i < getClassPath().length; i++)
+            IContent[] contentPath = getClassPath();
+            for (int i = 0; i < contentPath.length; i++)
             {
-                if (getClassPath()[i].hasEntry(name))
+                if (contentPath[i].hasEntry(name))
                 {
                     // Use the class path index + 1 for creating the path so
                     // that we can differentiate between module content URLs
@@ -260,7 +275,7 @@
         {
             return m_content.hasEntry(urlPath);
         }
-        return m_contentPath[index - 1].hasEntry(urlPath);
+        return getClassPath()[index - 1].hasEntry(urlPath);
     }
 
     public InputStream getInputStream(int index, String urlPath)
@@ -274,15 +289,27 @@
         {
             return m_content.getEntryAsStream(urlPath);
         }
-        return m_contentPath[index - 1].getEntryAsStream(urlPath);
+        return getClassPath()[index - 1].getEntryAsStream(urlPath);
     }
 
-    public String toString()
+    public synchronized String toString()
     {
         return m_searchPolicy.toString();
     }
 
-    private void initializeContentPath() throws Exception
+    private IContent[] initializeContentPath() throws Exception
+    {
+        List contentList = new ArrayList();
+        calculateContentPath(m_content, contentList, true);
+        for (int i = 0; (m_fragments != null) && (i < m_fragments.length); i++)
+        {
+            calculateContentPath(m_fragments[i], contentList, false);
+        }
+        return (IContent[]) contentList.toArray(new IContent[contentList.size()]);
+    }
+
+    private List calculateContentPath(IContent content, List contentList, boolean searchFragments)
+        throws Exception
     {
         // Creating the content path entails examining the bundle's
         // class path to determine whether the bundle JAR file itself
@@ -294,7 +321,7 @@
         Map headers = null;
         try
         {
-            is = m_content.getEntryAsStream("META-INF/MANIFEST.MF");
+            is = content.getEntryAsStream("META-INF/MANIFEST.MF");
             headers = new StringMap(new Manifest(is).getMainAttributes(), false);
         }
         finally
@@ -305,7 +332,6 @@
         // Find class path meta-data.
         String classPath = (headers == null)
             ? null : (String) headers.get(FelixConstants.BUNDLE_CLASSPATH);
-
         // Parse the class path into strings.
         String[] classPathStrings = ManifestParser.parseDelimitedString(
             classPath, FelixConstants.CLASS_PATH_SEPARATOR);
@@ -316,7 +342,6 @@
         }
 
         // Create the bundles class path.
-        List contentList = new ArrayList();
         for (int i = 0; i < classPathStrings.length; i++)
         {
             // Remove any leading slash, since all bundle class path
@@ -328,16 +353,28 @@
             // Check for the bundle itself on the class path.
             if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
             {
-                contentList.add(m_content);
+                contentList.add(content);
             }
             else
             {
-                // Determine if the class path entry is a file or directory
-                // in the bundle JAR file.
-                IContent content = m_content.getEntryAsContent(classPathStrings[i]);
-                if (content != null)
+                // Try to find the embedded class path entry in the current
+                // content.
+                IContent embeddedContent = content.getEntryAsContent(classPathStrings[i]);
+                // If the embedded class path entry was not found, it might be
+                // in one of the fragments if the current content is the bundle,
+                // so try to search the fragments if necessary.
+                for (int fragIdx = 0;
+                    searchFragments && (embeddedContent == null)
+                        && (m_fragments != null) && (fragIdx < m_fragments.length);
+                    fragIdx++)
                 {
-                    contentList.add(content);
+                    embeddedContent = m_fragments[fragIdx].getEntryAsContent(classPathStrings[i]);
+                }
+                // If we found the embedded content, then add it to the
+                // class path content list.
+                if (embeddedContent != null)
+                {
+                    contentList.add(embeddedContent);
                 }
                 else
                 {
@@ -354,9 +391,9 @@
         // "." by default, as per the spec.
         if (contentList.size() == 0)
         {
-            contentList.add(m_content);
+            contentList.add(content);
         }
 
-        m_contentPath = (IContent[]) contentList.toArray(new IContent[contentList.size()]);
+        return contentList;
     }
 }
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
index 7aa7059..4070ff0 100755
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
@@ -21,7 +21,6 @@
 import java.io.IOException;
 import java.lang.reflect.Proxy;
 import java.net.URL;
-import java.security.ProtectionDomain;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Enumeration;
@@ -34,6 +33,7 @@
 import org.apache.felix.framework.BundleProtectionDomain;
 import org.apache.felix.framework.Logger;
 import org.apache.felix.framework.util.CompoundEnumeration;
+import org.apache.felix.framework.util.FelixConstants;
 import org.apache.felix.framework.util.SecurityManagerEx;
 import org.apache.felix.framework.util.Util;
 import org.apache.felix.framework.util.manifestparser.Capability;
@@ -42,6 +42,7 @@
 import org.apache.felix.framework.util.manifestparser.R4Library;
 import org.apache.felix.framework.util.manifestparser.Requirement;
 import org.apache.felix.moduleloader.ICapability;
+import org.apache.felix.moduleloader.IContent;
 import org.apache.felix.moduleloader.IModule;
 import org.apache.felix.moduleloader.IModuleFactory;
 import org.apache.felix.moduleloader.IRequirement;
@@ -991,6 +992,7 @@
         // resolved modules, which can then be used to fire resolved
         // events outside of the synchronized block.
         Map resolvedModuleWireMap = null;
+        Map fragmentMap = null;
 
         // Synchronize on the module manager, because we don't want
         // any modules being added or removed while we are in the
@@ -1003,6 +1005,47 @@
                 return;
             }
 
+            // The root module is either a host or a fragment. If it is a host,
+            // then we want to go ahead and resolve it. If it is a fragment, then
+            // we want to select a host and resolve the host instead.
+            IModule targetFragment = null;
+// TODO: FRAGMENT - Currently we just make a single selection of the available
+//       fragments or hosts and try to resolve. In case of failure, we do not
+//       backtrack. We will likely want to add backtracking.
+            if (isFragment(rootModule))
+            {
+                targetFragment = rootModule;
+                List hostList = getPotentialHosts(targetFragment);
+                rootModule = (IModule) hostList.get(0);
+String msg = "(FRAGMENT) POSSILE HOST(S): ";
+for (int i = 0; i < hostList.size(); i++)
+{
+    msg += (hostList.get(i).toString() + " ");
+}
+m_logger.log(Logger.LOG_DEBUG, msg);
+            }
+
+            // Get the available fragments for the host.
+            fragmentMap = getPotentialFragments(rootModule);
+
+            // If the resolve was for a specific fragment, then
+            // eliminate all other potential candidate fragments
+            // of the same symbolic name.
+            if (targetFragment != null)
+            {
+                fragmentMap.put(
+                    getBundleSymbolicName(targetFragment),
+                    new IModule[] { targetFragment });
+            }
+for (Iterator iter = fragmentMap.entrySet().iterator(); iter.hasNext(); )
+{
+    Map.Entry entry = (Map.Entry) iter.next();
+    String symName = (String) entry.getKey();
+    IModule[] fragments = (IModule[]) entry.getValue();
+    m_logger.log(Logger.LOG_DEBUG, "(FRAGMENT) WIRE: "
+        + rootModule + " -> " + symName + "[" + fragments.length + "] -> " + fragments[0]);
+}
+
             // This variable maps an unresolved module to a list of candidate
             // sets, where there is one candidate set for each requirement that
             // must be resolved. A candidate set contains the potential canidates
@@ -1045,6 +1088,28 @@
             // wires.
             resolvedModuleWireMap = createWires(candidatesMap, rootModule);
 
+            // Attach fragments to root module.
+            if ((fragmentMap != null) && (fragmentMap.size() > 0))
+            {
+                List list = new ArrayList();
+                for (Iterator iter = fragmentMap.entrySet().iterator(); iter.hasNext(); )
+                {
+                    Map.Entry entry = (Map.Entry) iter.next();
+                    IModule[] fragments = (IModule[]) entry.getValue();
+                    list.add(fragments[0].getContentLoader().getContent()
+                        .getEntryAsContent(FelixConstants.CLASS_PATH_DOT));
+                }
+                try
+                {
+                    ((ContentLoaderImpl) rootModule.getContentLoader())
+                        .setFragmentContents(
+                            (IContent[]) list.toArray(new IContent[list.size()]));
+                }
+                catch (Exception ex)
+                {
+                    m_logger.log(Logger.LOG_ERROR, "Unable to attach fragments", ex);
+                }
+            }
 //dumpUsedPackages();
         } // End of synchronized block on module manager.
 
@@ -1058,9 +1123,201 @@
             {
                 fireModuleResolved((IModule) ((Map.Entry) iter.next()).getKey());
             }
+            iter = fragmentMap.entrySet().iterator();
+            while (iter.hasNext())
+            {
+                fireModuleResolved(((IModule[]) ((Map.Entry) iter.next()).getValue())[0]);
+            }
         }
     }
 
+// TODO: FRAGMENT - Not very efficient.
+    private boolean isFragment(IModule module)
+    {
+        IRequirement[] reqs = module.getDefinition().getRequirements();
+        for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
+        {
+            if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+// TODO: FRAGMENT - Not very efficient.
+    private List getPotentialHosts(IModule fragment)
+        throws ResolveException
+    {
+        List hostList = new ArrayList();
+
+        IRequirement[] reqs = fragment.getDefinition().getRequirements();
+        IRequirement hostReq = null;
+        for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
+        {
+            if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
+            {
+                hostReq = reqs[reqIdx];
+                break;
+            }
+        }
+
+        IModule[] modules = m_factory.getModules();
+        for (int modIdx = 0; (hostReq != null) && (modIdx < modules.length); modIdx++)
+        {
+            if (!fragment.equals(modules[modIdx]) && !isResolved(modules[modIdx]))
+            {
+                ICapability[] caps = modules[modIdx].getDefinition().getCapabilities();
+                for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+                {
+                    if (caps[capIdx].getNamespace().equals(ICapability.HOST_NAMESPACE)
+                        && hostReq.isSatisfied(caps[capIdx]))
+                    {
+                        hostList.add(modules[modIdx]);
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (hostList.size() == 0)
+        {
+            throw new ResolveException("Unable to resolve.", fragment, hostReq);
+        }
+
+        return hostList;
+    }
+
+// TODO: FRAGMENT - Not very efficient.
+    private Map getPotentialFragments(IModule host)
+    {
+// TODO: FRAGMENT - This should check to make sure that the host allows fragments.
+        Map fragmentMap = new HashMap();
+
+        ICapability[] caps = host.getDefinition().getCapabilities();
+        ICapability bundleCap = null;
+        for (int capIdx = 0; capIdx < caps.length; capIdx++)
+        {
+            if (caps[capIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
+            {
+                bundleCap = caps[capIdx];
+                break;
+            }
+        }
+
+        IModule[] modules = m_factory.getModules();
+        for (int modIdx = 0; (bundleCap != null) && (modIdx < modules.length); modIdx++)
+        {
+            if (!host.equals(modules[modIdx]))
+            {
+                IRequirement[] reqs = modules[modIdx].getDefinition().getRequirements();
+                for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
+                {
+                    if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE)
+                        && reqs[reqIdx].isSatisfied(bundleCap))
+                    {
+                        indexFragment(fragmentMap, modules[modIdx]);
+                        break;
+                    }
+                }
+            }
+        }
+
+        return fragmentMap;
+    }
+
+// TODO: FRAGMENT - Not very efficient.
+    private static String getBundleSymbolicName(IModule module)
+    {
+        ICapability[] caps = module.getDefinition().getCapabilities();
+        for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+        {
+            if (caps[capIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
+            {
+                return (String)
+                    caps[capIdx].getProperties().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+            }
+        }
+        return null;
+    }
+
+// TODO: FRAGMENT - Not very efficient.
+    private static Version getBundleVersion(IModule module)
+    {
+        ICapability[] caps = module.getDefinition().getCapabilities();
+        for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+        {
+            if (caps[capIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
+            {
+                return (Version)
+                    caps[capIdx].getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+            }
+        }
+        return Version.emptyVersion;
+    }
+
+    private void indexFragment(Map map, IModule module)
+    {
+        String symName = getBundleSymbolicName(module);
+        IModule[] modules = (IModule[]) map.get(symName);
+
+        // 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 IModule[] { module };
+        }
+        else
+        {
+            Version version = getBundleVersion(module);
+            Version middleVersion = null;
+            int top = 0, bottom = modules.length - 1, middle = 0;
+            while (top <= bottom)
+            {
+                middle = (bottom - top) / 2 + top;
+                middleVersion = getBundleVersion(modules[middle]);
+                // 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 = Util.getBundleIdFromModuleId(modules[middle].getId());
+                    long exportId = Util.getBundleIdFromModuleId(module.getId());
+                    if (middleId < exportId)
+                    {
+                        top = middle + 1;
+                    }
+                    else
+                    {
+                        bottom = middle - 1;
+                    }
+                }
+                else
+                {
+                    top = middle + 1;
+                }
+            }
+
+            // Ignore duplicates.
+            if ((top >= modules.length) || (modules[top] != module))
+            {
+                IModule[] newMods = new IModule[modules.length + 1];
+                System.arraycopy(modules, 0, newMods, 0, top);
+                System.arraycopy(modules, top, newMods, top + 1, modules.length - top);
+                newMods[top] = module;
+                modules = newMods;
+            }
+        }
+
+        map.put(symName, modules);
+    }
+
     private void populateCandidatesMap(Map candidatesMap, IModule module)
         throws ResolveException
     {
@@ -2616,12 +2873,12 @@
             }
             else
             {
-                int top = 0, bottom = modules.length - 1, middle = 0;
+                Version version = (Version)
+                    capability.getProperties().get(ICapability.VERSION_PROPERTY);
                 Version middleVersion = null;
+                int top = 0, bottom = modules.length - 1, middle = 0;
                 while (top <= bottom)
                 {
-                    Version version = (Version)
-                        capability.getProperties().get(ICapability.VERSION_PROPERTY);
                     middle = (bottom - top) / 2 + top;
                     middleVersion = (Version)
                         getExportPackageCapability(
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index f9946e7..1ead3bd 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -102,6 +102,10 @@
                     "Cannot have multiple symbolic names: "
                         + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
             }
+            // Add a module capability.
+            // TODO: FRAGMENT - Fragment bundles cannot be required, so we
+            //       should not add this capability, but for now we are using
+            //       it to get the symbolic name.
             m_bundleSymbolicName = (String) clauses[0][CLAUSE_PATHS_INDEX][0];
             R4Attribute[] attrs = new R4Attribute[2];
             attrs[0] = new R4Attribute(
@@ -109,6 +113,34 @@
             attrs[1] = new R4Attribute(
                 Constants.BUNDLE_VERSION_ATTRIBUTE, m_bundleVersion, false);
             capList.add(new Capability(ICapability.MODULE_NAMESPACE, null, attrs));
+            // Add a host capability if the bundle is not a fragment. A host
+            // capability is the same as a module capability, but with a
+            // different capability namespace.
+            if (headerMap.get(Constants.FRAGMENT_HOST) == null)
+            {
+                capList.add(new Capability(ICapability.HOST_NAMESPACE, null, attrs));
+            }
+        }
+
+        //
+        // Parse Fragment-Host.
+        //
+        clauses = parseStandardHeader(
+            (String) headerMap.get(Constants.FRAGMENT_HOST));
+        if (clauses.length > 0)
+        {
+            try
+            {
+                reqList.add(
+                    new Requirement(
+                        ICapability.HOST_NAMESPACE,
+                        "(" + Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE
+                            + "=" + clauses[0][CLAUSE_PATHS_INDEX][0] + ")"));
+            }
+            catch (InvalidSyntaxException ex)
+            {
+                ex.printStackTrace();
+            }
         }
 
         //
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/ICapability.java b/framework/src/main/java/org/apache/felix/moduleloader/ICapability.java
index 935cd7b..328a459 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/ICapability.java
+++ b/framework/src/main/java/org/apache/felix/moduleloader/ICapability.java
@@ -23,6 +23,7 @@
 public interface ICapability
 {
     public static final String MODULE_NAMESPACE = "module";
+    public static final String HOST_NAMESPACE = "host";
     public static final String PACKAGE_NAMESPACE = "package";
 
     public static final String PACKAGE_PROPERTY = "package";
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/IContent.java b/framework/src/main/java/org/apache/felix/moduleloader/IContent.java
index 8af13eb..2b51c1e 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/IContent.java
+++ b/framework/src/main/java/org/apache/felix/moduleloader/IContent.java
@@ -24,17 +24,6 @@
 
 public interface IContent
 {
-
-    /**
-     * <p>
-     * This method must be called before using any other methods on this
-     * interface. If the content is already opened, then subsequent calls
-     * should have no effect. This method is intended to allow the content
-     * to initialize any necessary resources.
-     * </p>
-    **/
-    public void open();
-
     /**
      * <p>
      * This method must be called when the content is no longer needed so
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/IContentLoader.java b/framework/src/main/java/org/apache/felix/moduleloader/IContentLoader.java
index 4420a44..f416e3a 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/IContentLoader.java
+++ b/framework/src/main/java/org/apache/felix/moduleloader/IContentLoader.java
@@ -25,7 +25,6 @@
 
 public interface IContentLoader
 {
-    public void open();
     public void close();
 
     public IContent getContent();
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/ModuleFactoryImpl.java b/framework/src/main/java/org/apache/felix/moduleloader/ModuleFactoryImpl.java
index bd49aa5..649cc61 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/ModuleFactoryImpl.java
+++ b/framework/src/main/java/org/apache/felix/moduleloader/ModuleFactoryImpl.java
@@ -126,11 +126,6 @@
         synchronized (this)
         {
             ((ModuleImpl) module).setContentLoader(contentLoader);
-            // Open the module's content loader to initialize it.
-            // TODO: This is not really the best place for this, but at
-            // least it is centralized with the call to IContentLoader.close()
-            // when the module is removed above.
-            contentLoader.open();
         }
     }