felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rickh...@apache.org
Subject svn commit: r739113 [1/2] - in /felix/trunk/framework/src/main/java/org/apache/felix: framework/ framework/searchpolicy/ framework/util/ moduleloader/
Date Fri, 30 Jan 2009 01:20:06 GMT
Author: rickhall
Date: Fri Jan 30 01:20:06 2009
New Revision: 739113

URL: http://svn.apache.org/viewvc?rev=739113&view=rev
Log:
Made ModuleClassLoader an inner class of ModuleImpl, since they are so
intimately connected, which allowed us to hide/eliminate some cruft. Also
changed how the class loading infrastructure detects cycles, which allowed
us to eliminate much cruft and make class loading more consistent. (FELIX-851)

Removed:
    felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleClassLoader.java
Modified:
    felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/FindEntriesEnumeration.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
    felix/trunk/framework/src/main/java/org/apache/felix/moduleloader/IModule.java

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java?rev=739113&r1=739112&r2=739113&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java Fri Jan 30 01:20:06 2009
@@ -294,7 +294,7 @@
         Properties mergedProperties = new Properties();
         for (Iterator it = resourceList.iterator(); it.hasNext(); )
         {
-            URL temp = this.getCurrentModule().getResourceFromModule(it.next() + ".properties");
+            URL temp = this.getCurrentModule().getResourceByDelegation(it.next() + ".properties");
             if (temp == null)
             {
                 continue;
@@ -923,8 +923,6 @@
         }
         module.setSecurityContext(new BundleProtectionDomain(getFramework(), this));
 
-        ((ModuleImpl) module).setBundle(this);
-
         IModule[] dest = new IModule[m_modules.length + 1];
         System.arraycopy(m_modules, 0, dest, 0, m_modules.length);
         dest[m_modules.length] = module;
@@ -947,6 +945,7 @@
             getFramework().getLogger(),
             getFramework().getConfig(),
             getFramework().getResolver(),
+            this,
             Long.toString(getBundleId()) + "." + Integer.toString(revision),
             headerMap,
             m_archive.getRevision(revision).getContent());

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java?rev=739113&r1=739112&r2=739113&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java Fri Jan 30 01:20:06 2009
@@ -139,9 +139,9 @@
      * @param config the configuration to read properties from.
      * @param systemBundleInfo the info to change if we need to add exports.
      */
-    ExtensionManager(Logger logger, Map configMap) throws BundleException
+    ExtensionManager(Logger logger, Felix felix) throws BundleException
     {
-        m_module = new ExtensionManagerModule();
+        m_module = new ExtensionManagerModule(felix);
         m_extensions = null;
         m_names = null;
         m_sourceToExtensions = null;
@@ -150,7 +150,7 @@
 // TODO: FRAMEWORK - Not all of this stuff really belongs here, probably only exports.
         // Populate system bundle header map.
         m_headerMap.put(FelixConstants.BUNDLE_VERSION,
-            configMap.get(FelixConstants.FELIX_VERSION_PROPERTY));
+            felix.getConfig().get(FelixConstants.FELIX_VERSION_PROPERTY));
         m_headerMap.put(FelixConstants.BUNDLE_SYMBOLICNAME,
             FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME);
         m_headerMap.put(FelixConstants.BUNDLE_NAME, "System Bundle");
@@ -166,11 +166,11 @@
         // We must construct the system bundle's export metadata.
         // Get configuration property that specifies which class path
         // packages should be exported by the system bundle.
-        String syspkgs = (String) configMap.get(Constants.FRAMEWORK_SYSTEMPACKAGES);
+        String syspkgs = (String) felix.getConfig().get(Constants.FRAMEWORK_SYSTEMPACKAGES);
         // If no system packages were specified, load our default value.
         syspkgs = (syspkgs == null) ? loadDefaultSystemPackages(m_logger) : syspkgs;
         // If any extra packages are specified, then append them.
-        String extra = (String) configMap.get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
+        String extra = (String) felix.getConfig().get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
         syspkgs = (extra == null) ? syspkgs : syspkgs + "," + extra;
         try
         {
@@ -453,7 +453,7 @@
 
         for (Iterator iter = m_extensions.iterator(); iter.hasNext();)
         {
-            URL result = ((BundleImpl) iter.next()).getCurrentModule().getResourceFromContent(path);
+            URL result = ((BundleImpl) iter.next()).getCurrentModule().getEntry(path);
 
             if (result != null)
             {
@@ -613,9 +613,9 @@
 
     class ExtensionManagerModule extends ModuleImpl
     {
-        ExtensionManagerModule() throws BundleException
+        ExtensionManagerModule(Felix felix) throws BundleException
         {
-            super(m_logger, null, null, "0", null, null);
+            super(m_logger, null, null, felix, "0", null, null);
         }
 
         public Map getHeaders()
@@ -635,20 +635,40 @@
 
         public Class getClassByDelegation(String name) throws ClassNotFoundException
         {
-            // System bundle does not delegate to other modules.
-            return getClassFromModule(name);
+            if (!m_exportNames.contains(Util.getClassPackage(name)))
+            {
+                return null;
+            }
+
+            try
+            {
+                return getClass().getClassLoader().loadClass(name);
+            }
+            catch (ClassNotFoundException ex)
+            {
+                m_logger.log(
+                    Logger.LOG_WARNING,
+                    ex.getMessage(),
+                    ex);
+            }
+            return null;
         }
 
         public URL getResourceByDelegation(String name)
         {
-            // System bundle does not delegate to other modules.
-            return getResourceFromModule(name);
+            return getClass().getClassLoader().getResource(name);
         }
 
         public Enumeration getResourcesByDelegation(String name)
         {
-            // System bundle does not delegate to other modules.
-            return getResourcesFromModule(name);
+           try
+           {
+               return getClass().getClassLoader().getResources(name);
+           }
+           catch (IOException ex)
+           {
+               return null;
+           }
         }
 
         public Logger getLogger()
@@ -666,12 +686,7 @@
             return null;
         }
 
-        public synchronized IContent[] getClassPath()
-        {
-            throw new UnsupportedOperationException("Should not be used!");
-        }
-
-        public synchronized void attachFragmentContents(IContent[] fragmentContents)
+        public void attachFragmentContents(IContent[] fragmentContents)
             throws Exception
         {
             throw new UnsupportedOperationException("Should not be used!");
@@ -697,60 +712,7 @@
             return m_urlPolicy;
         }
 
-        public Class findClassByDelegation(String name) throws ClassNotFoundException
-        {
-            return getClassFromModule(name);
-        }
-
-        public URL findResourceByDelegation(String name) throws ResourceNotFoundException
-        {
-            return getResourceFromModule(name);
-        }
-
-        public Enumeration findResourcesByDelegation(String name) throws ResourceNotFoundException
-        {
-            return getResourcesFromModule(name);
-        }
-
-        public Class getClassFromModule(String name)
-        {
-            if (!m_exportNames.contains(Util.getClassPackage(name)))
-            {
-                return null;
-            }
-
-            try
-            {
-                return getClass().getClassLoader().loadClass(name);
-            }
-            catch (ClassNotFoundException ex)
-            {
-                m_logger.log(
-                    Logger.LOG_WARNING,
-                    ex.getMessage(),
-                    ex);
-            }
-            return null;
-        }
-
-        public URL getResourceFromModule(String name)
-        {
-            return getClass().getClassLoader().getResource(name);
-        }
-
-        public Enumeration getResourcesFromModule(String name)
-        {
-           try
-           {
-               return getClass().getClassLoader().getResources(name);
-           }
-           catch (IOException ex)
-           {
-               return null;
-           }
-        }
-
-        public URL getResourceFromContent(String name)
+        public URL getEntry(String name)
         {
             // There is no content for the system bundle, so return null.
             return null;

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java?rev=739113&r1=739112&r2=739113&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java Fri Jan 30 01:20:06 2009
@@ -27,7 +27,7 @@
 import org.apache.felix.framework.cache.*;
 import org.apache.felix.framework.ext.SecurityProvider;
 import org.apache.felix.framework.searchpolicy.*;
-import org.apache.felix.framework.searchpolicy.PackageSource;
+import org.apache.felix.framework.searchpolicy.ModuleImpl.ModuleClassLoader;
 import org.apache.felix.framework.util.*;
 import org.apache.felix.framework.util.manifestparser.*;
 import org.apache.felix.moduleloader.*;
@@ -295,7 +295,7 @@
 
         // Create the extension manager, which we will use as the module
         // definition for creating the system bundle module.
-        m_extensionManager = new ExtensionManager(m_logger, m_configMap);
+        m_extensionManager = new ExtensionManager(m_logger, this);
         addModule(m_extensionManager.getModule());
     }
 
@@ -1168,7 +1168,7 @@
         {
             throw new IllegalStateException("The bundle is uninstalled.");
         }
-        return bundle.getCurrentModule().getResourceFromContent(name);
+        return bundle.getCurrentModule().getEntry(name);
     }
 
     /**

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/FindEntriesEnumeration.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/FindEntriesEnumeration.java?rev=739113&r1=739112&r2=739113&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/FindEntriesEnumeration.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/FindEntriesEnumeration.java Fri Jan 30 01:20:06 2009
@@ -113,8 +113,7 @@
                     if (checkSubstring(m_filePattern, lastElement))
                     {
                         // Convert entry name into an entry URL.
-                        return m_bundle.getCurrentModule()
-                            .getResourceFromContent(entryName);
+                        return m_bundle.getCurrentModule().getEntry(entryName);
                     }
                 }
             }

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlers.java?rev=739113&r1=739112&r2=739113&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlers.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlers.java Fri Jan 30 01:20:06 2009
@@ -18,7 +18,6 @@
  */
 package org.apache.felix.framework;
 
-import java.io.IOException;
 import java.net.ContentHandler;
 import java.net.ContentHandlerFactory;
 import java.net.MalformedURLException;
@@ -33,7 +32,7 @@
 import java.util.Map;
 import java.util.StringTokenizer;
 
-import org.apache.felix.framework.searchpolicy.ModuleClassLoader;
+import org.apache.felix.framework.searchpolicy.ModuleImpl.ModuleClassLoader;
 import org.apache.felix.framework.util.FelixConstants;
 import org.apache.felix.framework.util.SecureAction;
 import org.apache.felix.framework.util.SecurityManagerEx;

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java?rev=739113&r1=739112&r2=739113&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java Fri Jan 30 01:20:06 2009
@@ -21,18 +21,25 @@
 import org.apache.felix.moduleloader.*;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.net.URL;
 import java.security.ProtectionDomain;
+import java.security.SecureClassLoader;
 import java.util.ArrayList;
 import java.util.Enumeration;
 
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.Vector;
 import org.apache.felix.framework.Felix.FelixResolver;
 import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.cache.JarContent;
 import org.apache.felix.framework.util.CompoundEnumeration;
 import org.apache.felix.framework.util.FelixConstants;
 import org.apache.felix.framework.util.SecureAction;
@@ -40,9 +47,11 @@
 import org.apache.felix.framework.util.Util;
 import org.apache.felix.framework.util.manifestparser.ManifestParser;
 import org.apache.felix.framework.util.manifestparser.R4Library;
+import org.apache.felix.framework.util.manifestparser.Requirement;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.Version;
 
 public class ModuleImpl implements IModule
@@ -62,8 +71,7 @@
     private final IRequirement[] m_requirements;
     private final IRequirement[] m_dynamicRequirements;
     private final R4Library[] m_nativeLibraries;
-
-    private Bundle m_bundle = null;
+    private final Bundle m_bundle;
 
     private IModule[] m_fragments = null;
     private IWire[] m_wires = null;
@@ -86,12 +94,18 @@
     // Re-usable security manager for accessing class context.
     private static SecurityManagerEx m_sm = new SecurityManagerEx();
 
-    public ModuleImpl(Logger logger, Map configMap, FelixResolver resolver,
-        String id, Map headerMap, IContent content) throws BundleException
+    // Thread local to detect class loading cycles.
+    private final ThreadLocal m_cycleCheck = new ThreadLocal();
+
+    public ModuleImpl(
+        Logger logger, Map configMap, FelixResolver resolver,
+        Bundle bundle, String id, Map headerMap, IContent content)
+        throws BundleException
     {
         m_logger = logger;
         m_configMap = configMap;
         m_resolver = resolver;
+        m_bundle = bundle;
         m_id = id;
         m_headerMap = headerMap;
         m_content = content;
@@ -190,32 +204,18 @@
         }
    }
 
-    Logger getLogger()
-    {
-        return m_logger;
-    }
-
-    FelixResolver getResolver()
-    {
-        return m_resolver;
-    }
-
-    public synchronized Bundle getBundle()
-    {
-        return m_bundle;
-    }
+    //
+    // Metadata access methods.
+    //
 
-    public synchronized void setBundle(Bundle bundle)
+    public Map getHeaders()
     {
-        if (m_bundle == null)
-        {
-            m_bundle = bundle;
-        }
+        return m_headerMap;
     }
 
-    public String getId()
+    public String getSymbolicName()
     {
-        return m_id;
+        return m_symbolicName;
     }
 
     public String getManifestVersion()
@@ -228,16 +228,6 @@
         return m_version;
     }
 
-    public String getSymbolicName()
-    {
-        return m_symbolicName;
-    }
-
-    public Map getHeaders()
-    {
-        return m_headerMap;
-    }
-
     public ICapability[] getCapabilities()
     {
         return m_capabilities;
@@ -258,63 +248,18 @@
         return m_nativeLibraries;
     }
 
-    public synchronized IModule[] getFragments()
-    {
-        return m_fragments;
-    }
+    //
+    // Run-time data access.
+    //
 
-    public synchronized void attachFragments(IModule[] fragments) throws Exception
+    public Bundle getBundle()
     {
-        // Remove module from old fragment dependencies.
-        // We will generally only remove module fragment
-        // dependencies when we are uninstalling the module.
-        for (int i = 0; (m_fragments != null) && (i < m_fragments.length); i++)
-        {
-            ((ModuleImpl) m_fragments[i]).removeDependentHost(this);
-        }
-
-        // 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.
-        if (m_fragments != null)
-        {
-            IContent[] fragmentContents = new IContent[m_fragments.length];
-            for (int i = 0; (m_fragments != null) && (i < m_fragments.length); i++)
-            {
-                ((ModuleImpl) m_fragments[i]).addDependentHost(this);
-                fragmentContents[i] =
-                    m_fragments[i].getContent()
-                        .getEntryAsContent(FelixConstants.CLASS_PATH_DOT);
-            }
-            // Now attach the fragment contents to our content loader.
-            attachFragmentContents(fragmentContents);
-        }
+        return m_bundle;
     }
 
-    private void attachFragmentContents(IContent[] fragmentContents)
-        throws Exception
+    public String getId()
     {
-        // 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();
+        return m_id;
     }
 
     public synchronized IWire[] getWires()
@@ -355,116 +300,397 @@
         }
     }
 
-    public synchronized IModule[] getDependentHosts()
+    public boolean isResolved()
     {
-        return m_dependentHosts;
+        return m_isResolved;
     }
 
-    public synchronized void addDependentHost(IModule module)
+    public void setResolved()
     {
-        m_dependentHosts = addDependent(m_dependentHosts, module);
+        m_isResolved = true;
     }
 
-    public synchronized void removeDependentHost(IModule module)
-    {
-        m_dependentHosts = removeDependent(m_dependentHosts, module);
-    }
+    //
+    // Content access methods.
+    //
 
-    public synchronized IModule[] getDependentImporters()
+    public IContent getContent()
     {
-        return m_dependentImporters;
+        return m_content;
     }
 
-    public synchronized void addDependentImporter(IModule module)
+    private synchronized IContent[] getContentPath()
     {
-        m_dependentImporters = addDependent(m_dependentImporters, module);
+        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 synchronized void removeDependentImporter(IModule module)
+    private IContent[] initializeContentPath() throws Exception
     {
-        m_dependentImporters = removeDependent(m_dependentImporters, module);
+        List contentList = new ArrayList();
+        calculateContentPath(m_content, contentList, true);
+        for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
+        {
+            calculateContentPath(m_fragmentContents[i], contentList, false);
+        }
+        return (IContent[]) contentList.toArray(new IContent[contentList.size()]);
     }
 
-    public synchronized IModule[] getDependentRequirers()
+    private List calculateContentPath(IContent content, List contentList, boolean searchFragments)
+        throws Exception
     {
-        return m_dependentRequirers;
-    }
+        // Creating the content path entails examining the bundle's
+        // class path to determine whether the bundle JAR file itself
+        // is on the bundle's class path and then creating content
+        // objects for everything on the class path.
 
-    public synchronized void addDependentRequirer(IModule module)
-    {
-        m_dependentRequirers = addDependent(m_dependentRequirers, module);
-    }
+        // Create a list to contain the content path for the specified content.
+        List localContentList = new ArrayList();
 
-    public synchronized void removeDependentRequirer(IModule module)
-    {
-        m_dependentRequirers = removeDependent(m_dependentRequirers, module);
-    }
+        // Find class path meta-data.
+        String classPath = (String) m_headerMap.get(FelixConstants.BUNDLE_CLASSPATH);
+        // Parse the class path into strings.
+        String[] classPathStrings = ManifestParser.parseDelimitedString(
+            classPath, FelixConstants.CLASS_PATH_SEPARATOR);
 
-    public synchronized IModule[] getDependents()
-    {
-        IModule[] dependents = new IModule[
-            m_dependentHosts.length + m_dependentImporters.length + m_dependentRequirers.length];
-        System.arraycopy(
-            m_dependentHosts,
-            0,
-            dependents,
-            0,
-            m_dependentHosts.length);
-        System.arraycopy(
-            m_dependentImporters,
-            0,
-            dependents,
-            m_dependentHosts.length,
-            m_dependentImporters.length);
-        System.arraycopy(
-            m_dependentRequirers,
-            0,
-            dependents,
-            m_dependentHosts.length + m_dependentImporters.length,
-            m_dependentRequirers.length);
-        return dependents;
+        if (classPathStrings == null)
+        {
+            classPathStrings = new String[0];
+        }
+
+        // Create the bundles class path.
+        for (int i = 0; i < classPathStrings.length; i++)
+        {
+            // Remove any leading slash, since all bundle class path
+            // entries are relative to the root of the bundle.
+            classPathStrings[i] = (classPathStrings[i].startsWith("/"))
+                ? classPathStrings[i].substring(1)
+                : classPathStrings[i];
+
+            // Check for the bundle itself on the class path.
+            if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
+            {
+                localContentList.add(content);
+            }
+            else
+            {
+                // 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_fragmentContents != null) && (fragIdx < m_fragmentContents.length);
+                    fragIdx++)
+                {
+                    embeddedContent = m_fragmentContents[fragIdx].getEntryAsContent(classPathStrings[i]);
+                }
+                // If we found the embedded content, then add it to the
+                // class path content list.
+                if (embeddedContent != null)
+                {
+                    localContentList.add(embeddedContent);
+                }
+                else
+                {
+// TODO: FRAMEWORK - Per the spec, this should fire a FrameworkEvent.INFO event;
+//       need to create an "Eventer" class like "Logger" perhaps.
+                    m_logger.log(Logger.LOG_INFO,
+                        "Class path entry not found: "
+                        + classPathStrings[i]);
+                }
+            }
+        }
+
+        // If there is nothing on the class path, then include
+        // "." by default, as per the spec.
+        if (localContentList.size() == 0)
+        {
+            localContentList.add(content);
+        }
+
+        // Now add the local contents to the global content list and return it.
+        contentList.addAll(localContentList);
+        return contentList;
     }
 
     public Class getClassByDelegation(String name) throws ClassNotFoundException
     {
-        try
+        Set pkgCycleSet = (Set) m_cycleCheck.get();
+        if (pkgCycleSet == null)
         {
-            return getClassLoader().loadClass(name);
+            pkgCycleSet = new HashSet();
+            m_cycleCheck.set(pkgCycleSet);
         }
-        catch (ClassNotFoundException ex)
+        if (!pkgCycleSet.contains(name))
         {
+            pkgCycleSet.add(name);
+            try
+            {
+                return getClassLoader().loadClass(name);
+            }
+            catch (ClassNotFoundException ex)
+            {
 // TODO: REFACTOR - Should this log?
-            m_logger.log(
-                Logger.LOG_WARNING,
-                ex.getMessage(),
-                ex);
-            throw ex;
+                m_logger.log(
+                    Logger.LOG_WARNING,
+                    ex.getMessage(),
+                    ex);
+                throw ex;
+            }
+            finally
+            {
+                pkgCycleSet.remove(name);
+            }
         }
+        return null;
     }
 
     public URL getResourceByDelegation(String name)
     {
-        return getClassLoader().getResource(name);
-    }
-
-    public Enumeration getResourcesByDelegation(String name)
-    {
-        Enumeration urls = null;
-        List enums = new ArrayList();
-
-        // First, try to resolve the originating module.
-// TODO: FRAMEWORK - Consider opimizing this call to resolve, since it is called
-// for each class load.
-        try
+        Set pkgCycleSet = (Set) m_cycleCheck.get();
+        if (pkgCycleSet == null)
         {
-            m_resolver.resolve(this);
+            pkgCycleSet = new HashSet();
+            m_cycleCheck.set(pkgCycleSet);
         }
-        catch (ResolveException ex)
+        if (!pkgCycleSet.contains(name))
         {
-            // The spec states that if the bundle cannot be resolved, then
-            // only the local bundle's resources should be searched. So we
+            pkgCycleSet.add(name);
+            try
+            {
+                return (URL) findClassOrResourceByDelegation(name, false);
+            }
+            catch (ClassNotFoundException ex)
+            {
+            }
+            catch (ResourceNotFoundException ex)
+            {
+// TODO: REFACTOR - Should this log?
+            }
+            finally
+            {
+                pkgCycleSet.remove(name);
+            }
+        }
+
+        return null;
+    }
+
+    private Object findClassOrResourceByDelegation(String name, boolean isClass)
+        throws ClassNotFoundException, ResourceNotFoundException
+    {
+        // First, try to resolve the originating module.
+// TODO: FRAMEWORK - Consider opimizing this call to resolve, since it is called
+// for each class load.
+        try
+        {
+            m_resolver.resolve(this);
+        }
+        catch (ResolveException ex)
+        {
+            if (isClass)
+            {
+                // We do not use the resolve exception as the
+                // cause of the exception, since this would
+                // potentially leak internal module information.
+                throw new ClassNotFoundException(
+                    name + ": cannot resolve package "
+                    + ex.getRequirement());
+            }
+            else
+            {
+                // The spec states that if the bundle cannot be resolved, then
+                // only the local bundle's resources should be searched. So we
+                // will ask the module's own class path.
+                URL url = getResourceLocal(name);
+                if (url != null)
+                {
+                    return url;
+                }
+
+                // We need to throw a resource not found exception.
+                throw new ResourceNotFoundException(
+                    name + ": cannot resolve package "
+                    + ex.getRequirement());
+            }
+        }
+
+        // Get the package of the target class/resource.
+        String pkgName = (isClass)
+            ? Util.getClassPackage(name)
+            : Util.getResourcePackage(name);
+
+        // Delegate any packages listed in the boot delegation
+        // property to the parent class loader.
+        Object result = null;
+        for (int i = 0; i < m_bootPkgs.length; i++)
+        {
+            // A wildcarded boot delegation package will be in the form of "foo.",
+            // so if the package is wildcarded do a startsWith() or a regionMatches()
+            // to ignore the trailing "." to determine if the request should be
+            // delegated to the parent class loader. If the package is not wildcarded,
+            // then simply do an equals() test to see if the request should be
+            // delegated to the parent class loader.
+            if (pkgName.length() > 0)
+            {
+                // Only consider delegation if we have a package name, since
+                // we don't want to promote the default package. The spec does
+                // not take a stand on this issue.
+                if ((m_bootPkgWildcards[i] &&
+                    (pkgName.startsWith(m_bootPkgs[i]) ||
+                    m_bootPkgs[i].regionMatches(0, pkgName, 0, pkgName.length())))
+                    || (!m_bootPkgWildcards[i] && m_bootPkgs[i].equals(pkgName)))
+                {
+                    try
+                    {
+                        result = (isClass)
+                            ? (Object) getClass().getClassLoader().loadClass(name)
+                            : (Object) getClass().getClassLoader().getResource(name);
+                        // If this is a java.* package, then always terminate the
+                        // search; otherwise, continue to look locally if not found.
+                        if (m_bootPkgs[i].startsWith("java.") || (result != null))
+                        {
+                            return result;
+                        }
+                    }
+                    catch (ClassNotFoundException ex)
+                    {
+                        // If this is a java.* package, then always terminate the
+                        // search; otherwise, continue to look locally if not found.
+                        if (m_bootPkgs[i].startsWith("java."))
+                        {
+                            throw ex;
+                        }
+                        else
+                        {
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        // Look in the module's imports. Note that the search may
+        // be aborted if this method throws an exception, otherwise
+        // it continues if a null is returned.
+        result = searchImports(name, isClass);
+
+        // If not found, try the module's own class path.
+        if (result == null)
+        {
+            result = (isClass)
+                ? (Object) getClassLoader().findClass(name)
+                : (Object) getResourceLocal(name);
+
+            // If still not found, then try the module's dynamic imports.
+            if (result == null)
+            {
+                result = searchDynamicImports(name, pkgName, isClass);
+            }
+        }
+
+        if (result == null)
+        {
+            if (isClass)
+            {
+                throw new ClassNotFoundException(name);
+            }
+            else
+            {
+                throw new ResourceNotFoundException(name);
+            }
+        }
+
+        return result;
+    }
+
+    private URL getResourceLocal(String name)
+    {
+        URL url = null;
+
+        // Remove leading slash, if present, but special case
+        // "/" so that it returns a root URL...this isn't very
+        // clean or meaninful, but the Spring guys want it.
+        if (name.equals("/"))
+        {
+            // Just pick a class path index since it doesn't really matter.
+            url = getURLPolicy().createURL(1, name);
+        }
+        else if (name.startsWith("/"))
+        {
+            name = name.substring(1);
+        }
+
+        // Check the module class path.
+        IContent[] contentPath = getContentPath();
+        for (int i = 0;
+            (url == null) &&
+            (i < contentPath.length); i++)
+        {
+            if (contentPath[i].hasEntry(name))
+            {
+                url = getURLPolicy().createURL(i + 1, name);
+            }
+        }
+
+        return url;
+    }
+
+    public Enumeration getResourcesByDelegation(String name)
+    {
+        Set pkgCycleSet = (Set) m_cycleCheck.get();
+        if (pkgCycleSet == null)
+        {
+            pkgCycleSet = new HashSet();
+            m_cycleCheck.set(pkgCycleSet);
+        }
+        if (!pkgCycleSet.contains(name))
+        {
+            pkgCycleSet.add(name);
+            try
+            {
+                return findResourcesByDelegation(name);
+            }
+            finally
+            {
+                pkgCycleSet.remove(name);
+            }
+        }
+
+        return null;
+    }
+
+    private Enumeration findResourcesByDelegation(String name)
+    {
+        Enumeration urls = null;
+        List completeUrlList = new ArrayList();
+
+        // First, try to resolve the originating module.
+// TODO: FRAMEWORK - Consider opimizing this call to resolve, since it is called
+// for each class load.
+        try
+        {
+            m_resolver.resolve(this);
+        }
+        catch (ResolveException ex)
+        {
+            // The spec states that if the bundle cannot be resolved, then
+            // only the local bundle's resources should be searched. So we
             // will ask the module's own class path.
-            urls = getResourcesFromModule(name);
+            urls = getResourcesLocal(name);
             return urls;
         }
 
@@ -508,7 +734,7 @@
                         return urls;
                     }
 
-                    enums.add(urls);
+                    completeUrlList.add(urls);
                     break;
                 }
             }
@@ -538,9 +764,9 @@
                 }
                 if (urls != null)
                 {
-                    enums.add(urls);
+                    completeUrlList.add(urls);
                     return new CompoundEnumeration((Enumeration[])
-                        enums.toArray(new Enumeration[enums.size()]));
+                        completeUrlList.toArray(new Enumeration[completeUrlList.size()]));
                 }
             }
         }
@@ -563,7 +789,7 @@
                 }
                 if (urls != null)
                 {
-                    enums.add(urls);
+                    completeUrlList.add(urls);
                 }
             }
         }
@@ -571,10 +797,10 @@
         // Try the module's own class path. If we can find the resource then
         // return it together with the results from the other searches else
         // try to look into the dynamic imports.
-        urls = getResourcesFromModule(name);
+        urls = getResourcesLocal(name);
         if (urls != null)
         {
-            enums.add(urls);
+            completeUrlList.add(urls);
         }
         else
         {
@@ -603,207 +829,42 @@
                 }
                 if (urls != null)
                 {
-                    enums.add(urls);
+                    completeUrlList.add(urls);
                 }
             }
         }
 
         return new CompoundEnumeration((Enumeration[])
-            enums.toArray(new Enumeration[enums.size()]));
-    }
-
-    public boolean isResolved()
-    {
-        return m_isResolved;
-    }
-
-    public void setResolved()
-    {
-        m_isResolved = true;
+            completeUrlList.toArray(new Enumeration[completeUrlList.size()]));
     }
 
-    public String toString()
+    private Enumeration getResourcesLocal(String name)
     {
-        return m_id;
-    }
+        Vector v = new Vector();
 
-    private static IModule[] addDependent(IModule[] modules, IModule module)
-    {
-        // Make sure the dependent module is not already present.
-        for (int i = 0; i < modules.length; i++)
+        // Special case "/" so that it returns a root URLs for
+        // each bundle class path entry...this isn't very
+        // clean or meaningful, but the Spring guys want it.
+        final IContent[] contentPath = getContentPath();
+        if (name.equals("/"))
         {
-            if (modules[i].equals(module))
+            for (int i = 0; i < contentPath.length; i++)
             {
-                return modules;
+                v.addElement(getURLPolicy().createURL(i + 1, name));
             }
         }
-        IModule[] tmp = new IModule[modules.length + 1];
-        System.arraycopy(modules, 0, tmp, 0, modules.length);
-        tmp[modules.length] = module;
-        return tmp;
-    }
-
-    private static IModule[] removeDependent(IModule[] modules, IModule module)
-    {
-        IModule[] tmp = modules;
-
-        // Make sure the dependent module is present.
-        for (int i = 0; i < modules.length; i++)
+        else
         {
-            if (modules[i].equals(module))
+            // Remove leading slash, if present.
+            if (name.startsWith("/"))
             {
-                // If this is the module, then point to empty list.
-                if ((modules.length - 1) == 0)
-                {
-                    tmp = new IModule[0];
-                }
-                // Otherwise, we need to do some array copying.
-                else
-                {
-                    tmp = new IModule[modules.length - 1];
-                    System.arraycopy(modules, 0, tmp, 0, i);
-                    if (i < tmp.length)
-                    {
-                        System.arraycopy(modules, i + 1, tmp, i, tmp.length - i);
-                    }
-                }
-                break;
-            }
-        }
-
-        return tmp;
-    }
-
-    public synchronized void close()
-    {
-        m_content.close();
-        for (int i = 0; (m_contentPath != null) && (i < m_contentPath.length); i++)
-        {
-            m_contentPath[i].close();
-        }
-        for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
-        {
-            m_fragmentContents[i].close();
-        }
-        m_classLoader = null;
-    }
-
-    public IContent getContent()
-    {
-        return m_content;
-    }
-
-    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 synchronized void setURLPolicy(IURLPolicy urlPolicy)
-    {
-        m_urlPolicy = urlPolicy;
-    }
-
-    public synchronized IURLPolicy getURLPolicy()
-    {
-        return m_urlPolicy;
-    }
-
-    public synchronized void setSecurityContext(Object securityContext)
-    {
-        m_protectionDomain = (ProtectionDomain) securityContext;
-    }
-
-    public synchronized Object getSecurityContext()
-    {
-        return m_protectionDomain;
-    }
-
-    public Class getClassFromModule(String name) throws ClassNotFoundException
-    {
-        try
-        {
-            return getClassLoader().findClass(name);
-        }
-        catch (ClassNotFoundException ex)
-        {
-            m_logger.log(
-                Logger.LOG_WARNING,
-                ex.getMessage(),
-                ex);
-            throw ex;
-        }
-    }
-
-    public URL getResourceFromModule(String name)
-    {
-        URL url = null;
-
-        // Remove leading slash, if present, but special case
-        // "/" so that it returns a root URL...this isn't very
-        // clean or meaninful, but the Spring guys want it.
-        if (name.equals("/"))
-        {
-            // Just pick a class path index since it doesn't really matter.
-            url = getURLPolicy().createURL(1, name);
-        }
-        else if (name.startsWith("/"))
-        {
-            name = name.substring(1);
-        }
-
-        // Check the module class path.
-        IContent[] contentPath = getClassPath();
-        for (int i = 0;
-            (url == null) &&
-            (i < contentPath.length); i++)
-        {
-            if (contentPath[i].hasEntry(name))
-            {
-                url = getURLPolicy().createURL(i + 1, name);
-            }
-        }
-
-        return url;
-    }
-
-    public Enumeration getResourcesFromModule(String name)
-    {
-        Vector v = new Vector();
-
-        // Special case "/" so that it returns a root URLs for
-        // each bundle class path entry...this isn't very
-        // clean or meaningful, but the Spring guys want it.
-        if (name.equals("/"))
-        {
-            for (int i = 0; i < getClassPath().length; i++)
-            {
-                v.addElement(getURLPolicy().createURL(i + 1, name));
-            }
-        }
-        else
-        {
-            // Remove leading slash, if present.
-            if (name.startsWith("/"))
-            {
-                name = name.substring(1);
-            }
-
-            // Check the module class path.
-            IContent[] contentPath = getClassPath();
-            for (int i = 0; i < contentPath.length; i++)
-            {
-                if (contentPath[i].hasEntry(name))
+                name = name.substring(1);
+            }
+
+            // Check the module class path.
+            for (int i = 0; i < contentPath.length; i++)
+            {
+                if (contentPath[i].hasEntry(name))
                 {
                     // Use the class path index + 1 for creating the path so
                     // that we can differentiate between module content URLs
@@ -819,7 +880,7 @@
 
     // TODO: API: Investigate how to handle this better, perhaps we need
     // multiple URL policies, one for content -- one for class path.
-    public URL getResourceFromContent(String name)
+    public URL getEntry(String name)
     {
         URL url = null;
 
@@ -861,7 +922,7 @@
         {
             return m_content.hasEntry(urlPath);
         }
-        return getClassPath()[index - 1].hasEntry(urlPath);
+        return getContentPath()[index - 1].hasEntry(urlPath);
     }
 
     public InputStream getInputStream(int index, String urlPath)
@@ -875,238 +936,238 @@
         {
             return m_content.getEntryAsStream(urlPath);
         }
-        return getClassPath()[index - 1].getEntryAsStream(urlPath);
+        return getContentPath()[index - 1].getEntryAsStream(urlPath);
     }
 
-    private synchronized ModuleClassLoader getClassLoader()
+    //
+    // Fragment and dependency management methods.
+    //
+
+    public synchronized IModule[] getFragments()
     {
-        if (m_classLoader == null)
-        {
-            m_classLoader = m_secureAction.createModuleClassLoader(
-                this, m_protectionDomain);
-        }
-        return m_classLoader;
+        return m_fragments;
     }
 
-    private IContent[] initializeContentPath() throws Exception
+    public synchronized void attachFragments(IModule[] fragments) throws Exception
     {
-        List contentList = new ArrayList();
-        calculateContentPath(m_content, contentList, true);
-        for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
+        // Remove module from old fragment dependencies.
+        // We will generally only remove module fragment
+        // dependencies when we are uninstalling the module.
+        for (int i = 0; (m_fragments != null) && (i < m_fragments.length); i++)
         {
-            calculateContentPath(m_fragmentContents[i], contentList, false);
+            ((ModuleImpl) m_fragments[i]).removeDependentHost(this);
         }
-        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
-        // is on the bundle's class path and then creating content
-        // objects for everything on the class path.
 
-        // Create a list to contain the content path for the specified content.
-        List localContentList = new ArrayList();
-
-        // Find class path meta-data.
-        String classPath = (String) m_headerMap.get(FelixConstants.BUNDLE_CLASSPATH);
-        // Parse the class path into strings.
-        String[] classPathStrings = ManifestParser.parseDelimitedString(
-            classPath, FelixConstants.CLASS_PATH_SEPARATOR);
+        // Update the dependencies on the new fragments.
+        m_fragments = fragments;
 
-        if (classPathStrings == null)
+        // 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.
+        if (m_fragments != null)
         {
-            classPathStrings = new String[0];
+            IContent[] fragmentContents = new IContent[m_fragments.length];
+            for (int i = 0; (m_fragments != null) && (i < m_fragments.length); i++)
+            {
+                ((ModuleImpl) m_fragments[i]).addDependentHost(this);
+                fragmentContents[i] =
+                    m_fragments[i].getContent()
+                        .getEntryAsContent(FelixConstants.CLASS_PATH_DOT);
+            }
+            // Now attach the fragment contents to our content loader.
+            attachFragmentContents(fragmentContents);
         }
+    }
 
-        // Create the bundles class path.
-        for (int i = 0; i < classPathStrings.length; i++)
+    private void attachFragmentContents(IContent[] fragmentContents)
+        throws Exception
+    {
+        // Close existing fragment contents.
+        if (m_fragmentContents != null)
         {
-            // Remove any leading slash, since all bundle class path
-            // entries are relative to the root of the bundle.
-            classPathStrings[i] = (classPathStrings[i].startsWith("/"))
-                ? classPathStrings[i].substring(1)
-                : classPathStrings[i];
-
-            // Check for the bundle itself on the class path.
-            if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
-            {
-                localContentList.add(content);
-            }
-            else
+            for (int i = 0; i < m_fragmentContents.length; i++)
             {
-                // 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_fragmentContents != null) && (fragIdx < m_fragmentContents.length);
-                    fragIdx++)
-                {
-                    embeddedContent = m_fragmentContents[fragIdx].getEntryAsContent(classPathStrings[i]);
-                }
-                // If we found the embedded content, then add it to the
-                // class path content list.
-                if (embeddedContent != null)
-                {
-                    localContentList.add(embeddedContent);
-                }
-                else
-                {
-// TODO: FRAMEWORK - Per the spec, this should fire a FrameworkEvent.INFO event;
-//       need to create an "Eventer" class like "Logger" perhaps.
-                    m_logger.log(Logger.LOG_INFO,
-                        "Class path entry not found: "
-                        + classPathStrings[i]);
-                }
+                m_fragmentContents[i].close();
             }
         }
+        m_fragmentContents = fragmentContents;
 
-        // If there is nothing on the class path, then include
-        // "." by default, as per the spec.
-        if (localContentList.size() == 0)
+        if (m_contentPath != null)
         {
-            localContentList.add(content);
+            for (int i = 0; i < m_contentPath.length; i++)
+            {
+                m_contentPath[i].close();
+            }
         }
+        m_contentPath = initializeContentPath();
+    }
 
-        // Now add the local contents to the global content list and return it.
-        contentList.addAll(localContentList);
-        return contentList;
+    public synchronized IModule[] getDependentHosts()
+    {
+        return m_dependentHosts;
     }
 
-// From ModuleClassLoader
+    public synchronized void addDependentHost(IModule module)
+    {
+        m_dependentHosts = addDependent(m_dependentHosts, module);
+    }
 
-    Object findClassOrResourceByDelegation(String name, boolean isClass)
-        throws ClassNotFoundException, ResourceNotFoundException
+    public synchronized void removeDependentHost(IModule module)
     {
-        // First, try to resolve the originating module.
-// TODO: FRAMEWORK - Consider opimizing this call to resolve, since it is called
-// for each class load.
-        try
-        {
-            m_resolver.resolve(this);
-        }
-        catch (ResolveException ex)
+        m_dependentHosts = removeDependent(m_dependentHosts, module);
+    }
+
+    public synchronized IModule[] getDependentImporters()
+    {
+        return m_dependentImporters;
+    }
+
+    public synchronized void addDependentImporter(IModule module)
+    {
+        m_dependentImporters = addDependent(m_dependentImporters, module);
+    }
+
+    public synchronized void removeDependentImporter(IModule module)
+    {
+        m_dependentImporters = removeDependent(m_dependentImporters, module);
+    }
+
+    public synchronized IModule[] getDependentRequirers()
+    {
+        return m_dependentRequirers;
+    }
+
+    public synchronized void addDependentRequirer(IModule module)
+    {
+        m_dependentRequirers = addDependent(m_dependentRequirers, module);
+    }
+
+    public synchronized void removeDependentRequirer(IModule module)
+    {
+        m_dependentRequirers = removeDependent(m_dependentRequirers, module);
+    }
+
+    public synchronized IModule[] getDependents()
+    {
+        IModule[] dependents = new IModule[
+            m_dependentHosts.length + m_dependentImporters.length + m_dependentRequirers.length];
+        System.arraycopy(
+            m_dependentHosts,
+            0,
+            dependents,
+            0,
+            m_dependentHosts.length);
+        System.arraycopy(
+            m_dependentImporters,
+            0,
+            dependents,
+            m_dependentHosts.length,
+            m_dependentImporters.length);
+        System.arraycopy(
+            m_dependentRequirers,
+            0,
+            dependents,
+            m_dependentHosts.length + m_dependentImporters.length,
+            m_dependentRequirers.length);
+        return dependents;
+    }
+
+    private static IModule[] addDependent(IModule[] modules, IModule module)
+    {
+        // Make sure the dependent module is not already present.
+        for (int i = 0; i < modules.length; i++)
         {
-            if (isClass)
-            {
-                // We do not use the resolve exception as the
-                // cause of the exception, since this would
-                // potentially leak internal module information.
-                throw new ClassNotFoundException(
-                    name + ": cannot resolve package "
-                    + ex.getRequirement());
-            }
-            else
+            if (modules[i].equals(module))
             {
-                // The spec states that if the bundle cannot be resolved, then
-                // only the local bundle's resources should be searched. So we
-                // will ask the module's own class path.
-                URL url = getResourceFromModule(name);
-                if (url != null)
-                {
-                    return url;
-                }
-
-                // We need to throw a resource not found exception.
-                throw new ResourceNotFoundException(
-                    name + ": cannot resolve package "
-                    + ex.getRequirement());
+                return modules;
             }
         }
+        IModule[] tmp = new IModule[modules.length + 1];
+        System.arraycopy(modules, 0, tmp, 0, modules.length);
+        tmp[modules.length] = module;
+        return tmp;
+    }
 
-        // Get the package of the target class/resource.
-        String pkgName = (isClass)
-            ? Util.getClassPackage(name)
-            : Util.getResourcePackage(name);
+    private static IModule[] removeDependent(IModule[] modules, IModule module)
+    {
+        IModule[] tmp = modules;
 
-        // Delegate any packages listed in the boot delegation
-        // property to the parent class loader.
-        Object result = null;
-        for (int i = 0; i < m_bootPkgs.length; i++)
+        // Make sure the dependent module is present.
+        for (int i = 0; i < modules.length; i++)
         {
-            // A wildcarded boot delegation package will be in the form of "foo.",
-            // so if the package is wildcarded do a startsWith() or a regionMatches()
-            // to ignore the trailing "." to determine if the request should be
-            // delegated to the parent class loader. If the package is not wildcarded,
-            // then simply do an equals() test to see if the request should be
-            // delegated to the parent class loader.
-            if (pkgName.length() > 0)
+            if (modules[i].equals(module))
             {
-                // Only consider delegation if we have a package name, since
-                // we don't want to promote the default package. The spec does
-                // not take a stand on this issue.
-                if ((m_bootPkgWildcards[i] &&
-                    (pkgName.startsWith(m_bootPkgs[i]) ||
-                    m_bootPkgs[i].regionMatches(0, pkgName, 0, pkgName.length())))
-                    || (!m_bootPkgWildcards[i] && m_bootPkgs[i].equals(pkgName)))
+                // If this is the module, then point to empty list.
+                if ((modules.length - 1) == 0)
                 {
-                    try
-                    {
-                        result = (isClass)
-                            ? (Object) getClass().getClassLoader().loadClass(name)
-                            : (Object) getClass().getClassLoader().getResource(name);
-                        // If this is a java.* package, then always terminate the
-                        // search; otherwise, continue to look locally if not found.
-                        if (m_bootPkgs[i].startsWith("java.") || (result != null))
-                        {
-                            return result;
-                        }
-                    }
-                    catch (ClassNotFoundException ex)
+                    tmp = new IModule[0];
+                }
+                // Otherwise, we need to do some array copying.
+                else
+                {
+                    tmp = new IModule[modules.length - 1];
+                    System.arraycopy(modules, 0, tmp, 0, i);
+                    if (i < tmp.length)
                     {
-                        // If this is a java.* package, then always terminate the
-                        // search; otherwise, continue to look locally if not found.
-                        if (m_bootPkgs[i].startsWith("java."))
-                        {
-                            throw ex;
-                        }
-                        else
-                        {
-                            break;
-                        }
+                        System.arraycopy(modules, i + 1, tmp, i, tmp.length - i);
                     }
                 }
+                break;
             }
         }
 
-        // Look in the module's imports. Note that the search may
-        // be aborted if this method throws an exception, otherwise
-        // it continues if a null is returned.
-        result = searchImports(name, isClass);
+        return tmp;
+    }
+
+    public synchronized void close()
+    {
+        m_content.close();
+        for (int i = 0; (m_contentPath != null) && (i < m_contentPath.length); i++)
+        {
+            m_contentPath[i].close();
+        }
+        for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
+        {
+            m_fragmentContents[i].close();
+        }
+        m_classLoader = null;
+    }
+
+    public synchronized void setURLPolicy(IURLPolicy urlPolicy)
+    {
+        m_urlPolicy = urlPolicy;
+    }
 
-        // If not found, try the module's own class path.
-        if (result == null)
-        {
-            result = (isClass)
-                ? (Object) getClassFromModule(name)
-                : (Object) getResourceFromModule(name);
+    public synchronized IURLPolicy getURLPolicy()
+    {
+        return m_urlPolicy;
+    }
 
-            // If still not found, then try the module's dynamic imports.
-            if (result == null)
-            {
-                result = searchDynamicImports(name, pkgName, isClass);
-            }
-        }
+    public synchronized void setSecurityContext(Object securityContext)
+    {
+        m_protectionDomain = (ProtectionDomain) securityContext;
+    }
 
-        if (result == null)
+    public synchronized Object getSecurityContext()
+    {
+        return m_protectionDomain;
+    }
+
+    public String toString()
+    {
+        return m_id;
+    }
+
+    private synchronized ModuleClassLoader getClassLoader()
+    {
+        if (m_classLoader == null)
         {
-            if (isClass)
-            {
-                throw new ClassNotFoundException(name);
-            }
-            else
-            {
-                throw new ResourceNotFoundException(name);
-            }
+// TODO: REFACTOR - SecureAction fix needed.
+              m_classLoader = new ModuleClassLoader();
+//            m_classLoader = m_secureAction.createModuleClassLoader(
+//                this, m_protectionDomain);
         }
-
-        return result;
+        return m_classLoader;
     }
 
     private Object searchImports(String name, boolean isClass)
@@ -1244,4 +1305,592 @@
 
         return null;
     }
+
+    private static final Constructor m_dexFileClassConstructor;
+    private static final Method m_dexFileClassLoadClass;
+    static
+    {
+        Constructor dexFileClassConstructor = null;
+        Method dexFileClassLoadClass = null;
+        try
+        {
+            Class dexFileClass;
+            try
+            {
+                dexFileClass = Class.forName("dalvik.system.DexFile");
+            }
+            catch (Exception ex)
+            {
+                dexFileClass = Class.forName("android.dalvik.DexFile");
+            }
+
+            dexFileClassConstructor = dexFileClass.getConstructor(
+                new Class[] { java.io.File.class });
+            dexFileClassLoadClass = dexFileClass.getMethod("loadClass",
+                new Class[] { String.class, ClassLoader.class });
+        }
+        catch (Exception ex)
+        {
+           dexFileClassConstructor = null;
+           dexFileClassLoadClass = null;
+        }
+        m_dexFileClassConstructor = dexFileClassConstructor;
+        m_dexFileClassLoadClass = dexFileClassLoadClass;
+    }
+
+    public class ModuleClassLoader extends SecureClassLoader
+    {
+        private final Map m_jarContentToDexFile;
+
+        public ModuleClassLoader()
+        {
+            if (m_dexFileClassConstructor != null)
+            {
+                m_jarContentToDexFile = new HashMap();
+            }
+            else
+            {
+                m_jarContentToDexFile = null;
+            }
+        }
+
+        public IModule getModule()
+        {
+            return ModuleImpl.this;
+        }
+
+        protected Class loadClass(String name, boolean resolve)
+            throws ClassNotFoundException
+        {
+            Class clazz = null;
+
+            // Make sure the class was not already loaded.
+            synchronized (this)
+            {
+                clazz = findLoadedClass(name);
+            }
+
+            if (clazz == null)
+            {
+                try
+                {
+                    return (Class) findClassOrResourceByDelegation(name, true);
+                }
+                catch (ResourceNotFoundException ex)
+                {
+                    // This should never happen since we are asking for a class,
+                    // so just ignore it.
+                }
+                catch (ClassNotFoundException cnfe)
+                {
+                    ClassNotFoundException ex = cnfe;
+                    String msg = name;
+                    if (m_logger.getLogLevel() >= Logger.LOG_DEBUG)
+                    {
+                        msg = diagnoseClassLoadError(m_resolver, ModuleImpl.this, name);
+                        ex = new ClassNotFoundException(msg, cnfe);
+                    }
+                    throw ex;
+                }
+            }
+
+            // Resolve the class and return it.
+            if (resolve)
+            {
+                resolveClass(clazz);
+            }
+            return clazz;
+        }
+
+        protected Class findClass(String name) throws ClassNotFoundException
+        {
+            // Do a quick check here to see if we can short-circuit this
+            // entire process if the class was already loaded.
+            Class clazz = null;
+            synchronized (this)
+            {
+                clazz = findLoadedClass(name);
+            }
+
+            // Search for class in module.
+            if (clazz == null)
+            {
+                String actual = name.replace('.', '/') + ".class";
+
+                byte[] bytes = null;
+
+                // Check the module class path.
+                IContent[] contentPath = getContentPath();
+                IContent content = null;
+                for (int i = 0;
+                    (bytes == null) &&
+                    (i < contentPath.length); i++)
+                {
+                    bytes = contentPath[i].getEntryAsBytes(actual);
+                    content = contentPath[i];
+                }
+
+                if (bytes != null)
+                {
+                    // Before we actually attempt to define the class, grab
+                    // the lock for this class loader and make sure than no
+                    // other thread has defined this class in the meantime.
+                    synchronized (this)
+                    {
+                        clazz = findLoadedClass(name);
+
+                        if (clazz == null)
+                        {
+                            // We need to try to define a Package object for the class
+                            // before we call defineClass(). Get the package name and
+                            // see if we have already created the package.
+                            String pkgName = Util.getClassPackage(name);
+                            if (pkgName.length() > 0)
+                            {
+                                if (getPackage(pkgName) == null)
+                                {
+                                    Object[] params = definePackage(pkgName);
+                                    if (params != null)
+                                    {
+                                        definePackage(
+                                            pkgName,
+                                            (String) params[0],
+                                            (String) params[1],
+                                            (String) params[2],
+                                            (String) params[3],
+                                            (String) params[4],
+                                            (String) params[5],
+                                            null);
+                                    }
+                                    else
+                                    {
+                                        definePackage(pkgName, null, null,
+                                            null, null, null, null, null);
+                                    }
+                                }
+                            }
+
+                            // If we can load the class from a dex file do so
+                            if (content instanceof JarContent)
+                            {
+                                try
+                                {
+                                    clazz = getDexFileClass((JarContent) content, name, this);
+                                }
+                                catch (Exception ex)
+                                {
+                                    // Looks like we can't
+                                }
+                            }
+
+                            if (clazz == null)
+                            {
+                                // If we have a security context, then use it to
+                                // define the class with it for security purposes,
+                                // otherwise define the class without a protection domain.
+                                if (m_protectionDomain != null)
+                                {
+                                    clazz = defineClass(name, bytes, 0, bytes.length,
+                                        m_protectionDomain);
+                                }
+                                else
+                                {
+                                    clazz = defineClass(name, bytes, 0, bytes.length);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            return clazz;
+        }
+
+        private Object[] definePackage(String pkgName)
+        {
+            String spectitle = (String) m_headerMap.get("Specification-Title");
+            String specversion = (String) m_headerMap.get("Specification-Version");
+            String specvendor = (String) m_headerMap.get("Specification-Vendor");
+            String impltitle = (String) m_headerMap.get("Implementation-Title");
+            String implversion = (String) m_headerMap.get("Implementation-Version");
+            String implvendor = (String) m_headerMap.get("Implementation-Vendor");
+            if ((spectitle != null)
+                || (specversion != null)
+                || (specvendor != null)
+                || (impltitle != null)
+                || (implversion != null)
+                || (implvendor != null))
+            {
+                return new Object[] {
+                    spectitle, specversion, specvendor, impltitle, implversion, implvendor
+                };
+            }
+            return null;
+        }
+
+        private Class getDexFileClass(JarContent content, String name, ClassLoader loader)
+            throws Exception
+        {
+            if (m_jarContentToDexFile == null)
+            {
+                return null;
+            }
+
+            Object dexFile = null;
+
+            if (!m_jarContentToDexFile.containsKey(content))
+            {
+                try
+                {
+                    dexFile = m_dexFileClassConstructor.newInstance(
+                        new Object[] { content.getFile() });
+                }
+                finally
+                {
+                    m_jarContentToDexFile.put(content, dexFile);
+                }
+            }
+            else
+            {
+                dexFile = m_jarContentToDexFile.get(content);
+            }
+
+            if (dexFile != null)
+            {
+                return (Class) m_dexFileClassLoadClass.invoke(dexFile,
+                    new Object[] { name.replace('.','/'), loader });
+            }
+            return null;
+        }
+
+        public URL getResource(String name)
+        {
+            return ModuleImpl.this.getResourceByDelegation(name);
+        }
+
+        protected URL findResource(String name)
+        {
+            return getResourceLocal(name);
+        }
+
+        // The findResources() method should only look at the module itself, but
+        // instead it tries to delegate because in Java version prior to 1.5 the
+        // getResources() method was final and could not be overridden. We should
+        // override getResources() like getResource() to make it delegate, but we
+        // can't. As a workaround, we make findResources() delegate instead.
+        protected Enumeration findResources(String name)
+        {
+            return getResourcesByDelegation(name);
+        }
+
+        protected String findLibrary(String name)
+        {
+            // Remove leading slash, if present.
+            if (name.startsWith("/"))
+            {
+                name = name.substring(1);
+            }
+
+            R4Library[] libs = getNativeLibraries();
+            for (int i = 0; (libs != null) && (i < libs.length); i++)
+            {
+                if (libs[i].match(name))
+                {
+                    return getContent().getEntryAsNativeLibrary(libs[i].getEntryName());
+                }
+            }
+
+            return null;
+        }
+
+        public String toString()
+        {
+            return ModuleImpl.this.toString();
+        }
+    }
+
+    private static String diagnoseClassLoadError(
+        FelixResolver resolver, ModuleImpl module, String name)
+    {
+        // We will try to do some diagnostics here to help the developer
+        // deal with this exception.
+
+        // Get package name.
+        String pkgName = Util.getClassPackage(name);
+
+        // First, get the bundle ID of the module doing the class loader.
+        long impId = Util.getBundleIdFromModuleId(module.getId());
+
+        // Next, check to see if the module imports the package.
+        IWire[] wires = module.getWires();
+        for (int i = 0; (wires != null) && (i < wires.length); i++)
+        {
+            if (wires[i].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
+                wires[i].getCapability().getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
+            {
+                long expId = Util.getBundleIdFromModuleId(wires[i].getExporter().getId());
+
+                StringBuffer sb = new StringBuffer("*** Package '");
+                sb.append(pkgName);
+                sb.append("' is imported by bundle ");
+                sb.append(impId);
+                sb.append(" from bundle ");
+                sb.append(expId);
+                sb.append(", but the exported package from bundle ");
+                sb.append(expId);
+                sb.append(" does not contain the requested class '");
+                sb.append(name);
+                sb.append("'. Please verify that the class name is correct in the importing bundle ");
+                sb.append(impId);
+                sb.append(" and/or that the exported package is correctly bundled in ");
+                sb.append(expId);
+                sb.append(". ***");
+
+                return sb.toString();
+            }
+        }
+
+        // Next, check to see if the package was optionally imported and
+        // whether or not there is an exporter available.
+        IRequirement[] reqs = module.getRequirements();
+/*
+* TODO: RB - Fix diagnostic message for optional imports.
+        for (int i = 0; (reqs != null) && (i < reqs.length); i++)
+        {
+            if (reqs[i].getName().equals(pkgName) && reqs[i].isOptional())
+            {
+                // Try to see if there is an exporter available.
+                IModule[] exporters = getResolvedExporters(reqs[i], true);
+                exporters = (exporters.length == 0)
+                    ? getUnresolvedExporters(reqs[i], true) : exporters;
+
+                // An exporter might be available, but it may have attributes
+                // that do not match the importer's required attributes, so
+                // check that case by simply looking for an exporter of the
+                // desired package without any attributes.
+                if (exporters.length == 0)
+                {
+                    IRequirement pkgReq = new Requirement(
+                        ICapability.PACKAGE_NAMESPACE, "(package=" + pkgName + ")");
+                    exporters = getResolvedExporters(pkgReq, true);
+                    exporters = (exporters.length == 0)
+                        ? getUnresolvedExporters(pkgReq, true) : exporters;
+                }
+
+                long expId = (exporters.length == 0)
+                    ? -1 : Util.getBundleIdFromModuleId(exporters[0].getId());
+
+                StringBuffer sb = new StringBuffer("*** Class '");
+                sb.append(name);
+                sb.append("' was not found, but this is likely normal since package '");
+                sb.append(pkgName);
+                sb.append("' is optionally imported by bundle ");
+                sb.append(impId);
+                sb.append(".");
+                if (exporters.length > 0)
+                {
+                    sb.append(" However, bundle ");
+                    sb.append(expId);
+                    if (reqs[i].isSatisfied(
+                        Util.getExportPackage(exporters[0], reqs[i].getName())))
+                    {
+                        sb.append(" does export this package. Bundle ");
+                        sb.append(expId);
+                        sb.append(" must be installed before bundle ");
+                        sb.append(impId);
+                        sb.append(" is resolved or else the optional import will be ignored.");
+                    }
+                    else
+                    {
+                        sb.append(" does export this package with attributes that do not match.");
+                    }
+                }
+                sb.append(" ***");
+
+                return sb.toString();
+            }
+        }
+*/
+        // Next, check to see if the package is dynamically imported by the module.
+/* TODO: RESOLVER: Need to fix this too.
+        IRequirement[] dynamics = module.getDefinition().getDynamicRequirements();
+        for (int dynIdx = 0; dynIdx < dynamics.length; dynIdx++)
+        {
+            IRequirement target = createDynamicRequirement(dynamics[dynIdx], pkgName);
+            if (target != null)
+            {
+                // Try to see if there is an exporter available.
+                PackageSource[] exporters = getResolvedCandidates(target);
+                exporters = (exporters.length == 0)
+                    ? getUnresolvedCandidates(target) : exporters;
+
+                // An exporter might be available, but it may have attributes
+                // that do not match the importer's required attributes, so
+                // check that case by simply looking for an exporter of the
+                // desired package without any attributes.
+                if (exporters.length == 0)
+                {
+                    try
+                    {
+                        IRequirement pkgReq = new Requirement(
+                            ICapability.PACKAGE_NAMESPACE, "(package=" + pkgName + ")");
+                        exporters = getResolvedCandidates(pkgReq);
+                        exporters = (exporters.length == 0)
+                            ? getUnresolvedCandidates(pkgReq) : exporters;
+                    }
+                    catch (InvalidSyntaxException ex)
+                    {
+                        // This should never happen.
+                    }
+                }
+
+                long expId = (exporters.length == 0)
+                    ? -1 : Util.getBundleIdFromModuleId(exporters[0].m_module.getId());
+
+                StringBuffer sb = new StringBuffer("*** Class '");
+                sb.append(name);
+                sb.append("' was not found, but this is likely normal since package '");
+                sb.append(pkgName);
+                sb.append("' is dynamically imported by bundle ");
+                sb.append(impId);
+                sb.append(".");
+                if (exporters.length > 0)
+                {
+                    try
+                    {
+                        if (!target.isSatisfied(
+                            Util.getSatisfyingCapability(exporters[0].m_module,
+                                new Requirement(ICapability.PACKAGE_NAMESPACE, "(package=" + pkgName + ")"))))
+                        {
+                            sb.append(" However, bundle ");
+                            sb.append(expId);
+                            sb.append(" does export this package with attributes that do not match.");
+                        }
+                    }
+                    catch (InvalidSyntaxException ex)
+                    {
+                        // This should never happen.
+                    }
+                }
+                sb.append(" ***");
+
+                return sb.toString();
+            }
+        }
+*/
+        IRequirement pkgReq = null;
+        try
+        {
+            pkgReq = new Requirement(ICapability.PACKAGE_NAMESPACE, "(package=" + pkgName + ")");
+        }
+        catch (InvalidSyntaxException ex)
+        {
+            // This should never happen.
+        }
+        PackageSource[] exporters =
+            resolver.getResolvedCandidates(pkgReq);
+        exporters = (exporters.length == 0)
+            ? resolver.getUnresolvedCandidates(pkgReq)
+            : exporters;
+        if (exporters.length > 0)
+        {
+            boolean classpath = false;
+            try
+            {
+                ModuleClassLoader.class.getClassLoader().loadClass(name);
+                classpath = true;
+            }
+            catch (NoClassDefFoundError err)
+            {
+                // Ignore
+            }
+            catch (Exception ex)
+            {
+                // Ignore
+            }
+
+            long expId = Util.getBundleIdFromModuleId(exporters[0].m_module.getId());
+
+            StringBuffer sb = new StringBuffer("*** Class '");
+            sb.append(name);
+            sb.append("' was not found because bundle ");
+            sb.append(impId);
+            sb.append(" does not import '");
+            sb.append(pkgName);
+            sb.append("' even though bundle ");
+            sb.append(expId);
+            sb.append(" does export it.");
+            if (classpath)
+            {
+                sb.append(" Additionally, the class is also available from the system class loader. There are two fixes: 1) Add an import for '");
+                sb.append(pkgName);
+                sb.append("' to bundle ");
+                sb.append(impId);
+                sb.append("; imports are necessary for each class directly touched by bundle code or indirectly touched, such as super classes if their methods are used. ");
+                sb.append("2) Add package '");
+                sb.append(pkgName);
+                sb.append("' to the '");
+                sb.append(Constants.FRAMEWORK_BOOTDELEGATION);
+                sb.append("' property; a library or VM bug can cause classes to be loaded by the wrong class loader. The first approach is preferable for preserving modularity.");
+            }
+            else
+            {
+                sb.append(" To resolve this issue, add an import for '");
+                sb.append(pkgName);
+                sb.append("' to bundle ");
+                sb.append(impId);
+                sb.append(".");
+            }
+            sb.append(" ***");
+
+            return sb.toString();
+        }
+
+        // Next, try to see if the class is available from the system
+        // class loader.
+        try
+        {
+            ModuleClassLoader.class.getClassLoader().loadClass(name);
+
+            StringBuffer sb = new StringBuffer("*** Package '");
+            sb.append(pkgName);
+            sb.append("' is not imported by bundle ");
+            sb.append(impId);
+            sb.append(", nor is there any bundle that exports package '");
+            sb.append(pkgName);
+            sb.append("'. However, the class '");
+            sb.append(name);
+            sb.append("' is available from the system class loader. There are two fixes: 1) Add package '");
+            sb.append(pkgName);
+            sb.append("' to the '");
+            sb.append(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
+            sb.append("' property and modify bundle ");
+            sb.append(impId);
+            sb.append(" to import this package; this causes the system bundle to export class path packages. 2) Add package '");
+            sb.append(pkgName);
+            sb.append("' to the '");
+            sb.append(Constants.FRAMEWORK_BOOTDELEGATION);
+            sb.append("' property; a library or VM bug can cause classes to be loaded by the wrong class loader. The first approach is preferable for preserving modularity.");
+            sb.append(" ***");
+
+            return sb.toString();
+        }
+        catch (Exception ex2)
+        {
+        }
+
+        // Finally, if there are no imports or exports for the package
+        // and it is not available on the system class path, simply
+        // log a message saying so.
+        StringBuffer sb = new StringBuffer("*** Class '");
+        sb.append(name);
+        sb.append("' was not found. Bundle ");
+        sb.append(impId);
+        sb.append(" does not import package '");
+        sb.append(pkgName);
+        sb.append("', nor is the package exported by any other bundle or available from the system class loader.");
+        sb.append(" ***");
+
+        return sb.toString();
+    }
 }
\ No newline at end of file



Mime
View raw message