You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2009/01/30 02:20:06 UTC
svn commit: r739113 [1/2] - in
/felix/trunk/framework/src/main/java/org/apache/felix: framework/
framework/searchpolicy/ framework/util/ moduleloader/
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