Separate BundleWire implementation from BundleRevision implementation. (FELIX-2950)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1102824 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index f6d2c99..88bfd9d 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -131,21 +131,6 @@
// Remove the revision from the resolver state.
getFramework().getResolver().removeRevision(br);
- // Set fragments to null, which will remove the revision from all
- // of its dependent fragment revisions.
- try
- {
- ((BundleRevisionImpl) br).attachFragments(null);
- }
- catch (Exception ex)
- {
- getFramework().getLogger().log(
- br.getBundle(), Logger.LOG_ERROR, "Error detaching fragments.", ex);
- }
- // Set wires to null, which will remove the revision from all
- // of its dependent revisions.
- ((BundleRevisionImpl) br).setWires(null, null);
-
// Close the revision's content.
((BundleRevisionImpl) br).close();
}
@@ -467,8 +452,7 @@
}
}
- private static List<BundleRevision> createLocalizationRevisionList(
- BundleRevisionImpl bri)
+ private static List<BundleRevision> createLocalizationRevisionList(BundleRevision br)
{
// If the revision is a fragment, then we actually need
// to search its host and associated fragments for its
@@ -477,19 +461,21 @@
// version instead of the fragment itself. If there are
// no hosts, but the revision is a fragment, then just
// search the revision itself.
- if (Util.isFragment(bri))
+ if (Util.isFragment(br))
{
- List<BundleWire> hostWires = bri.getWires();
- if ((hostWires != null) && (hostWires.size() > 0))
+ if (br.getWiring() != null)
{
- bri = (BundleRevisionImpl) hostWires.get(0).getProviderWiring().getRevision();
- for (int hostIdx = 1; hostIdx < hostWires.size(); hostIdx++)
+ List<BundleWire> hostWires = br.getWiring().getRequiredWires(null);
+ if ((hostWires != null) && (hostWires.size() > 0))
{
- if (bri.getVersion().compareTo(
- hostWires.get(hostIdx).getProviderWiring().getRevision().getVersion()) < 0)
+ br = hostWires.get(0).getProviderWiring().getRevision();
+ for (int hostIdx = 1; hostIdx < hostWires.size(); hostIdx++)
{
- bri = (BundleRevisionImpl)
- hostWires.get(hostIdx).getProviderWiring().getRevision();
+ if (br.getVersion().compareTo(
+ hostWires.get(hostIdx).getProviderWiring().getRevision().getVersion()) < 0)
+ {
+ br = hostWires.get(hostIdx).getProviderWiring().getRevision();
+ }
}
}
}
@@ -497,8 +483,8 @@
// Create a list of the revision and any attached fragment revisions.
List<BundleRevision> result = new ArrayList<BundleRevision>();
- result.add(bri);
- List<BundleRevision> fragments = bri.getFragments();
+ result.add(br);
+ List<BundleRevision> fragments = ((BundleWiringImpl) br.getWiring()).getFragments();
if (fragments != null)
{
result.addAll(fragments);
@@ -1075,13 +1061,16 @@
boolean used = false;
for (int i = 0; !unresolved && !used && (i < m_revisions.size()); i++)
{
- List<BundleRevision> dependents =
- ((BundleRevisionImpl) m_revisions.get(i)).getDependents();
- for (int j = 0; (dependents != null) && (j < dependents.size()) && !used; j++)
+ if (m_revisions.get(i).getWiring() != null)
{
- if (dependents.get(j) != m_revisions.get(i))
+ List<BundleRevision> dependents =
+ ((BundleRevisionImpl) m_revisions.get(i)).getDependents();
+ for (int j = 0; (dependents != null) && (j < dependents.size()) && !used; j++)
{
- used = true;
+ if (dependents.get(j) != m_revisions.get(i))
+ {
+ used = true;
+ }
}
}
}
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
index 032419d..1813617 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
@@ -31,7 +31,6 @@
import java.security.ProtectionDomain;
import java.security.SecureClassLoader;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
@@ -45,8 +44,6 @@
import org.apache.felix.framework.Felix.StatefulResolver;
import org.apache.felix.framework.cache.JarContent;
import org.apache.felix.framework.resolver.Content;
-import org.apache.felix.framework.resolver.HostedCapability;
-import org.apache.felix.framework.resolver.HostedRequirement;
import org.apache.felix.framework.resolver.ResolveException;
import org.apache.felix.framework.resolver.ResolverWire;
import org.apache.felix.framework.resolver.ResourceNotFoundException;
@@ -71,7 +68,7 @@
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
-public class BundleRevisionImpl implements BundleRevision, BundleWiring
+public class BundleRevisionImpl implements BundleRevision
{
public final static int EAGER_ACTIVATION = 0;
public final static int LAZY_ACTIVATION = 1;
@@ -90,81 +87,33 @@
private final Version m_version;
private final List<BundleCapability> m_declaredCaps;
- private List<BundleCapability> m_resolvedCaps = null;
private final List<BundleRequirement> m_declaredReqs;
- private List<BundleRequirement> m_resolvedReqs = null;
- private final List<BundleRequirement> m_declaredDynReqs;
- private List<BundleRequirement> m_resolvedDynReqs = null;
- private final List<R4Library> m_nativeLibraries;
+ private final List<BundleRequirement> m_declaredDynamicReqs;
+ private final List<R4Library> m_declaredNativeLibs;
private final int m_declaredActivationPolicy;
private final List<String> m_activationIncludes;
private final List<String> m_activationExcludes;
private final Bundle m_bundle;
- private List<BundleRevision> m_fragments = null;
- private List<BundleWire> m_wires = null;
- private Map<String, BundleRevision> m_importedPkgs = null;
- private Map<String, List<BundleRevision>> m_requiredPkgs = null;
private List<BundleRevision> m_dependentImporters = new ArrayList<BundleRevision>(0);
private List<BundleRevision> m_dependentRequirers = new ArrayList<BundleRevision>(0);
- private volatile boolean m_isResolved = false;
private Content[] m_contentPath;
- private Content[] m_fragmentContents = null;
- private BundleClassLoader m_classLoader;
private boolean m_isActivationTriggered = false;
private ProtectionDomain m_protectionDomain = null;
private final static SecureAction m_secureAction = new SecureAction();
- // Bundle-specific class loader for boot delegation.
- private final ClassLoader m_bootClassLoader;
- // Default class loader for boot delegation.
- private final static ClassLoader m_defBootClassLoader;
-
- // Statically define the default class loader for boot delegation.
- static
- {
- ClassLoader cl = null;
- try
- {
- Constructor ctor = m_secureAction.getDeclaredConstructor(
- SecureClassLoader.class, new Class[] { ClassLoader.class });
- m_secureAction.setAccesssible(ctor);
- cl = (ClassLoader) m_secureAction.invoke(ctor, new Object[] { null });
- }
- catch (Throwable ex)
- {
- // On Android we get an exception if we set the parent class loader
- // to null, so we will work around that case by setting the parent
- // class loader to the system class loader in getClassLoader() below.
- cl = null;
- System.err.println("Problem creating boot delegation class loader: " + ex);
- }
- m_defBootClassLoader = cl;
- }
+ // Bundle wiring when resolved.
+ private volatile BundleWiringImpl m_wiring = null;
// Boot delegation packages.
private final String[] m_bootPkgs;
private final boolean[] m_bootPkgWildcards;
- // Boolean flag to enable/disable implicit boot delegation.
- private final boolean m_implicitBootDelegation;
- // Boolean flag to enable/disable local URLs.
- private final boolean m_useLocalURLs;
-
// Re-usable security manager for accessing class context.
private static SecurityManagerEx m_sm = new SecurityManagerEx();
- // Thread local to detect class loading cycles.
- private final ThreadLocal m_cycleCheck = new ThreadLocal();
-
- // Thread local to keep track of deferred activation.
- private static final ThreadLocal m_deferredActivation = new ThreadLocal();
-
- // Flag indicating whether we are on an old JVM or not.
- private volatile static boolean m_isPreJava5 = false;
-
/**
* This constructor is used by the extension manager, since it needs
* a constructor that does not throw an exception.
@@ -195,16 +144,11 @@
m_version = null;
m_declaredCaps = Collections.EMPTY_LIST;
m_declaredReqs = Collections.EMPTY_LIST;
- m_declaredDynReqs = Collections.EMPTY_LIST;
- m_nativeLibraries = null;
+ m_declaredDynamicReqs = Collections.EMPTY_LIST;
+ m_declaredNativeLibs = null;
m_declaredActivationPolicy = EAGER_ACTIVATION;
m_activationExcludes = null;
m_activationIncludes = null;
- m_implicitBootDelegation = false;
- m_useLocalURLs =
- (m_configMap.get(FelixConstants.USE_LOCALURLS_PROP) == null)
- ? false : true;
- m_bootClassLoader = m_defBootClassLoader;
}
BundleRevisionImpl(
@@ -225,28 +169,6 @@
m_bootPkgs = bootPkgs;
m_bootPkgWildcards = bootPkgWildcards;
- m_implicitBootDelegation =
- (m_configMap.get(FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP) == null)
- || Boolean.valueOf(
- (String) m_configMap.get(
- FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP)).booleanValue();
-
- m_useLocalURLs =
- (m_configMap.get(FelixConstants.USE_LOCALURLS_PROP) == null)
- ? false : true;
-
- ClassLoader bootLoader = m_defBootClassLoader;
- Object map = m_configMap.get(FelixConstants.BOOT_CLASSLOADERS_PROP);
- if (map instanceof Map)
- {
- Object l = ((Map) map).get(bundle);
- if (l instanceof ClassLoader)
- {
- bootLoader = (ClassLoader) l;
- }
- }
- m_bootClassLoader = bootLoader;
-
ManifestParser mp = new ManifestParser(m_logger, m_configMap, this, m_headerMap);
// Record some of the parsed metadata. Note, if this is an extension
@@ -256,8 +178,8 @@
m_version = mp.getBundleVersion();
m_declaredCaps = mp.isExtension() ? null : mp.getCapabilities();
m_declaredReqs = mp.getRequirements();
- m_declaredDynReqs = mp.getDynamicRequirements();
- m_nativeLibraries = mp.getLibraries();
+ m_declaredDynamicReqs = mp.getDynamicRequirements();
+ m_declaredNativeLibs = mp.getLibraries();
m_declaredActivationPolicy = mp.getActivationPolicy();
m_activationExcludes = (mp.getActivationExcludeDirective() == null)
? null
@@ -269,6 +191,42 @@
m_isExtension = mp.isExtension();
}
+ int getDeclaredActivationPolicy()
+ {
+ return m_declaredActivationPolicy;
+ }
+
+ List<String> getActivationExcludes()
+ {
+ return m_activationExcludes;
+ }
+
+ List<String> getActivationIncludes()
+ {
+ return m_activationIncludes;
+ }
+
+ URLStreamHandler getURLStreamHandler()
+ {
+ return m_streamHandler;
+ }
+
+ // TODO: OSGi R4.3 - Figure out how to handle this. Here we provide access
+ // needed for BundleWiringImpl, but for implicit boot delegation property
+ // we store it in BundleWiringImpl.
+ String[] getBootDelegationPackages()
+ {
+ return m_bootPkgs;
+ }
+
+ // TODO: OSGi R4.3 - Figure out how to handle this. Here we provide access
+ // needed for BundleWiringImpl, but for implicit boot delegation property
+ // we store it in BundleWiringImpl.
+ boolean[] getBootDelegationPackageWildcards()
+ {
+ return m_bootPkgWildcards;
+ }
+
//
// BundleRevision methods.
//
@@ -283,7 +241,7 @@
return m_version;
}
- public synchronized List<BundleCapability> getDeclaredCapabilities(String namespace)
+ public List<BundleCapability> getDeclaredCapabilities(String namespace)
{
List<BundleCapability> result = m_declaredCaps;
if (namespace != null)
@@ -300,7 +258,7 @@
return result;
}
- public synchronized List<BundleRequirement> getDeclaredRequirements(String namespace)
+ public List<BundleRequirement> getDeclaredRequirements(String namespace)
{
List<BundleRequirement> result = m_declaredReqs;
if (namespace != null)
@@ -317,11 +275,6 @@
return result;
}
- public synchronized List<BundleRequirement> getDeclaredDynamicRequirements()
- {
- return m_declaredDynReqs;
- }
-
public int getTypes()
{
if (getHeaders().containsKey(Constants.FRAGMENT_HOST))
@@ -333,7 +286,7 @@
public BundleWiring getWiring()
{
- return (m_isResolved) ? this : null;
+ return m_wiring;
}
public Bundle getBundle()
@@ -342,182 +295,9 @@
}
//
- // BundleWiring methods.
- //
-
- public boolean isCurrent()
- {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- public boolean isInUse()
- {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- public synchronized List<BundleCapability> getCapabilities(String namespace)
- {
- if (m_isResolved && (m_resolvedCaps == null))
- {
- List capList = (m_declaredCaps == null)
- ? new ArrayList<BundleCapability>()
- : new ArrayList<BundleCapability>(m_declaredCaps);
- for (int fragIdx = 0;
- (m_fragments != null) && (fragIdx < m_fragments.size());
- fragIdx++)
- {
- List<BundleCapability> caps =
- m_fragments.get(fragIdx).getDeclaredCapabilities(null);
- for (int capIdx = 0;
- (caps != null) && (capIdx < caps.size());
- capIdx++)
- {
- if (caps.get(capIdx).getNamespace().equals(
- BundleCapabilityImpl.PACKAGE_NAMESPACE))
- {
- capList.add(
- new HostedCapability(
- this, (BundleCapabilityImpl) caps.get(capIdx)));
- }
- }
- }
- m_resolvedCaps = Collections.unmodifiableList(capList);
- }
- List<BundleCapability> result = m_resolvedCaps;
- if (namespace != null)
- {
- result = new ArrayList<BundleCapability>();
- for (BundleCapability cap : m_resolvedCaps)
- {
- if (cap.getNamespace().equals(namespace))
- {
- result.add(cap);
- }
- }
- }
- return result;
- }
-
- public synchronized List<BundleRequirement> getRequirements(String namespace)
- {
- if (m_isResolved && (m_resolvedReqs == null))
- {
- List<BundleRequirement> reqList = (m_declaredReqs == null)
- ? new ArrayList() : new ArrayList(m_declaredReqs);
- for (int fragIdx = 0;
- (m_fragments != null) && (fragIdx < m_fragments.size());
- fragIdx++)
- {
- List<BundleRequirement> reqs =
- m_fragments.get(fragIdx).getDeclaredRequirements(null);
- for (int reqIdx = 0;
- (reqs != null) && (reqIdx < reqs.size());
- reqIdx++)
- {
- if (reqs.get(reqIdx).getNamespace().equals(
- BundleCapabilityImpl.PACKAGE_NAMESPACE)
- || reqs.get(reqIdx).getNamespace().equals(
- BundleCapabilityImpl.BUNDLE_NAMESPACE))
- {
- reqList.add(
- new HostedRequirement(
- this, (BundleRequirementImpl) reqs.get(reqIdx)));
- }
- }
- }
- m_resolvedReqs = Collections.unmodifiableList(reqList);
- }
- List<BundleRequirement> result = m_resolvedReqs;
- if (namespace != null)
- {
- result = new ArrayList<BundleRequirement>();
- for (BundleRequirement req : m_resolvedReqs)
- {
- if (req.getNamespace().equals(namespace))
- {
- result.add(req);
- }
- }
- }
- return result;
- }
-
- public List<BundleWire> getProvidedWires(String namespace)
- {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- public List<BundleWire> getRequiredWires(String namespace)
- {
- return m_wires;
- }
-
- public BundleRevision getRevision()
- {
- return this;
- }
-
- public synchronized ClassLoader getClassLoader()
- {
- if (m_classLoader == null)
- {
- // Determine which class loader to use based on which
- // Java platform we are running on.
- Class clazz;
- if (m_isPreJava5)
- {
- clazz = BundleClassLoader.class;
- }
- else
- {
- try
- {
- clazz = BundleClassLoaderJava5.class;
- }
- catch (Throwable th)
- {
- // If we are on pre-Java5 then we will get a verify error
- // here since we try to override a getResources() which is
- // a final method in pre-Java5.
- m_isPreJava5 = true;
- clazz = BundleClassLoader.class;
- }
- }
-
- // Use SecureAction to create the class loader if security is
- // enabled; otherwise, create it directly.
- try
- {
- Constructor ctor = (Constructor) m_secureAction.getConstructor(
- clazz, new Class[] { BundleRevisionImpl.class, ClassLoader.class });
- m_classLoader = (BundleClassLoader)
- m_secureAction.invoke(ctor,
- new Object[] { this, determineParentClassLoader() });
- }
- catch (Exception ex)
- {
- throw new RuntimeException("Unable to create module class loader: "
- + ex.getMessage() + " [" + ex.getClass().getName() + "]");
- }
- }
- return m_classLoader;
- }
-
- public List<URL> findEntries(String path, String filePattern, int options)
- {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- public Collection<String> listResources(String path, String filePattern, int options)
- {
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
- //
// Implementating details.
//
-
public Map getHeaders()
{
return m_headerMap;
@@ -533,75 +313,14 @@
return m_manifestVersion;
}
- public synchronized List<BundleRequirement> getResolvedDynamicRequirements()
+ public List<BundleRequirement> getDeclaredDynamicRequirements()
{
- if (m_isResolved && (m_resolvedDynReqs == null))
- {
- List<BundleRequirement> reqList = (m_declaredDynReqs == null)
- ? new ArrayList() : new ArrayList(m_declaredDynReqs);
- for (int fragIdx = 0;
- (m_fragments != null) && (fragIdx < m_fragments.size());
- fragIdx++)
- {
- List<BundleRequirement> reqs =
- ((BundleRevisionImpl) m_fragments.get(fragIdx))
- .getDeclaredDynamicRequirements();
- for (int reqIdx = 0;
- (reqs != null) && (reqIdx < reqs.size());
- reqIdx++)
- {
- if (reqs.get(reqIdx).getNamespace().equals(
- BundleCapabilityImpl.PACKAGE_NAMESPACE))
- {
- reqList.add(reqs.get(reqIdx));
- }
- }
- }
- m_resolvedDynReqs = Collections.unmodifiableList(reqList);
- }
- return m_resolvedDynReqs;
+ return m_declaredDynamicReqs;
}
- public synchronized List<R4Library> getNativeLibraries()
+ public List<R4Library> getDeclaredNativeLibraries()
{
- List<R4Library> result = null;
- if (m_isResolved)
- {
- List<R4Library> nativeList = (m_nativeLibraries == null)
- ? new ArrayList() : new ArrayList(m_nativeLibraries);
- for (int fragIdx = 0;
- (m_fragments != null) && (fragIdx < m_fragments.size());
- fragIdx++)
- {
- List<R4Library> libs =
- ((BundleRevisionImpl) m_fragments.get(fragIdx))
- .getNativeLibraries();
- for (int reqIdx = 0;
- (libs != null) && (reqIdx < libs.size());
- reqIdx++)
- {
- nativeList.add(libs.get(reqIdx));
- }
- }
-
- // We need to return null here if we don't have any libraries, since a
- // zero-length array is used to indicate that matching native libraries
- // could not be found when resolving the bundle.
- result = (nativeList.isEmpty())
- ? null
- : Collections.unmodifiableList(nativeList);
- }
- else
- {
- result = m_nativeLibraries;
- }
-
- return result;
- }
-
- public int getDeclaredActivationPolicy()
- {
- return m_declaredActivationPolicy;
+ return m_declaredNativeLibs;
}
synchronized boolean isActivationTriggered()
@@ -643,196 +362,31 @@
return m_id;
}
-// TODO: OSGi R4.3 - This really shouldn't be public, but it is needed by the
-// resolver to determine if a bundle can dynamically import.
- public synchronized boolean hasPackageSource(String pkgName)
- {
- return (m_importedPkgs.containsKey(pkgName) || m_requiredPkgs.containsKey(pkgName));
- }
-
-// TODO: OSGi R4.3 - This really shouldn't be public, but it is needed by the
-// to implement dynamic imports.
- public synchronized BundleRevision getImportedPackageSource(String pkgName)
- {
- return m_importedPkgs.get(pkgName);
- }
-
- private synchronized List<BundleRevision> getRequiredPackageSources(String pkgName)
- {
- return m_requiredPkgs.get(pkgName);
- }
-
- public synchronized List<BundleWire> getWires()
- {
- return m_wires;
- }
-
- public synchronized void addDynamicWire(ResolverWire rw)
- {
- // This not only sets the wires for the module, but it also records
- // the dependencies this module has on other modules (i.e., the provider
- // end of the wire) to simplify bookkeeping.
-
- BundleWire wire = new BundleWireImpl(
- rw.getRequirer(),
- rw.getRequirement(),
- rw.getProvider(),
- rw.getCapability());
- m_wires.add(wire);
- m_importedPkgs.put(
- (String) wire.getCapability().getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR),
- rw.getProvider());
-
-// TODO: OSGi R4.3 - What's the correct way to handle this?
-// ((BundleRevisionImpl) m_wires.get(i).getProviderWiring().getRevision())
-// .addDependentImporter(this);
- ((BundleRevisionImpl) rw.getProvider()).addDependentImporter(this);
- }
-
- public synchronized void setWires(
+ public synchronized void resolve(
+ List<BundleRevision> fragments,
List<ResolverWire> rws,
Map<ResolverWire, Set<String>> requiredPkgWires)
+ throws Exception
{
// This not only sets the wires for the module, but it also records
// the dependencies this module has on other modules (i.e., the provider
// end of the wire) to simplify bookkeeping.
- // For fragments we don't need to capture any additional dependency
- // information, since the wires are sufficient, so just record the
- // new wires. The wires are to the hosts to which the fragment is attached.
- boolean isFragment = Util.isFragment(this);
-
- // Fragments are allowed to add new wires to existing wires, but
- // normal bundles should never be wired again if they already
- // have wires.
- if (!isFragment && (m_wires != null) && (rws != null))
+ // If there is an existing wiring, then dispose of it, which will
+ // remove any dependencies on other wirings.
+ if (m_wiring != null)
{
- throw new IllegalStateException("The revision already has wires.");
+ m_wiring.dispose();
+ m_wiring = null;
}
- // Convert resolver wires to bundle wires and aggregate all imported
- // or required packages.
- List<BundleWire> wires = null;
- Map<String, BundleRevision> importedPkgs = null;
- Map<String, List<BundleRevision>> requiredPkgs = null;
if (rws != null)
{
- wires = new ArrayList<BundleWire>(rws.size());
- importedPkgs = new HashMap<String, BundleRevision>();
- requiredPkgs = new HashMap<String, List<BundleRevision>>();
-
- for (ResolverWire rw : rws)
- {
- wires.add(
- new BundleWireImpl(
- rw.getRequirer(),
- rw.getRequirement(),
- rw.getProvider(),
- rw.getCapability()));
-
- if (isFragment)
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "FRAGMENT WIRE: "
- + this + " -> hosted by -> " + rw.getProvider());
- }
- else
- {
- m_logger.log(Logger.LOG_DEBUG, "WIRE: " + rw);
-
- if (rw.getCapability().getNamespace()
- .equals(BundleCapabilityImpl.PACKAGE_NAMESPACE))
- {
- importedPkgs.put(
- (String) rw.getCapability().getAttributes()
- .get(BundleCapabilityImpl.PACKAGE_ATTR),
- rw.getProvider());
- }
- else if (rw.getCapability().getNamespace()
- .equals(BundleCapabilityImpl.BUNDLE_NAMESPACE))
- {
- for (String pkgName : requiredPkgWires.get(rw))
- {
- List<BundleRevision> revs = requiredPkgs.get(pkgName);
- if (revs != null)
- {
- revs.add(rw.getProvider());
- }
- else
- {
- revs = new ArrayList<BundleRevision>();
- revs.add(rw.getProvider());
- requiredPkgs.put(pkgName, revs);
- }
- }
- }
- }
- }
- }
-
- // Remove module from old wire modules' dependencies,
- // since we are no longer dependent on any the moduels
- // from the old wires.
- for (int i = 0; !isFragment && (m_wires != null) && (i < m_wires.size()); i++)
- {
- if (m_wires.get(i).getCapability().getNamespace()
- .equals(BundleCapabilityImpl.BUNDLE_NAMESPACE))
- {
- ((BundleRevisionImpl) m_wires.get(i).getProviderWiring().getRevision())
- .removeDependentRequirer(this);
- }
- else if (m_wires.get(i).getCapability().getNamespace()
- .equals(BundleCapabilityImpl.PACKAGE_NAMESPACE))
- {
- ((BundleRevisionImpl) m_wires.get(i).getProviderWiring().getRevision())
- .removeDependentImporter(this);
- }
- }
-
- // If we already have wires, then add new wires to existing list (this
- // should only happen for fragments). Otherwise, simply set wires value.
- if ((m_wires != null) && (wires != null))
- {
- m_wires.addAll(wires);
- }
- else
- {
- m_wires = wires;
- }
- m_importedPkgs = importedPkgs;
- m_requiredPkgs = requiredPkgs;
-
- // Add ourself as a dependent to the new wires' modules.
- if (!isFragment && (rws != null))
- {
- for (ResolverWire rw : rws)
- {
- if (rw.getCapability().getNamespace()
- .equals(BundleCapabilityImpl.BUNDLE_NAMESPACE))
- {
- ((BundleRevisionImpl) rw.getProvider()).addDependentRequirer(this);
- }
- else if (rw.getCapability().getNamespace()
- .equals(BundleCapabilityImpl.PACKAGE_NAMESPACE))
- {
- ((BundleRevisionImpl) rw.getProvider()).addDependentImporter(this);
- }
- }
+ m_wiring = new BundleWiringImpl(
+ m_logger, m_configMap, m_resolver, this, fragments, rws, requiredPkgWires);
}
}
- public boolean isResolved()
- {
- return m_isResolved;
- }
-
- public void setResolved()
- {
- m_isResolved = true;
- }
-
-
public synchronized void setSecurityContext(Object securityContext)
{
m_protectionDomain = (ProtectionDomain) securityContext;
@@ -883,10 +437,6 @@
{
List contentList = new ArrayList();
calculateContentPath(this, m_content, contentList, true);
- for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
- {
- calculateContentPath(m_fragments.get(i), m_fragmentContents[i], contentList, false);
- }
return (Content[]) contentList.toArray(new Content[contentList.size()]);
}
@@ -933,17 +483,6 @@
// Try to find the embedded class path entry in the current
// content.
Content embeddedContent = content.getEntryAsContent(classPathStrings.get(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.get(i));
- }
// If we found the embedded content, then add it to the
// class path content list.
if (embeddedContent != null)
@@ -973,170 +512,6 @@
return contentList;
}
- public Class getClassByDelegation(String name) throws ClassNotFoundException
- {
- // We do not call getClassLoader().loadClass() for arrays because
- // it does not correctly handle array types, which is necessary in
- // cases like deserialization using a wrapper class loader.
- if ((name != null) && (name.length() > 0) && (name.charAt(0) == '['))
- {
- return Class.forName(name, false, getClassLoader());
- }
- return getClassLoader().loadClass(name);
- }
-
- public URL getResourceByDelegation(String name)
- {
- try
- {
- return (URL) findClassOrResourceByDelegation(name, false);
- }
- catch (ClassNotFoundException ex)
- {
- // This should never be thrown because we are loading resources.
- }
- catch (ResourceNotFoundException ex)
- {
- m_logger.log(m_bundle,
- Logger.LOG_DEBUG,
- ex.getMessage());
- }
- return null;
- }
-
- private Object findClassOrResourceByDelegation(String name, boolean isClass)
- throws ClassNotFoundException, ResourceNotFoundException
- {
- Object result = null;
-
- Set requestSet = (Set) m_cycleCheck.get();
- if (requestSet == null)
- {
- requestSet = new HashSet();
- m_cycleCheck.set(requestSet);
- }
- if (requestSet.add(name))
- {
- try
- {
- // First, try to resolve the originating revision.
- m_resolver.resolve(this);
-
- // 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.
- if (shouldBootDelegate(pkgName))
- {
- try
- {
- // Get the appropriate class loader for delegation.
- ClassLoader bdcl = getBootDelegationClassLoader();
- result = (isClass)
- ? (Object) bdcl.loadClass(name)
- : (Object) bdcl.getResource(name);
- // If this is a java.* package, then always terminate the
- // search; otherwise, continue to look locally if not found.
- if (pkgName.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 (pkgName.startsWith("java."))
- {
- throw ex;
- }
- }
- }
-
- // Look in the revision'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(pkgName, name, isClass);
-
- // If not found, try the revision's own class path.
- if (result == null)
- {
- result = (isClass)
- ? (Object) ((BundleClassLoader) getClassLoader()).findClass(name)
- : (Object) getResourceLocal(name);
-
- // If still not found, then try the revision's dynamic imports.
- if (result == null)
- {
- result = searchDynamicImports(pkgName, name, isClass);
- }
- }
- }
- 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 + " not found because "
- + getBundle()
- + " cannot resolve: "
- + 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 + " not found because "
- + getBundle()
- + " cannot resolve: "
- + ex.getRequirement());
- }
- }
- finally
- {
- requestSet.remove(name);
- }
- }
- else
- {
- // If a cycle is detected, we should return null to break the
- // cycle. This should only ever be return to internal class
- // loading code and not to the actual instigator of the class load.
- return null;
- }
-
- if (result == null)
- {
- if (isClass)
- {
- throw new ClassNotFoundException(
- name + " not found by " + this.getBundle());
- }
- else
- {
- throw new ResourceNotFoundException(
- name + " not found by " + this.getBundle());
- }
- }
-
- return result;
- }
-
URL getResourceLocal(String name)
{
URL url = null;
@@ -1169,159 +544,7 @@
return url;
}
- public Enumeration getResourcesByDelegation(String name)
- {
- Set requestSet = (Set) m_cycleCheck.get();
- if (requestSet == null)
- {
- requestSet = new HashSet();
- m_cycleCheck.set(requestSet);
- }
- if (!requestSet.contains(name))
- {
- requestSet.add(name);
- try
- {
- return findResourcesByDelegation(name);
- }
- finally
- {
- requestSet.remove(name);
- }
- }
-
- return null;
- }
-
- private Enumeration findResourcesByDelegation(String name)
- {
- Enumeration urls = null;
- List completeUrlList = new ArrayList();
-
- // First, try to resolve the originating module.
- 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.
- return getResourcesLocal(name);
- }
-
- // Get the package of the target class/resource.
- String pkgName = Util.getResourcePackage(name);
-
- // Delegate any packages listed in the boot delegation
- // property to the parent class loader.
- if (shouldBootDelegate(pkgName))
- {
- try
- {
- // Get the appropriate class loader for delegation.
- ClassLoader bdcl = getBootDelegationClassLoader();
- urls = bdcl.getResources(name);
- }
- catch (IOException ex)
- {
- // This shouldn't happen and even if it does, there
- // is nothing we can do, so just ignore it.
- }
- // If this is a java.* package, then always terminate the
- // search; otherwise, continue to look locally.
- if (pkgName.startsWith("java."))
- {
- return urls;
- }
-
- completeUrlList.add(urls);
- }
-
- // Look in the revisions's imported packages. If the package is
- // imported, then we stop searching no matter the result since
- // imported packages cannot be split.
- BundleRevision provider = getImportedPackageSource(pkgName);
- if (provider != null)
- {
- // Delegate to the provider revision.
- urls = ((BundleRevisionImpl) provider).getResourcesByDelegation(name);
-
- // If we find any resources, then add them.
- if ((urls != null) && (urls.hasMoreElements()))
- {
- completeUrlList.add(urls);
- }
-
- // Always return here since imported packages cannot be split
- // across required bundles or the revision's content.
- return new CompoundEnumeration((Enumeration[])
- completeUrlList.toArray(new Enumeration[completeUrlList.size()]));
- }
-
- // See whether we can get the resource from the required bundles and
- // regardless of whether or not this is the case continue to the next
- // step potentially passing on the result of this search (if any).
- List<BundleRevision> providers = getRequiredPackageSources(pkgName);
- if (providers != null)
- {
- for (BundleRevision p : providers)
- {
- // Delegate to the provider revision.
- urls = ((BundleRevisionImpl) p).getResourcesByDelegation(name);
-
- // If we find any resources, then add them.
- if ((urls != null) && (urls.hasMoreElements()))
- {
- completeUrlList.add(urls);
- }
-
- // Do not return here, since required packages can be split
- // across the revision's content.
- }
- }
-
- // 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 = getResourcesLocal(name);
- if ((urls != null) && (urls.hasMoreElements()))
- {
- completeUrlList.add(urls);
- }
- else
- {
- // If not found, then try the module's dynamic imports.
- // At this point, the module's imports were searched and so was the
- // the module's content. Now we make an attempt to load the
- // class/resource via a dynamic import, if possible.
- try
- {
- provider = m_resolver.resolve(this, pkgName);
- }
- catch (ResolveException ex)
- {
- // Ignore this since it is likely normal.
- }
- if (provider != null)
- {
- // Delegate to the provider revision.
- urls = ((BundleRevisionImpl) provider).getResourcesByDelegation(name);
-
- // If we find any resources, then add them.
- if ((urls != null) && (urls.hasMoreElements()))
- {
- completeUrlList.add(urls);
- }
- }
- }
-
- return new CompoundEnumeration((Enumeration[])
- completeUrlList.toArray(new Enumeration[completeUrlList.size()]));
- }
-
- private Enumeration getResourcesLocal(String name)
+ Enumeration getResourcesLocal(String name)
{
List l = new ArrayList();
@@ -1461,102 +684,6 @@
return null;
}
- //
- // Fragment and dependency management methods.
- //
-
- public synchronized List<BundleRevision> getFragments()
- {
- return m_fragments;
- }
-
- private synchronized void detachHost(BundleRevision host)
- {
- if (m_wires != null)
- {
- for (Iterator<BundleWire> it = m_wires.iterator(); it.hasNext(); )
- {
- BundleWire wire = it.next();
- if (wire.getCapability().getNamespace().equals(BundleCapabilityImpl.HOST_NAMESPACE)
- && wire.getProviderWiring().getRevision().equals(host))
- {
- it.remove();
- break;
- }
- }
- }
- }
-
- public synchronized void attachFragments(List<BundleRevision> fragments) throws Exception
- {
- // Remove the host wires for this module from old fragments.
- // We will generally only remove host wires when we are uninstalling
- // the module.
- if (m_fragments != null)
- {
- for (BundleRevision fragment : m_fragments)
- {
- ((BundleRevisionImpl) fragment).detachHost(this);
- }
- }
-
- // Close previous fragment contents.
- for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
- {
- m_fragmentContents[i].close();
- }
- m_fragmentContents = null;
-
- // Close the old content path, since we'll need to recalculate it for
- // for the added (or removed) fragments.
- for (int i = 0; (m_contentPath != null) && (i < m_contentPath.length); i++)
- {
- // Don't close this module's content, if it is on the content path.
- if (m_content != m_contentPath[i])
- {
- m_contentPath[i].close();
- }
- }
- m_contentPath = null;
-
- // Remove cached capabilities and requirements.
- m_resolvedCaps = null;
- m_resolvedReqs = null;
- m_resolvedDynReqs = null;
-
- // Update the dependencies on the new fragments.
- m_fragments = fragments;
-
- // We need to sort the fragments and add ourself as a dependent of each one.
- // We also need to create an array of fragment contents to attach to our
- // content path.
- if (m_fragments != null)
- {
- // Sort fragments according to ID order, if necessary.
- // Note that this sort order isn't 100% correct since
- // it uses a string, but it is likely close enough and
- // avoids having to create more objects.
- if (m_fragments.size() > 1)
- {
- SortedMap<String, BundleRevision> sorted = new TreeMap<String, BundleRevision>();
- for (BundleRevision f : m_fragments)
- {
- sorted.put(((BundleRevisionImpl) f).getId(), f);
- }
- m_fragments = new ArrayList(sorted.values());
- }
- m_fragmentContents = new Content[m_fragments.size()];
- for (int i = 0; (m_fragments != null) && (i < m_fragments.size()); i++)
- {
- m_fragmentContents[i] =
- ((BundleRevisionImpl) m_fragments.get(i)).getContent()
- .getEntryAsContent(FelixConstants.CLASS_PATH_DOT);
- }
- // Recalculate the content path for the new fragments.
- m_contentPath = initializeContentPath();
- }
- }
-
public synchronized List<BundleRevision> getDependentImporters()
{
return m_dependentImporters;
@@ -1599,9 +726,11 @@
if (Util.isFragment(this))
{
dependents = new ArrayList<BundleRevision>();
- for (int i = 0; (m_wires != null) && (i < m_wires.size()); i++)
+ List<BundleWire> wires = (m_wiring == null)
+ ? null : m_wiring.getRequiredWires(null);
+ for (int i = 0; (wires != null) && (i < wires.size()); i++)
{
- dependents.add(m_wires.get(i).getCapability().getRevision());
+ dependents.add(wires.get(i).getProviderWiring().getRevision());
}
}
else
@@ -1616,18 +745,20 @@
public synchronized void close()
{
+ try
+ {
+ resolve(null, null, null);
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(Logger.LOG_ERROR, "Error releasing revision: " + ex.getMessage(), ex);
+ }
m_content.close();
for (int i = 0; (m_contentPath != null) && (i < m_contentPath.length); i++)
{
m_contentPath[i].close();
}
m_contentPath = null;
- for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
- {
- m_fragmentContents[i].close();
- }
- m_fragmentContents = null;
- m_classLoader = null;
}
@Override
@@ -1636,815 +767,6 @@
return m_id;
}
- private ClassLoader determineParentClassLoader()
- {
- // Determine the class loader's parent based on the
- // configuration property; use boot class loader by
- // default.
- String cfg = (String) m_configMap.get(Constants.FRAMEWORK_BUNDLE_PARENT);
- cfg = (cfg == null) ? Constants.FRAMEWORK_BUNDLE_PARENT_BOOT : cfg;
- final ClassLoader parent;
- if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_APP))
- {
- parent = m_secureAction.getSystemClassLoader();
- }
- else if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_EXT))
- {
- parent = m_secureAction.getParentClassLoader(m_secureAction.getSystemClassLoader());
- }
- else if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_FRAMEWORK))
- {
- parent = m_secureAction.getClassLoader(BundleRevisionImpl.class);
- }
- // On Android we cannot set the parent class loader to be null, so
- // we special case that situation here and set it to the system
- // class loader by default instead, which is not really spec.
- else if (m_bootClassLoader == null)
- {
- parent = m_secureAction.getSystemClassLoader();
- }
- else
- {
- parent = null;
- }
- return parent;
- }
-
- private Object searchImports(String pkgName, String name, boolean isClass)
- throws ClassNotFoundException, ResourceNotFoundException
- {
- // Check if the package is imported.
- BundleRevision provider = getImportedPackageSource(pkgName);
- if (provider != null)
- {
- // If we find the class or resource, then return it.
- Object result = (isClass)
- ? (Object) ((BundleRevisionImpl) provider).getClassByDelegation(name)
- : (Object) ((BundleRevisionImpl) provider).getResourceByDelegation(name);
- if (result != null)
- {
- return result;
- }
-
- // If no class was found, then we must throw an exception
- // since the provider of this package did not contain the
- // requested class and imported packages are atomic.
- throw new ClassNotFoundException(name);
- }
-
- // Check if the package is required.
- List<BundleRevision> providers = getRequiredPackageSources(pkgName);
- if (providers != null)
- {
- for (BundleRevision p : providers)
- {
- // If we find the class or resource, then return it.
- try
- {
- Object result = (isClass)
- ? (Object) ((BundleRevisionImpl) p).getClassByDelegation(name)
- : (Object) ((BundleRevisionImpl) p).getResourceByDelegation(name);
- if (result != null)
- {
- return result;
- }
- }
- catch (ClassNotFoundException ex)
- {
- // Since required packages can be split, don't throw an
- // exception here if it is not found. Instead, we'll just
- // continue searching other required bundles and the
- // revision's local content.
- }
- }
- }
-
- return null;
- }
-
- private Object searchDynamicImports(
- final String pkgName, final String name, final boolean isClass)
- throws ClassNotFoundException, ResourceNotFoundException
- {
- // At this point, the module's imports were searched and so was the
- // the module's content. Now we make an attempt to load the
- // class/resource via a dynamic import, if possible.
- BundleRevision provider = null;
- try
- {
- provider = m_resolver.resolve(this, pkgName);
- }
- catch (ResolveException ex)
- {
- // Ignore this since it is likely normal.
- }
-
- // If the dynamic import was successful, then this initial
- // time we must directly return the result from dynamically
- // created package sources, but subsequent requests for
- // classes/resources in the associated package will be
- // processed as part of normal static imports.
- if (provider != null)
- {
- // Return the class or resource.
- return (isClass)
- ? (Object) ((BundleRevisionImpl) provider).getClassByDelegation(name)
- : (Object) ((BundleRevisionImpl) provider).getResourceByDelegation(name);
- }
-
- // If implicit boot delegation is enabled, then try to guess whether
- // we should boot delegate.
- if (m_implicitBootDelegation)
- {
- // At this point, the class/resource could not be found by the bundle's
- // static or dynamic imports, nor its own content. Before we throw
- // an exception, we will try to determine if the instigator of the
- // class/resource load was a class from a bundle or not. This is necessary
- // because the specification mandates that classes on the class path
- // should be hidden (except for java.*), but it does allow for these
- // classes/resources to be exposed by the system bundle as an export.
- // However, in some situations classes on the class path make the faulty
- // assumption that they can access everything on the class path from
- // every other class loader that they come in contact with. This is
- // not true if the class loader in question is from a bundle. Thus,
- // this code tries to detect that situation. If the class instigating
- // the load request was NOT from a bundle, then we will make the
- // assumption that the caller actually wanted to use the parent class
- // loader and we will delegate to it. If the class was
- // from a bundle, then we will enforce strict class loading rules
- // for the bundle and throw an exception.
-
- // Get the class context to see the classes on the stack.
- final Class[] classes = m_sm.getClassContext();
- try
- {
- if (System.getSecurityManager() != null)
- {
- return AccessController
- .doPrivileged(new PrivilegedExceptionAction()
- {
- public Object run() throws Exception
- {
- return doImplicitBootDelegation(classes, name,
- isClass);
- }
- });
- }
- else
- {
- return doImplicitBootDelegation(classes, name, isClass);
- }
- }
- catch (PrivilegedActionException ex)
- {
- Exception cause = ex.getException();
- if (cause instanceof ClassNotFoundException)
- {
- throw (ClassNotFoundException) cause;
- }
- else
- {
- throw (ResourceNotFoundException) cause;
- }
- }
- }
- return null;
- }
-
- private Object doImplicitBootDelegation(Class[] classes, String name, boolean isClass)
- throws ClassNotFoundException, ResourceNotFoundException
- {
- // Start from 1 to skip security manager class.
- for (int i = 1; i < classes.length; i++)
- {
- // Find the first class on the call stack that is not from
- // the class loader that loaded the Felix classes or is not
- // a class loader or class itself, because we want to ignore
- // calls to ClassLoader.loadClass() and Class.forName() since
- // we are trying to find out who instigated the class load.
- // Also ignore inner classes of class loaders, since we can
- // assume they are a class loader too.
-
- // TODO: FRAMEWORK - This check is a hack and we should see if we can think
- // of another way to do it, since it won't necessarily work in all situations.
- // Since Felix uses threads for changing the start level
- // and refreshing packages, it is possible that there are no
- // bundle classes on the call stack; therefore, as soon as we
- // see Thread on the call stack we exit this loop. Other cases
- // where bundles actually use threads are not an issue because
- // the bundle classes will be on the call stack before the
- // Thread class.
- if (Thread.class.equals(classes[i]))
- {
- break;
- }
- // Break if the current class came from a bundle, since we should
- // not implicitly boot delegate in that case.
- else if (isClassLoadedFromBundleRevision(classes[i]))
- {
- break;
- }
- // Break if this goes through BundleImpl because it must be a call
- // to Bundle.loadClass() which should not implicitly boot delegate.
- else if (BundleImpl.class.equals(classes[i]))
- {
- break;
- }
- else if (isClassExternal(classes[i]))
- {
- try
- {
- // Return the class or resource from the parent class loader.
- return (isClass)
- ? (Object) m_secureAction.getClassLoader(this.getClass()).loadClass(name)
- : (Object) m_secureAction.getClassLoader(this.getClass()).getResource(name);
- }
- catch (NoClassDefFoundError ex)
- {
- // Ignore, will return null
- }
- break;
- }
- }
-
- return null;
- }
-
- private boolean isClassLoadedFromBundleRevision(Class clazz)
- {
- // The target class is loaded by a bundle class loader,
- // then return true.
- if (BundleClassLoader.class.isInstance(m_secureAction.getClassLoader(clazz)))
- {
- return true;
- }
-
- // If the target class was loaded from a class loader that
- // came from a bundle, then return true.
- ClassLoader last = null;
- for (ClassLoader cl = m_secureAction.getClassLoader(clazz);
- (cl != null) && (last != cl);
- cl = m_secureAction.getClassLoader(cl.getClass()))
- {
- last = cl;
- if (BundleClassLoader.class.isInstance(cl))
- {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Tries to determine whether the given class is part of the framework or not.
- * Framework classes include everything in org.apache.felix.framework.* and
- * org.osgi.framework.*. We also consider ClassLoader and Class to be internal
- * classes, because they are inserted into the stack trace as a result of
- * method overloading. Typically, ClassLoader or Class will be mixed in
- * between framework classes or will be at the point where the class loading
- * request enters the framework class loading mechanism, which will then be
- * followed by either bundle or external code, which will then exit our
- * attempt to determine if we should boot delegate or not. Other standard
- * class loaders, like URLClassLoader, are considered external classes and
- * should trigger boot delegation. This means that bundles can create standard
- * class loaders to get access to boot packages, but this is the standard
- * behavior of class loaders.
- * @param clazz the class to determine if it is external or not.
- * @return <tt>true</tt> if the class is external, otherwise <tt>false</tt>.
- */
- private boolean isClassExternal(Class clazz)
- {
- if (clazz.getName().startsWith("org.apache.felix.framework."))
- {
- return false;
- }
- else if (clazz.getName().startsWith("org.osgi.framework."))
- {
- return false;
- }
- else if (ClassLoader.class.equals(clazz))
- {
- return false;
- }
- else if (Class.class.equals(clazz))
- {
- return false;
- }
- return true;
- }
-
- boolean shouldBootDelegate(String pkgName)
- {
- // Always boot delegate if the bundle has a configured
- // boot class loader.
- if (m_bootClassLoader != m_defBootClassLoader)
- {
- return true;
- }
-
- boolean result = false;
-
- // 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 (pkgName.length() > 0)
- {
- for (int i = 0; !result && (i < m_bootPkgs.length); i++)
- {
- // Check if the boot package is wildcarded.
- // A wildcarded boot package will be in the form "foo.",
- // so a matching subpackage will start with "foo.", e.g.,
- // "foo.bar".
- if (m_bootPkgWildcards[i] && pkgName.startsWith(m_bootPkgs[i]))
- {
- return true;
- }
- // If not wildcarded, then check for an exact match.
- else if (m_bootPkgs[i].equals(pkgName))
- {
- return true;
- }
- }
- }
-
- return result;
- }
-
- ClassLoader getBootDelegationClassLoader()
- {
- // Get the appropriate class loader for delegation.
- ClassLoader parent = (m_classLoader == null)
- ? determineParentClassLoader() : m_secureAction.getParentClassLoader(m_classLoader);
- return (parent == null) ? m_bootClassLoader : parent;
- }
-
- private static final Constructor m_dexFileClassConstructor;
- private static final Method m_dexFileClassLoadDex;
- private static final Method m_dexFileClassLoadClass;
-
- static
- {
- Constructor dexFileClassConstructor = null;
- Method dexFileClassLoadDex = null;
- Method dexFileClassLoadClass = null;
- try
- {
- Class dexFileClass;
- try
- {
- dexFileClass = Class.forName("dalvik.system.DexFile");
- }
- catch (Exception ex)
- {
- dexFileClass = Class.forName("android.dalvik.DexFile");
- }
-
- try
- {
- dexFileClassLoadDex = dexFileClass.getMethod("loadDex",
- new Class[]{String.class, String.class, Integer.TYPE});
- }
- catch (Exception ex)
- {
- // Nothing we need to do
- }
- dexFileClassConstructor = dexFileClass.getConstructor(
- new Class[] { java.io.File.class });
- dexFileClassLoadClass = dexFileClass.getMethod("loadClass",
- new Class[] { String.class, ClassLoader.class });
- }
- catch (Throwable ex)
- {
- dexFileClassConstructor = null;
- dexFileClassLoadDex = null;
- dexFileClassLoadClass = null;
- }
- m_dexFileClassConstructor = dexFileClassConstructor;
- m_dexFileClassLoadDex= dexFileClassLoadDex;
- m_dexFileClassLoadClass = dexFileClassLoadClass;
- }
-
- public class BundleClassLoaderJava5 extends BundleClassLoader
- {
- public BundleClassLoaderJava5(ClassLoader parent)
- {
- super(parent);
- }
-
- @Override
- public Enumeration getResources(String name)
- {
- Enumeration urls = BundleRevisionImpl.this.getResourcesByDelegation(name);
- if (m_useLocalURLs)
- {
- urls = new ToLocalUrlEnumeration(urls);
- }
- return urls;
- }
-
- @Override
- protected Enumeration findResources(String name)
- {
- return BundleRevisionImpl.this.getResourcesLocal(name);
- }
- }
-
- public class BundleClassLoader extends SecureClassLoader implements BundleReference
- {
- private final Map m_jarContentToDexFile;
- private Object[][] m_cachedLibs = new Object[0][];
- private static final int LIBNAME_IDX = 0;
- private static final int LIBPATH_IDX = 1;
-
- public BundleClassLoader(ClassLoader parent)
- {
- super(parent);
- if (m_dexFileClassLoadClass != null)
- {
- m_jarContentToDexFile = new HashMap();
- }
- else
- {
- m_jarContentToDexFile = null;
- }
- }
-
- public Bundle getBundle()
- {
- return BundleRevisionImpl.this.getBundle();
- }
-
- @Override
- 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
- {
- clazz = (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, BundleRevisionImpl.this, name);
- ex = (msg != null)
- ? new ClassNotFoundException(msg, cnfe)
- : ex;
- }
- throw ex;
- }
- }
-
- // Resolve the class and return it.
- if (resolve)
- {
- resolveClass(clazz);
- }
- return clazz;
- }
-
- @Override
- protected Class findClass(String name) throws ClassNotFoundException
- {
- Class clazz = null;
-
- // Search for class in bundle revision.
- if (clazz == null)
- {
- String actual = name.replace('.', '/') + ".class";
-
- byte[] bytes = null;
-
- // Check the bundle class path.
- Content[] contentPath = getContentPath();
- Content content = null;
- for (int i = 0;
- (bytes == null) &&
- (i < contentPath.length); i++)
- {
- bytes = contentPath[i].getEntryAsBytes(actual);
- content = contentPath[i];
- }
-
- if (bytes != null)
- {
- // Get package name.
- String pkgName = Util.getClassPackage(name);
-
- // 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)
- {
- int activationPolicy =
- ((BundleImpl) getBundle()).isDeclaredActivationPolicyUsed()
- ? ((BundleRevisionImpl) ((BundleImpl) getBundle())
- .getCurrentRevision()).getDeclaredActivationPolicy()
- : EAGER_ACTIVATION;
-
- // If the revision is using deferred activation, then if
- // we load this class from this revision we need to activate
- // the bundle before returning the class. We will short
- // circuit the trigger matching if the trigger is already
- // tripped.
- boolean isTriggerClass = m_isActivationTriggered
- ? false : isActivationTrigger(pkgName);
- if (!m_isActivationTriggered
- && isTriggerClass
- && (activationPolicy == BundleRevisionImpl.LAZY_ACTIVATION)
- && (getBundle().getState() == Bundle.STARTING))
- {
- List deferredList = (List) m_deferredActivation.get();
- if (deferredList == null)
- {
- deferredList = new ArrayList();
- m_deferredActivation.set(deferredList);
- }
- deferredList.add(new Object[] { name, getBundle() });
- }
- // We need to try to define a Package object for the class
- // before we call defineClass() if we haven't already
- // created it.
- 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);
- }
- }
-
- // At this point if we have a trigger class, then the deferred
- // activation trigger has tripped.
- if (!m_isActivationTriggered && isTriggerClass && (clazz != null))
- {
- m_isActivationTriggered = true;
- }
- }
- }
-
- // Perform deferred activation without holding the class loader lock,
- // if the class we are returning is the instigating class.
- List deferredList = (List) m_deferredActivation.get();
- if ((deferredList != null)
- && (deferredList.size() > 0)
- && ((Object[]) deferredList.get(0))[0].equals(name))
- {
- for (int i = deferredList.size() - 1; i >= 0; i--)
- {
- try
- {
- ((BundleImpl) ((Object[]) deferredList.get(i))[1]).getFramework().activateBundle(
- (BundleImpl) ((Object[]) deferredList.get(i))[1], true);
- }
- catch (BundleException ex)
- {
- ex.printStackTrace();
- }
- }
- deferredList.clear();
- }
- }
- }
-
- 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
- {
- if (m_dexFileClassLoadDex != null)
- {
- dexFile = m_dexFileClassLoadDex.invoke(null,
- new Object[]{content.getFile().getAbsolutePath(),
- content.getFile().getAbsolutePath() + ".dex", new Integer(0)});
- }
- else
- {
- 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;
- }
-
- @Override
- public URL getResource(String name)
- {
- URL url = BundleRevisionImpl.this.getResourceByDelegation(name);
- if (m_useLocalURLs)
- {
- url = convertToLocalUrl(url);
- }
- return url;
- }
-
- @Override
- protected URL findResource(String name)
- {
- return BundleRevisionImpl.this.getResourceLocal(name);
- }
-
- // The findResources() method should only look at the revision 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.
- @Override
- protected Enumeration findResources(String name)
- {
- Enumeration urls = BundleRevisionImpl.this.getResourcesByDelegation(name);
- if (m_useLocalURLs)
- {
- urls = new ToLocalUrlEnumeration(urls);
- }
- return urls;
- }
-
- @Override
- protected String findLibrary(String name)
- {
- // Remove leading slash, if present.
- if (name.startsWith("/"))
- {
- name = name.substring(1);
- }
-
- String result = null;
- // CONCURRENCY: In the long run, we might want to break this
- // sync block in two to avoid manipulating the cache while
- // holding the lock, but for now we will do it the simple way.
- synchronized (this)
- {
- // Check to make sure we haven't already found this library.
- for (int i = 0; (result == null) && (i < m_cachedLibs.length); i++)
- {
- if (m_cachedLibs[i][LIBNAME_IDX].equals(name))
- {
- result = (String) m_cachedLibs[i][LIBPATH_IDX];
- }
- }
-
- // If we don't have a cached result, see if we have a matching
- // native library.
- if (result == null)
- {
- List<R4Library> libs = getNativeLibraries();
- for (int libIdx = 0; (libs != null) && (libIdx < libs.size()); libIdx++)
- {
- if (libs.get(libIdx).match(m_configMap, name))
- {
- // Search bundle content first for native library.
- result = getContent().getEntryAsNativeLibrary(
- libs.get(libIdx).getEntryName());
- // If not found, then search fragments in order.
- for (int i = 0;
- (result == null) && (m_fragmentContents != null)
- && (i < m_fragmentContents.length);
- i++)
- {
- result = m_fragmentContents[i].getEntryAsNativeLibrary(
- libs.get(libIdx).getEntryName());
- }
- }
- }
-
- // Remember the result for future requests.
- if (result != null)
- {
- Object[][] tmp = new Object[m_cachedLibs.length + 1][];
- System.arraycopy(m_cachedLibs, 0, tmp, 0, m_cachedLibs.length);
- tmp[m_cachedLibs.length] = new Object[] { name, result };
- m_cachedLibs = tmp;
- }
- }
- }
-
- return result;
- }
-
- @Override
- public String toString()
- {
- return BundleRevisionImpl.this.toString();
- }
- }
-
static URL convertToLocalUrl(URL url)
{
if (url.getProtocol().equals("bundle"))
@@ -2461,281 +783,4 @@
}
return url;
}
-
- static class ToLocalUrlEnumeration implements Enumeration
- {
- final Enumeration m_enumeration;
-
- ToLocalUrlEnumeration(Enumeration enumeration)
- {
- m_enumeration = enumeration;
- }
-
- public boolean hasMoreElements()
- {
- return m_enumeration.hasMoreElements();
- }
-
- public Object nextElement()
- {
- return convertToLocalUrl((URL) m_enumeration.nextElement());
- }
- }
-
- private static String diagnoseClassLoadError(
- StatefulResolver resolver, BundleRevisionImpl revision, 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);
- if (pkgName.length() == 0)
- {
- return null;
- }
-
- // First, get the bundle string of the revision doing the class loader.
- String importer = revision.getBundle().toString();
-
- // Next, check to see if the revision imports the package.
- List<BundleWire> wires = revision.getWires();
- for (int i = 0; (wires != null) && (i < wires.size()); i++)
- {
- if (wires.get(i).getCapability().getNamespace().equals(BundleCapabilityImpl.PACKAGE_NAMESPACE) &&
- wires.get(i).getCapability().getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
- {
- String exporter = wires.get(i).getProviderWiring().getBundle().toString();
-
- StringBuffer sb = new StringBuffer("*** Package '");
- sb.append(pkgName);
- sb.append("' is imported by bundle ");
- sb.append(importer);
- sb.append(" from bundle ");
- sb.append(exporter);
- sb.append(", but the exported package from bundle ");
- sb.append(exporter);
- 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(importer);
- sb.append(" and/or that the exported package is correctly bundled in ");
- sb.append(exporter);
- sb.append(". ***");
-
- return sb.toString();
- }
- }
-
- // Next, check to see if the package was optionally imported and
- // whether or not there is an exporter available.
- List<BundleRequirement> reqs = revision.getWiring().getRequirements(null);
-/*
-* 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 revision.
- if (resolver.isAllowedDynamicImport(revision, pkgName))
- {
- // Try to see if there is an exporter available.
- Map<String, String> dirs = Collections.EMPTY_MAP;
- Map<String, Object> attrs = new HashMap<String, Object>(1);
- attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
- BundleRequirementImpl req = new BundleRequirementImpl(
- revision, BundleCapabilityImpl.PACKAGE_NAMESPACE, dirs, attrs);
- Set<BundleCapability> exporters = resolver.getCandidates(req, false);
-
- BundleRevision provider = null;
- try
- {
- provider = resolver.resolve(revision, pkgName);
- }
- catch (Exception ex)
- {
- provider = null;
- }
-
- String exporter = (exporters.isEmpty())
- ? null : exporters.iterator().next().getRevision().getBundle().toString();
-
- 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(importer);
- sb.append(".");
- if ((exporters.size() > 0) && (provider == null))
- {
- sb.append(" However, bundle ");
- sb.append(exporter);
- sb.append(" does export this package with attributes that do not match.");
- }
- sb.append(" ***");
-
- return sb.toString();
- }
-
- // Next, check to see if there are any exporters for the package at all.
- Map<String, String> dirs = Collections.EMPTY_MAP;
- Map<String, Object> attrs = new HashMap<String, Object>(1);
- attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
- BundleRequirementImpl req = new BundleRequirementImpl(
- revision, BundleCapabilityImpl.PACKAGE_NAMESPACE, dirs, attrs);
- Set<BundleCapability> exports = resolver.getCandidates(req, false);
- if (exports.size() > 0)
- {
- boolean classpath = false;
- try
- {
- m_secureAction.getClassLoader(BundleClassLoader.class).loadClass(name);
- classpath = true;
- }
- catch (NoClassDefFoundError err)
- {
- // Ignore
- }
- catch (Exception ex)
- {
- // Ignore
- }
-
- String exporter = exports.iterator().next().getRevision().getBundle().toString();
-
- StringBuffer sb = new StringBuffer("*** Class '");
- sb.append(name);
- sb.append("' was not found because bundle ");
- sb.append(importer);
- sb.append(" does not import '");
- sb.append(pkgName);
- sb.append("' even though bundle ");
- sb.append(exporter);
- 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(importer);
- 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(importer);
- sb.append(".");
- }
- sb.append(" ***");
-
- return sb.toString();
- }
-
- // Next, try to see if the class is available from the system
- // class loader.
- try
- {
- m_secureAction.getClassLoader(BundleClassLoader.class).loadClass(name);
-
- StringBuffer sb = new StringBuffer("*** Package '");
- sb.append(pkgName);
- sb.append("' is not imported by bundle ");
- sb.append(importer);
- 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(importer);
- 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(importer);
- 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();
- }
}
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
new file mode 100644
index 0000000..db4c8cd
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -0,0 +1,2235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.security.SecureClassLoader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+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.SortedMap;
+import java.util.TreeMap;
+import org.apache.felix.framework.Felix.StatefulResolver;
+import org.apache.felix.framework.cache.JarContent;
+import org.apache.felix.framework.resolver.Content;
+import org.apache.felix.framework.resolver.HostedCapability;
+import org.apache.felix.framework.resolver.HostedRequirement;
+import org.apache.felix.framework.resolver.ResolveException;
+import org.apache.felix.framework.resolver.ResolverWire;
+import org.apache.felix.framework.resolver.ResourceNotFoundException;
+import org.apache.felix.framework.util.CompoundEnumeration;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.SecureAction;
+import org.apache.felix.framework.util.SecurityManagerEx;
+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.wiring.BundleCapabilityImpl;
+import org.apache.felix.framework.wiring.BundleRequirementImpl;
+import org.apache.felix.framework.wiring.BundleWireImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+
+public class BundleWiringImpl implements BundleWiring
+{
+ public final static int EAGER_ACTIVATION = 0;
+ public final static int LAZY_ACTIVATION = 1;
+
+ private final Logger m_logger;
+ private final Map m_configMap;
+ private final StatefulResolver m_resolver;
+ private final BundleRevisionImpl m_revision;
+ private final List<BundleRevision> m_fragments;
+ private final List<BundleWire> m_wires;
+ private final Map<String, BundleRevision> m_importedPkgs;
+ private final Map<String, List<BundleRevision>> m_requiredPkgs;
+ private final List<BundleCapability> m_resolvedCaps;
+ private final List<BundleRequirement> m_resolvedReqs;
+ private final List<BundleRequirement> m_resolvedDynamicReqs;
+ private final List<R4Library> m_resolvedNativeLibs;
+ private final Content[] m_contentPath;
+ private final Content[] m_fragmentContents;
+
+ private BundleClassLoader m_classLoader;
+ private boolean m_isActivationTriggered = false;
+ private ProtectionDomain m_protectionDomain = null;
+ private final static SecureAction m_secureAction = new SecureAction();
+
+ // Bundle-specific class loader for boot delegation.
+ private final ClassLoader m_bootClassLoader;
+ // Default class loader for boot delegation.
+ private final static ClassLoader m_defBootClassLoader;
+
+ // Statically define the default class loader for boot delegation.
+ static
+ {
+ ClassLoader cl = null;
+ try
+ {
+ Constructor ctor = m_secureAction.getDeclaredConstructor(
+ SecureClassLoader.class, new Class[] { ClassLoader.class });
+ m_secureAction.setAccesssible(ctor);
+ cl = (ClassLoader) m_secureAction.invoke(ctor, new Object[] { null });
+ }
+ catch (Throwable ex)
+ {
+ // On Android we get an exception if we set the parent class loader
+ // to null, so we will work around that case by setting the parent
+ // class loader to the system class loader in getClassLoader() below.
+ cl = null;
+ System.err.println("Problem creating boot delegation class loader: " + ex);
+ }
+ m_defBootClassLoader = cl;
+ }
+
+ // Boolean flag to enable/disable implicit boot delegation.
+ private final boolean m_implicitBootDelegation;
+ // Boolean flag to enable/disable local URLs.
+ private final boolean m_useLocalURLs;
+
+ // Re-usable security manager for accessing class context.
+ private static SecurityManagerEx m_sm = new SecurityManagerEx();
+
+ // Thread local to detect class loading cycles.
+ private final ThreadLocal m_cycleCheck = new ThreadLocal();
+
+ // Thread local to keep track of deferred activation.
+ private static final ThreadLocal m_deferredActivation = new ThreadLocal();
+
+ // Flag indicating whether we are on an old JVM or not.
+ private volatile static boolean m_isPreJava5 = false;
+
+ BundleWiringImpl(
+ Logger logger, Map configMap, StatefulResolver resolver,
+ BundleRevisionImpl revision, List<BundleRevision> fragments,
+ List<ResolverWire> resolverWires,
+ Map<ResolverWire, Set<String>> requiredPkgWires)
+ throws Exception
+ {
+ m_logger = logger;
+ m_configMap = configMap;
+ m_resolver = resolver;
+ m_revision = revision;
+
+ List<BundleWire> wires = new ArrayList<BundleWire>(resolverWires.size());
+ Map<String, BundleRevision> importedPkgs =
+ new HashMap<String, BundleRevision>();
+ Map<String, List<BundleRevision>> requiredPkgs =
+ new HashMap<String, List<BundleRevision>>();
+
+ for (ResolverWire rw : resolverWires)
+ {
+ wires.add(
+ new BundleWireImpl(
+ rw.getRequirer(),
+ rw.getRequirement(),
+ rw.getProvider(),
+ rw.getCapability()));
+
+ if (Util.isFragment(m_revision))
+ {
+ m_logger.log(
+ Logger.LOG_DEBUG,
+ "FRAGMENT WIRE: "
+ + this + " -> hosted by -> " + rw.getProvider());
+ }
+ else
+ {
+ m_logger.log(Logger.LOG_DEBUG, "WIRE: " + rw);
+
+ if (rw.getCapability().getNamespace()
+ .equals(BundleCapabilityImpl.PACKAGE_NAMESPACE))
+ {
+ ((BundleRevisionImpl) rw.getProvider()).addDependentImporter(m_revision);
+
+ importedPkgs.put(
+ (String) rw.getCapability().getAttributes()
+ .get(BundleCapabilityImpl.PACKAGE_ATTR),
+ rw.getProvider());
+ }
+ else if (rw.getCapability().getNamespace()
+ .equals(BundleCapabilityImpl.BUNDLE_NAMESPACE))
+ {
+ ((BundleRevisionImpl) rw.getProvider()).addDependentRequirer(m_revision);
+
+ for (String pkgName : requiredPkgWires.get(rw))
+ {
+ List<BundleRevision> revs = requiredPkgs.get(pkgName);
+ if (revs != null)
+ {
+ revs.add(rw.getProvider());
+ }
+ else
+ {
+ revs = new ArrayList<BundleRevision>();
+ revs.add(rw.getProvider());
+ requiredPkgs.put(pkgName, revs);
+ }
+ }
+ }
+ }
+ }
+ m_wires = wires;
+ m_requiredPkgs = requiredPkgs;
+ m_importedPkgs = importedPkgs;
+
+ // We need to sort the fragments and add ourself as a dependent of each one.
+ // We also need to create an array of fragment contents to attach to our
+ // content path.
+ Content[] fragmentContents = null;
+ if (fragments != null)
+ {
+ // Sort fragments according to ID order, if necessary.
+ // Note that this sort order isn't 100% correct since
+ // it uses a string, but it is likely close enough and
+ // avoids having to create more objects.
+ if (fragments.size() > 1)
+ {
+ SortedMap<String, BundleRevision> sorted = new TreeMap<String, BundleRevision>();
+ for (BundleRevision f : fragments)
+ {
+ sorted.put(((BundleRevisionImpl) f).getId(), f);
+ }
+ fragments = new ArrayList(sorted.values());
+ }
+ fragmentContents = new Content[fragments.size()];
+ for (int i = 0; (fragments != null) && (i < fragments.size()); i++)
+ {
+ fragmentContents[i] =
+ ((BundleRevisionImpl) fragments.get(i)).getContent()
+ .getEntryAsContent(FelixConstants.CLASS_PATH_DOT);
+ }
+ }
+ m_fragments = fragments;
+ m_fragmentContents = fragmentContents;
+
+ // Recalculate the content path for the new fragments.
+ m_contentPath = initializeContentPath();
+
+ List<BundleCapability> capList = (m_revision.getDeclaredCapabilities(null) == null)
+ ? new ArrayList<BundleCapability>()
+ : new ArrayList<BundleCapability>(m_revision.getDeclaredCapabilities(null));
+ for (int fragIdx = 0;
+ (m_fragments != null) && (fragIdx < m_fragments.size());
+ fragIdx++)
+ {
+ List<BundleCapability> caps =
+ m_fragments.get(fragIdx).getDeclaredCapabilities(null);
+ for (int capIdx = 0;
+ (caps != null) && (capIdx < caps.size());
+ capIdx++)
+ {
+ if (caps.get(capIdx).getNamespace().equals(
+ BundleCapabilityImpl.PACKAGE_NAMESPACE))
+ {
+ capList.add(
+ new HostedCapability(
+ m_revision, (BundleCapabilityImpl) caps.get(capIdx)));
+ }
+ }
+ }
+ m_resolvedCaps = Collections.unmodifiableList(capList);
+
+ List<BundleRequirement> reqList = (m_revision.getDeclaredRequirements(null) == null)
+ ? new ArrayList() : new ArrayList(m_revision.getDeclaredRequirements(null));
+ for (int fragIdx = 0;
+ (m_fragments != null) && (fragIdx < m_fragments.size());
+ fragIdx++)
+ {
+ List<BundleRequirement> reqs =
+ m_fragments.get(fragIdx).getDeclaredRequirements(null);
+ for (int reqIdx = 0;
+ (reqs != null) && (reqIdx < reqs.size());
+ reqIdx++)
+ {
+ if (reqs.get(reqIdx).getNamespace().equals(
+ BundleCapabilityImpl.PACKAGE_NAMESPACE)
+ || reqs.get(reqIdx).getNamespace().equals(
+ BundleCapabilityImpl.BUNDLE_NAMESPACE))
+ {
+ reqList.add(
+ new HostedRequirement(
+ m_revision, (BundleRequirementImpl) reqs.get(reqIdx)));
+ }
+ }
+ }
+ m_resolvedReqs = Collections.unmodifiableList(reqList);
+
+ List<BundleRequirement> dynReqList = (m_revision.getDeclaredDynamicRequirements() == null)
+ ? new ArrayList()
+ : new ArrayList(m_revision.getDeclaredDynamicRequirements());
+ for (int fragIdx = 0;
+ (m_fragments != null) && (fragIdx < m_fragments.size());
+ fragIdx++)
+ {
+ List<BundleRequirement> reqs =
+ ((BundleRevisionImpl) m_fragments.get(fragIdx))
+ .getDeclaredDynamicRequirements();
+ for (int reqIdx = 0;
+ (reqs != null) && (reqIdx < reqs.size());
+ reqIdx++)
+ {
+ if (reqs.get(reqIdx).getNamespace().equals(
+ BundleCapabilityImpl.PACKAGE_NAMESPACE))
+ {
+ dynReqList.add(reqs.get(reqIdx));
+ }
+ }
+ }
+ m_resolvedDynamicReqs = Collections.unmodifiableList(dynReqList);
+
+ List<R4Library> libList = (m_revision.getDeclaredNativeLibraries() == null)
+ ? new ArrayList<R4Library>()
+ : new ArrayList<R4Library>(m_revision.getDeclaredNativeLibraries());
+ for (int fragIdx = 0;
+ (m_fragments != null) && (fragIdx < m_fragments.size());
+ fragIdx++)
+ {
+ List<R4Library> libs =
+ ((BundleRevisionImpl) m_fragments.get(fragIdx))
+ .getDeclaredNativeLibraries();
+ for (int reqIdx = 0;
+ (libs != null) && (reqIdx < libs.size());
+ reqIdx++)
+ {
+ libList.add(libs.get(reqIdx));
+ }
+ }
+ // We need to return null here if we don't have any libraries, since a
+ // zero-length array is used to indicate that matching native libraries
+ // could not be found when resolving the bundle.
+ m_resolvedNativeLibs = (libList.isEmpty())
+ ? null
+ : Collections.unmodifiableList(libList);
+
+ ClassLoader bootLoader = m_defBootClassLoader;
+ if (revision.getBundle().getBundleId() != 0)
+ {
+ Object map = m_configMap.get(FelixConstants.BOOT_CLASSLOADERS_PROP);
+ if (map instanceof Map)
+ {
+ Object l = ((Map) map).get(m_revision.getBundle());
+ if (l instanceof ClassLoader)
+ {
+ bootLoader = (ClassLoader) l;
+ }
+ }
+ }
+ m_bootClassLoader = bootLoader;
+
+ m_implicitBootDelegation =
+ (m_configMap.get(FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP) == null)
+ || Boolean.valueOf(
+ (String) m_configMap.get(
+ FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP)).booleanValue();
+
+ m_useLocalURLs =
+ (m_configMap.get(FelixConstants.USE_LOCALURLS_PROP) == null)
+ ? false : true;
+ }
+
+ public void dispose()
+ {
+ if (!Util.isFragment(m_revision) && (m_wires != null))
+ {
+ for (BundleWire bw : m_wires)
+ {
+ if (bw.getProviderWiring() != null)
+ {
+ if (bw.getCapability().getNamespace()
+ .equals(BundleCapabilityImpl.PACKAGE_NAMESPACE))
+ {
+ ((BundleRevisionImpl) bw.getProviderWiring().getRevision())
+ .removeDependentImporter(m_revision);
+ }
+ else if (bw.getCapability().getNamespace()
+ .equals(BundleCapabilityImpl.BUNDLE_NAMESPACE))
+ {
+ ((BundleRevisionImpl) bw.getProviderWiring().getRevision())
+ .removeDependentRequirer(m_revision);
+ }
+ }
+ }
+ }
+
+ 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;
+ }
+
+ private Content[] initializeContentPath() throws Exception
+ {
+ List contentList = new ArrayList();
+ calculateContentPath(m_revision, m_revision.getContent(), contentList, true);
+ for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
+ {
+ calculateContentPath(m_fragments.get(i), m_fragmentContents[i], contentList, false);
+ }
+ return (Content[]) contentList.toArray(new Content[contentList.size()]);
+ }
+
+ private List calculateContentPath(
+ BundleRevision revision, Content 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) ((BundleRevisionImpl) revision)
+ .getHeaders().get(FelixConstants.BUNDLE_CLASSPATH);
+ // Parse the class path into strings.
+ List<String> classPathStrings = ManifestParser.parseDelimitedString(
+ classPath, FelixConstants.CLASS_PATH_SEPARATOR);
+
+ if (classPathStrings == null)
+ {
+ classPathStrings = new ArrayList<String>(0);
+ }
+
+ // Create the bundles class path.
+ for (int i = 0; i < classPathStrings.size(); i++)
+ {
+ // Remove any leading slash, since all bundle class path
+ // entries are relative to the root of the bundle.
+ classPathStrings.set(i, (classPathStrings.get(i).startsWith("/"))
+ ? classPathStrings.get(i).substring(1)
+ : classPathStrings.get(i));
+
+ // Check for the bundle itself on the class path.
+ if (classPathStrings.get(i).equals(FelixConstants.CLASS_PATH_DOT))
+ {
+ localContentList.add(content);
+ }
+ else
+ {
+ // Try to find the embedded class path entry in the current
+ // content.
+ Content embeddedContent = content.getEntryAsContent(classPathStrings.get(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.get(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(m_revision.getBundle(), Logger.LOG_INFO,
+ "Class path entry not found: "
+ + classPathStrings.get(i));
+ }
+ }
+ }
+
+ // If there is nothing on the class path, then include
+ // "." by default, as per the spec.
+ if (localContentList.isEmpty())
+ {
+ localContentList.add(content);
+ }
+
+ // Now add the local contents to the global content list and return it.
+ contentList.addAll(localContentList);
+ return contentList;
+ }
+
+// TODO: OSGi R4.3 - This really shouldn't be public, but it is needed by the
+// resolver to determine if a bundle can dynamically import.
+ public synchronized boolean hasPackageSource(String pkgName)
+ {
+ return (m_importedPkgs.containsKey(pkgName) || m_requiredPkgs.containsKey(pkgName));
+ }
+
+// TODO: OSGi R4.3 - This really shouldn't be public, but it is needed by the
+// to implement dynamic imports.
+ public synchronized BundleRevision getImportedPackageSource(String pkgName)
+ {
+ return m_importedPkgs.get(pkgName);
+ }
+
+ public List<BundleRevision> getFragments()
+ {
+ return m_fragments;
+ }
+
+ public boolean isCurrent()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public boolean isInUse()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public List<BundleCapability> getCapabilities(String namespace)
+ {
+ List<BundleCapability> result = m_resolvedCaps;
+ if (namespace != null)
+ {
+ result = new ArrayList<BundleCapability>();
+ for (BundleCapability cap : m_resolvedCaps)
+ {
+ if (cap.getNamespace().equals(namespace))
+ {
+ result.add(cap);
+ }
+ }
+ }
+ return result;
+ }
+
+ public List<BundleRequirement> getRequirements(String namespace)
+ {
+
+ List<BundleRequirement> result = m_resolvedReqs;
+ if (namespace != null)
+ {
+ result = new ArrayList<BundleRequirement>();
+ for (BundleRequirement req : m_resolvedReqs)
+ {
+ if (req.getNamespace().equals(namespace))
+ {
+ result.add(req);
+ }
+ }
+ }
+ return result;
+ }
+
+ public List<BundleRequirement> getDynamicRequirements()
+ {
+ return m_resolvedDynamicReqs;
+ }
+
+ public List<R4Library> getNativeLibraries()
+ {
+ return m_resolvedNativeLibs;
+ }
+
+ public List<BundleWire> getProvidedWires(String namespace)
+ {
+// TODO: OSGI R4.3 - IMPLEMENT THIS!!
+ return Collections.EMPTY_LIST;
+ }
+
+ public List<BundleWire> getRequiredWires(String namespace)
+ {
+ return m_wires;
+ }
+
+ public synchronized void addDynamicWire(ResolverWire rw)
+ {
+ // This not only sets the wires for the module, but it also records
+ // the dependencies this module has on other modules (i.e., the provider
+ // end of the wire) to simplify bookkeeping.
+
+ BundleWire wire = new BundleWireImpl(
+ rw.getRequirer(),
+ rw.getRequirement(),
+ rw.getProvider(),
+ rw.getCapability());
+ m_wires.add(wire);
+ m_importedPkgs.put(
+ (String) wire.getCapability().getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR),
+ rw.getProvider());
+ }
+
+ public BundleRevision getRevision()
+ {
+ return m_revision;
+ }
+
+ public synchronized ClassLoader getClassLoader()
+ {
+ if (m_classLoader == null)
+ {
+ // Determine which class loader to use based on which
+ // Java platform we are running on.
+ Class clazz;
+ if (m_isPreJava5)
+ {
+ clazz = BundleClassLoader.class;
+ }
+ else
+ {
+ try
+ {
+ clazz = BundleClassLoaderJava5.class;
+ }
+ catch (Throwable th)
+ {
+ // If we are on pre-Java5 then we will get a verify error
+ // here since we try to override a getResources() which is
+ // a final method in pre-Java5.
+ m_isPreJava5 = true;
+ clazz = BundleClassLoader.class;
+ }
+ }
+
+ // Use SecureAction to create the class loader if security is
+ // enabled; otherwise, create it directly.
+ try
+ {
+ Constructor ctor = (Constructor) m_secureAction.getConstructor(
+ clazz, new Class[] { BundleWiringImpl.class, ClassLoader.class });
+ m_classLoader = (BundleClassLoader)
+ m_secureAction.invoke(ctor,
+ new Object[] { this, determineParentClassLoader() });
+ }
+ catch (Exception ex)
+ {
+ throw new RuntimeException("Unable to create module class loader: "
+ + ex.getMessage() + " [" + ex.getClass().getName() + "]");
+ }
+ }
+ return m_classLoader;
+ }
+
+ public List<URL> findEntries(String path, String filePattern, int options)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Collection<String> listResources(String path, String filePattern, int options)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Bundle getBundle()
+ {
+ return m_revision.getBundle();
+ }
+
+ //
+ // Class loader implementation methods.
+ //
+
+ public URL getLocalURL(int index, String urlPath)
+ {
+ if (urlPath.startsWith("/"))
+ {
+ urlPath = urlPath.substring(1);
+ }
+ if (index == 0)
+ {
+ return m_revision.getContent().getEntryAsURL(urlPath);
+ }
+ return m_contentPath[index - 1].getEntryAsURL(urlPath);
+ }
+
+ private URL createURL(int port, String path)
+ {
+ // Add a slash if there is one already, otherwise
+ // the is no slash separating the host from the file
+ // in the resulting URL.
+ if (!path.startsWith("/"))
+ {
+ path = "/" + path;
+ }
+
+ try
+ {
+ return m_secureAction.createURL(null,
+ FelixConstants.BUNDLE_URL_PROTOCOL + "://" +
+ m_revision.getId() + ":" + port + path, m_revision.getURLStreamHandler());
+ }
+ catch (MalformedURLException ex)
+ {
+ m_logger.log(m_revision.getBundle(),
+ Logger.LOG_ERROR,
+ "Unable to create resource URL.",
+ ex);
+ }
+ return null;
+ }
+
+ 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 = createURL(1, name);
+ }
+ else if (name.startsWith("/"))
+ {
+ name = name.substring(1);
+ }
+
+ // Check the module class path.
+ for (int i = 0;
+ (url == null) &&
+ (i < m_contentPath.length); i++)
+ {
+ if (m_contentPath[i].hasEntry(name))
+ {
+ url = createURL(i + 1, name);
+ }
+ }
+
+ return url;
+ }
+
+ public Enumeration getResourcesByDelegation(String name)
+ {
+ Set requestSet = (Set) m_cycleCheck.get();
+ if (requestSet == null)
+ {
+ requestSet = new HashSet();
+ m_cycleCheck.set(requestSet);
+ }
+ if (!requestSet.contains(name))
+ {
+ requestSet.add(name);
+ try
+ {
+ return findResourcesByDelegation(name);
+ }
+ finally
+ {
+ requestSet.remove(name);
+ }
+ }
+
+ return null;
+ }
+
+ private Enumeration findResourcesByDelegation(String name)
+ {
+ Enumeration urls = null;
+ List completeUrlList = new ArrayList();
+
+ // First, try to resolve the originating module.
+ try
+ {
+ m_resolver.resolve(m_revision);
+ }
+ 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.
+ return getResourcesLocal(name);
+ }
+
+ // Get the package of the target class/resource.
+ String pkgName = Util.getResourcePackage(name);
+
+ // Delegate any packages listed in the boot delegation
+ // property to the parent class loader.
+ if (shouldBootDelegate(pkgName))
+ {
+ try
+ {
+ // Get the appropriate class loader for delegation.
+ ClassLoader bdcl = getBootDelegationClassLoader();
+ urls = bdcl.getResources(name);
+ }
+ catch (IOException ex)
+ {
+ // This shouldn't happen and even if it does, there
+ // is nothing we can do, so just ignore it.
+ }
+ // If this is a java.* package, then always terminate the
+ // search; otherwise, continue to look locally.
+ if (pkgName.startsWith("java."))
+ {
+ return urls;
+ }
+
+ completeUrlList.add(urls);
+ }
+
+ // Look in the revisions's imported packages. If the package is
+ // imported, then we stop searching no matter the result since
+ // imported packages cannot be split.
+ BundleRevision provider = m_importedPkgs.get(pkgName);
+ if (provider != null)
+ {
+ // Delegate to the provider revision.
+ urls = ((BundleWiringImpl) provider.getWiring()).getResourcesByDelegation(name);
+
+ // If we find any resources, then add them.
+ if ((urls != null) && (urls.hasMoreElements()))
+ {
+ completeUrlList.add(urls);
+ }
+
+ // Always return here since imported packages cannot be split
+ // across required bundles or the revision's content.
+ return new CompoundEnumeration((Enumeration[])
+ completeUrlList.toArray(new Enumeration[completeUrlList.size()]));
+ }
+
+ // See whether we can get the resource from the required bundles and
+ // regardless of whether or not this is the case continue to the next
+ // step potentially passing on the result of this search (if any).
+ List<BundleRevision> providers = m_requiredPkgs.get(pkgName);
+ if (providers != null)
+ {
+ for (BundleRevision p : providers)
+ {
+ // Delegate to the provider revision.
+ urls = ((BundleWiringImpl) p.getWiring()).getResourcesByDelegation(name);
+
+ // If we find any resources, then add them.
+ if ((urls != null) && (urls.hasMoreElements()))
+ {
+ completeUrlList.add(urls);
+ }
+
+ // Do not return here, since required packages can be split
+ // across the revision's content.
+ }
+ }
+
+ // 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 = getResourcesLocal(name);
+ if ((urls != null) && (urls.hasMoreElements()))
+ {
+ completeUrlList.add(urls);
+ }
+ else
+ {
+ // If not found, then try the module's dynamic imports.
+ // At this point, the module's imports were searched and so was the
+ // the module's content. Now we make an attempt to load the
+ // class/resource via a dynamic import, if possible.
+ try
+ {
+ provider = m_resolver.resolve(m_revision, pkgName);
+ }
+ catch (ResolveException ex)
+ {
+ // Ignore this since it is likely normal.
+ }
+ if (provider != null)
+ {
+ // Delegate to the provider revision.
+ urls = ((BundleWiringImpl) provider.getWiring()).getResourcesByDelegation(name);
+
+ // If we find any resources, then add them.
+ if ((urls != null) && (urls.hasMoreElements()))
+ {
+ completeUrlList.add(urls);
+ }
+ }
+ }
+
+ return new CompoundEnumeration((Enumeration[])
+ completeUrlList.toArray(new Enumeration[completeUrlList.size()]));
+ }
+
+ private Enumeration getResourcesLocal(String name)
+ {
+ List l = new ArrayList();
+
+ // 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 < m_contentPath.length; i++)
+ {
+ l.add(createURL(i + 1, name));
+ }
+ }
+ else
+ {
+ // Remove leading slash, if present.
+ if (name.startsWith("/"))
+ {
+ name = name.substring(1);
+ }
+
+ // Check the module class path.
+ for (int i = 0; i < m_contentPath.length; i++)
+ {
+ if (m_contentPath[i].hasEntry(name))
+ {
+ // Use the class path index + 1 for creating the path so
+ // that we can differentiate between module content URLs
+ // (where the path will start with 0) and module class
+ // path URLs.
+ l.add(createURL(i + 1, name));
+ }
+ }
+ }
+
+ return Collections.enumeration(l);
+ }
+
+ private ClassLoader determineParentClassLoader()
+ {
+ // Determine the class loader's parent based on the
+ // configuration property; use boot class loader by
+ // default.
+ String cfg = (String) m_configMap.get(Constants.FRAMEWORK_BUNDLE_PARENT);
+ cfg = (cfg == null) ? Constants.FRAMEWORK_BUNDLE_PARENT_BOOT : cfg;
+ final ClassLoader parent;
+ if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_APP))
+ {
+ parent = m_secureAction.getSystemClassLoader();
+ }
+ else if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_EXT))
+ {
+ parent = m_secureAction.getParentClassLoader(m_secureAction.getSystemClassLoader());
+ }
+ else if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_FRAMEWORK))
+ {
+ parent = m_secureAction.getClassLoader(BundleRevisionImpl.class);
+ }
+ // On Android we cannot set the parent class loader to be null, so
+ // we special case that situation here and set it to the system
+ // class loader by default instead, which is not really spec.
+ else if (m_bootClassLoader == null)
+ {
+ parent = m_secureAction.getSystemClassLoader();
+ }
+ else
+ {
+ parent = null;
+ }
+ return parent;
+ }
+
+ boolean shouldBootDelegate(String pkgName)
+ {
+ // Always boot delegate if the bundle has a configured
+ // boot class loader.
+ if (m_bootClassLoader != m_defBootClassLoader)
+ {
+ return true;
+ }
+
+ boolean result = false;
+
+ // 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 (pkgName.length() > 0)
+ {
+ for (int i = 0; !result && (i < m_revision.getBootDelegationPackages().length); i++)
+ {
+ // Check if the boot package is wildcarded.
+ // A wildcarded boot package will be in the form "foo.",
+ // so a matching subpackage will start with "foo.", e.g.,
+ // "foo.bar".
+ if (m_revision.getBootDelegationPackageWildcards()[i]
+ && pkgName.startsWith(m_revision.getBootDelegationPackages()[i]))
+ {
+ return true;
+ }
+ // If not wildcarded, then check for an exact match.
+ else if (m_revision.getBootDelegationPackages()[i].equals(pkgName))
+ {
+ return true;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ ClassLoader getBootDelegationClassLoader()
+ {
+ // Get the appropriate class loader for delegation.
+ ClassLoader parent = (m_classLoader == null)
+ ? determineParentClassLoader() : m_classLoader.getParent();
+ return (parent == null) ? m_bootClassLoader : parent;
+ }
+
+ private static final Constructor m_dexFileClassConstructor;
+ private static final Method m_dexFileClassLoadDex;
+ private static final Method m_dexFileClassLoadClass;
+
+ static
+ {
+ Constructor dexFileClassConstructor = null;
+ Method dexFileClassLoadDex = null;
+ Method dexFileClassLoadClass = null;
+ try
+ {
+ Class dexFileClass;
+ try
+ {
+ dexFileClass = Class.forName("dalvik.system.DexFile");
+ }
+ catch (Exception ex)
+ {
+ dexFileClass = Class.forName("android.dalvik.DexFile");
+ }
+
+ try
+ {
+ dexFileClassLoadDex = dexFileClass.getMethod("loadDex",
+ new Class[]{String.class, String.class, Integer.TYPE});
+ }
+ catch (Exception ex)
+ {
+ // Nothing we need to do
+ }
+ dexFileClassConstructor = dexFileClass.getConstructor(
+ new Class[] { java.io.File.class });
+ dexFileClassLoadClass = dexFileClass.getMethod("loadClass",
+ new Class[] { String.class, ClassLoader.class });
+ }
+ catch (Throwable ex)
+ {
+ dexFileClassConstructor = null;
+ dexFileClassLoadDex = null;
+ dexFileClassLoadClass = null;
+ }
+ m_dexFileClassConstructor = dexFileClassConstructor;
+ m_dexFileClassLoadDex= dexFileClassLoadDex;
+ m_dexFileClassLoadClass = dexFileClassLoadClass;
+ }
+
+ public Class getClassByDelegation(String name) throws ClassNotFoundException
+ {
+ // We do not call getClassLoader().loadClass() for arrays because
+ // it does not correctly handle array types, which is necessary in
+ // cases like deserialization using a wrapper class loader.
+ if ((name != null) && (name.length() > 0) && (name.charAt(0) == '['))
+ {
+ return Class.forName(name, false, getClassLoader());
+ }
+ return getClassLoader().loadClass(name);
+ }
+
+ public URL getResourceByDelegation(String name)
+ {
+ try
+ {
+ return (URL) findClassOrResourceByDelegation(name, false);
+ }
+ catch (ClassNotFoundException ex)
+ {
+ // This should never be thrown because we are loading resources.
+ }
+ catch (ResourceNotFoundException ex)
+ {
+ m_logger.log(m_revision.getBundle(),
+ Logger.LOG_DEBUG,
+ ex.getMessage());
+ }
+ return null;
+ }
+
+ private Object findClassOrResourceByDelegation(String name, boolean isClass)
+ throws ClassNotFoundException, ResourceNotFoundException
+ {
+ Object result = null;
+
+ Set requestSet = (Set) m_cycleCheck.get();
+ if (requestSet == null)
+ {
+ requestSet = new HashSet();
+ m_cycleCheck.set(requestSet);
+ }
+ if (requestSet.add(name))
+ {
+ try
+ {
+ // First, try to resolve the originating revision.
+ m_resolver.resolve(m_revision);
+
+ // 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.
+ if (shouldBootDelegate(pkgName))
+ {
+ try
+ {
+ // Get the appropriate class loader for delegation.
+ ClassLoader bdcl = getBootDelegationClassLoader();
+ result = (isClass)
+ ? (Object) bdcl.loadClass(name)
+ : (Object) bdcl.getResource(name);
+ // If this is a java.* package, then always terminate the
+ // search; otherwise, continue to look locally if not found.
+ if (pkgName.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 (pkgName.startsWith("java."))
+ {
+ throw ex;
+ }
+ }
+ }
+
+ // Look in the revision'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(pkgName, name, isClass);
+
+ // If not found, try the revision's own class path.
+ if (result == null)
+ {
+ result = (isClass)
+ ? (Object) ((BundleClassLoader) getClassLoader()).findClass(name)
+ : (Object) getResourceLocal(name);
+
+ // If still not found, then try the revision's dynamic imports.
+ if (result == null)
+ {
+ result = searchDynamicImports(pkgName, name, isClass);
+ }
+ }
+ }
+ 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 + " not found because "
+ + getBundle()
+ + " cannot resolve: "
+ + 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 + " not found because "
+ + getBundle()
+ + " cannot resolve: "
+ + ex.getRequirement());
+ }
+ }
+ finally
+ {
+ requestSet.remove(name);
+ }
+ }
+ else
+ {
+ // If a cycle is detected, we should return null to break the
+ // cycle. This should only ever be return to internal class
+ // loading code and not to the actual instigator of the class load.
+ return null;
+ }
+
+ if (result == null)
+ {
+ if (isClass)
+ {
+ throw new ClassNotFoundException(
+ name + " not found by " + this.getBundle());
+ }
+ else
+ {
+ throw new ResourceNotFoundException(
+ name + " not found by " + this.getBundle());
+ }
+ }
+
+ return result;
+ }
+
+ private Object searchImports(String pkgName, String name, boolean isClass)
+ throws ClassNotFoundException, ResourceNotFoundException
+ {
+ // Check if the package is imported.
+ BundleRevision provider = m_importedPkgs.get(pkgName);
+ if (provider != null)
+ {
+ // If we find the class or resource, then return it.
+ Object result = (isClass)
+ ? (Object) ((BundleWiringImpl) provider.getWiring()).getClassByDelegation(name)
+ : (Object) ((BundleWiringImpl) provider.getWiring()).getResourceByDelegation(name);
+ if (result != null)
+ {
+ return result;
+ }
+
+ // If no class was found, then we must throw an exception
+ // since the provider of this package did not contain the
+ // requested class and imported packages are atomic.
+ throw new ClassNotFoundException(name);
+ }
+
+ // Check if the package is required.
+ List<BundleRevision> providers = m_requiredPkgs.get(pkgName);
+ if (providers != null)
+ {
+ for (BundleRevision p : providers)
+ {
+ // If we find the class or resource, then return it.
+ try
+ {
+ Object result = (isClass)
+ ? (Object) ((BundleWiringImpl) p.getWiring()).getClassByDelegation(name)
+ : (Object) ((BundleWiringImpl) p.getWiring()).getResourceByDelegation(name);
+ if (result != null)
+ {
+ return result;
+ }
+ }
+ catch (ClassNotFoundException ex)
+ {
+ // Since required packages can be split, don't throw an
+ // exception here if it is not found. Instead, we'll just
+ // continue searching other required bundles and the
+ // revision's local content.
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private Object searchDynamicImports(
+ final String pkgName, final String name, final boolean isClass)
+ throws ClassNotFoundException, ResourceNotFoundException
+ {
+ // At this point, the module's imports were searched and so was the
+ // the module's content. Now we make an attempt to load the
+ // class/resource via a dynamic import, if possible.
+ BundleRevision provider = null;
+ try
+ {
+ provider = m_resolver.resolve(m_revision, pkgName);
+ }
+ catch (ResolveException ex)
+ {
+ // Ignore this since it is likely normal.
+ }
+
+ // If the dynamic import was successful, then this initial
+ // time we must directly return the result from dynamically
+ // created package sources, but subsequent requests for
+ // classes/resources in the associated package will be
+ // processed as part of normal static imports.
+ if (provider != null)
+ {
+ // Return the class or resource.
+ return (isClass)
+ ? (Object) ((BundleWiringImpl) provider.getWiring()).getClassByDelegation(name)
+ : (Object) ((BundleWiringImpl) provider.getWiring()).getResourceByDelegation(name);
+ }
+
+ // If implicit boot delegation is enabled, then try to guess whether
+ // we should boot delegate.
+ if (m_implicitBootDelegation)
+ {
+ // At this point, the class/resource could not be found by the bundle's
+ // static or dynamic imports, nor its own content. Before we throw
+ // an exception, we will try to determine if the instigator of the
+ // class/resource load was a class from a bundle or not. This is necessary
+ // because the specification mandates that classes on the class path
+ // should be hidden (except for java.*), but it does allow for these
+ // classes/resources to be exposed by the system bundle as an export.
+ // However, in some situations classes on the class path make the faulty
+ // assumption that they can access everything on the class path from
+ // every other class loader that they come in contact with. This is
+ // not true if the class loader in question is from a bundle. Thus,
+ // this code tries to detect that situation. If the class instigating
+ // the load request was NOT from a bundle, then we will make the
+ // assumption that the caller actually wanted to use the parent class
+ // loader and we will delegate to it. If the class was
+ // from a bundle, then we will enforce strict class loading rules
+ // for the bundle and throw an exception.
+
+ // Get the class context to see the classes on the stack.
+ final Class[] classes = m_sm.getClassContext();
+ try
+ {
+ if (System.getSecurityManager() != null)
+ {
+ return AccessController
+ .doPrivileged(new PrivilegedExceptionAction()
+ {
+ public Object run() throws Exception
+ {
+ return doImplicitBootDelegation(classes, name,
+ isClass);
+ }
+ });
+ }
+ else
+ {
+ return doImplicitBootDelegation(classes, name, isClass);
+ }
+ }
+ catch (PrivilegedActionException ex)
+ {
+ Exception cause = ex.getException();
+ if (cause instanceof ClassNotFoundException)
+ {
+ throw (ClassNotFoundException) cause;
+ }
+ else
+ {
+ throw (ResourceNotFoundException) cause;
+ }
+ }
+ }
+ return null;
+ }
+
+ private Object doImplicitBootDelegation(Class[] classes, String name, boolean isClass)
+ throws ClassNotFoundException, ResourceNotFoundException
+ {
+ // Start from 1 to skip security manager class.
+ for (int i = 1; i < classes.length; i++)
+ {
+ // Find the first class on the call stack that is not from
+ // the class loader that loaded the Felix classes or is not
+ // a class loader or class itself, because we want to ignore
+ // calls to ClassLoader.loadClass() and Class.forName() since
+ // we are trying to find out who instigated the class load.
+ // Also ignore inner classes of class loaders, since we can
+ // assume they are a class loader too.
+
+ // TODO: FRAMEWORK - This check is a hack and we should see if we can think
+ // of another way to do it, since it won't necessarily work in all situations.
+ // Since Felix uses threads for changing the start level
+ // and refreshing packages, it is possible that there are no
+ // bundle classes on the call stack; therefore, as soon as we
+ // see Thread on the call stack we exit this loop. Other cases
+ // where bundles actually use threads are not an issue because
+ // the bundle classes will be on the call stack before the
+ // Thread class.
+ if (Thread.class.equals(classes[i]))
+ {
+ break;
+ }
+ // Break if the current class came from a bundle, since we should
+ // not implicitly boot delegate in that case.
+ else if (isClassLoadedFromBundleRevision(classes[i]))
+ {
+ break;
+ }
+ // Break if this goes through BundleImpl because it must be a call
+ // to Bundle.loadClass() which should not implicitly boot delegate.
+ else if (BundleImpl.class.equals(classes[i]))
+ {
+ break;
+ }
+ else if (isClassExternal(classes[i]))
+ {
+ try
+ {
+ // Return the class or resource from the parent class loader.
+ return (isClass)
+ ? (Object) m_secureAction.getClassLoader(this.getClass()).loadClass(name)
+ : (Object) m_secureAction.getClassLoader(this.getClass()).getResource(name);
+ }
+ catch (NoClassDefFoundError ex)
+ {
+ // Ignore, will return null
+ }
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ private boolean isClassLoadedFromBundleRevision(Class clazz)
+ {
+ // The target class is loaded by a bundle class loader,
+ // then return true.
+ if (BundleClassLoader.class.isInstance(m_secureAction.getClassLoader(clazz)))
+ {
+ return true;
+ }
+
+ // If the target class was loaded from a class loader that
+ // came from a bundle, then return true.
+ ClassLoader last = null;
+ for (ClassLoader cl = m_secureAction.getClassLoader(clazz);
+ (cl != null) && (last != cl);
+ cl = m_secureAction.getClassLoader(cl.getClass()))
+ {
+ last = cl;
+ if (BundleClassLoader.class.isInstance(cl))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Tries to determine whether the given class is part of the framework or not.
+ * Framework classes include everything in org.apache.felix.framework.* and
+ * org.osgi.framework.*. We also consider ClassLoader and Class to be internal
+ * classes, because they are inserted into the stack trace as a result of
+ * method overloading. Typically, ClassLoader or Class will be mixed in
+ * between framework classes or will be at the point where the class loading
+ * request enters the framework class loading mechanism, which will then be
+ * followed by either bundle or external code, which will then exit our
+ * attempt to determine if we should boot delegate or not. Other standard
+ * class loaders, like URLClassLoader, are considered external classes and
+ * should trigger boot delegation. This means that bundles can create standard
+ * class loaders to get access to boot packages, but this is the standard
+ * behavior of class loaders.
+ * @param clazz the class to determine if it is external or not.
+ * @return <tt>true</tt> if the class is external, otherwise <tt>false</tt>.
+ */
+ private boolean isClassExternal(Class clazz)
+ {
+ if (clazz.getName().startsWith("org.apache.felix.framework."))
+ {
+ return false;
+ }
+ else if (clazz.getName().startsWith("org.osgi.framework."))
+ {
+ return false;
+ }
+ else if (ClassLoader.class.equals(clazz))
+ {
+ return false;
+ }
+ else if (Class.class.equals(clazz))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ synchronized boolean isActivationTriggered()
+ {
+ return m_isActivationTriggered;
+ }
+
+ boolean isActivationTrigger(String pkgName)
+ {
+ List<String> activationIncludes = m_revision.getActivationIncludes();
+ List<String> activationExcludes = m_revision.getActivationExcludes();
+
+ if ((activationIncludes == null) && (activationExcludes == null))
+ {
+ return true;
+ }
+
+ // If there are no include filters then all classes are included
+ // by default, otherwise try to find one match.
+ boolean included = (activationIncludes == null);
+ for (int i = 0;
+ (!included) && (activationIncludes != null) && (i < activationIncludes.size());
+ i++)
+ {
+ included = activationIncludes.get(i).equals(pkgName);
+ }
+
+ // If there are no exclude filters then no classes are excluded
+ // by default, otherwise try to find one match.
+ boolean excluded = false;
+ for (int i = 0;
+ (!excluded) && (activationExcludes != null) && (i < activationExcludes.size());
+ i++)
+ {
+ excluded = activationExcludes.get(i).equals(pkgName);
+ }
+ return included && !excluded;
+ }
+
+ static class ToLocalUrlEnumeration implements Enumeration
+ {
+ final Enumeration m_enumeration;
+
+ ToLocalUrlEnumeration(Enumeration enumeration)
+ {
+ m_enumeration = enumeration;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return m_enumeration.hasMoreElements();
+ }
+
+ public Object nextElement()
+ {
+ return convertToLocalUrl((URL) m_enumeration.nextElement());
+ }
+ }
+
+ public class BundleClassLoaderJava5 extends BundleClassLoader
+ {
+ public BundleClassLoaderJava5(ClassLoader parent)
+ {
+ super(parent);
+ }
+
+ @Override
+ public Enumeration getResources(String name)
+ {
+ Enumeration urls = BundleWiringImpl.this.getResourcesByDelegation(name);
+ if (m_useLocalURLs)
+ {
+ urls = new ToLocalUrlEnumeration(urls);
+ }
+ return urls;
+ }
+
+ @Override
+ protected Enumeration findResources(String name)
+ {
+ return BundleWiringImpl.this.getResourcesLocal(name);
+ }
+ }
+
+ public class BundleClassLoader extends SecureClassLoader implements BundleReference
+ {
+ private final Map m_jarContentToDexFile;
+ private Object[][] m_cachedLibs = new Object[0][];
+ private static final int LIBNAME_IDX = 0;
+ private static final int LIBPATH_IDX = 1;
+
+ public BundleClassLoader(ClassLoader parent)
+ {
+ super(parent);
+ if (m_dexFileClassLoadClass != null)
+ {
+ m_jarContentToDexFile = new HashMap();
+ }
+ else
+ {
+ m_jarContentToDexFile = null;
+ }
+ }
+
+ public Bundle getBundle()
+ {
+ return BundleWiringImpl.this.getBundle();
+ }
+
+ @Override
+ 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
+ {
+ clazz = (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, m_revision, name);
+ ex = (msg != null)
+ ? new ClassNotFoundException(msg, cnfe)
+ : ex;
+ }
+ throw ex;
+ }
+ }
+
+ // Resolve the class and return it.
+ if (resolve)
+ {
+ resolveClass(clazz);
+ }
+ return clazz;
+ }
+
+ @Override
+ protected Class findClass(String name) throws ClassNotFoundException
+ {
+ Class clazz = null;
+
+ // Search for class in bundle revision.
+ if (clazz == null)
+ {
+ String actual = name.replace('.', '/') + ".class";
+
+ byte[] bytes = null;
+
+ // Check the bundle class path.
+ Content content = null;
+ for (int i = 0;
+ (bytes == null) &&
+ (i < m_contentPath.length); i++)
+ {
+ bytes = m_contentPath[i].getEntryAsBytes(actual);
+ content = m_contentPath[i];
+ }
+
+ if (bytes != null)
+ {
+ // Get package name.
+ String pkgName = Util.getClassPackage(name);
+
+ // 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)
+ {
+ int activationPolicy =
+ ((BundleImpl) getBundle()).isDeclaredActivationPolicyUsed()
+ ? ((BundleRevisionImpl) ((BundleImpl) getBundle())
+ .getCurrentRevision()).getDeclaredActivationPolicy()
+ : EAGER_ACTIVATION;
+
+ // If the revision is using deferred activation, then if
+ // we load this class from this revision we need to activate
+ // the bundle before returning the class. We will short
+ // circuit the trigger matching if the trigger is already
+ // tripped.
+ boolean isTriggerClass = m_isActivationTriggered
+ ? false : isActivationTrigger(pkgName);
+ if (!m_isActivationTriggered
+ && isTriggerClass
+ && (activationPolicy == BundleRevisionImpl.LAZY_ACTIVATION)
+ && (getBundle().getState() == Bundle.STARTING))
+ {
+ List deferredList = (List) m_deferredActivation.get();
+ if (deferredList == null)
+ {
+ deferredList = new ArrayList();
+ m_deferredActivation.set(deferredList);
+ }
+ deferredList.add(new Object[] { name, getBundle() });
+ }
+ // We need to try to define a Package object for the class
+ // before we call defineClass() if we haven't already
+ // created it.
+ 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);
+ }
+ }
+
+ // At this point if we have a trigger class, then the deferred
+ // activation trigger has tripped.
+ if (!m_isActivationTriggered && isTriggerClass && (clazz != null))
+ {
+// TODO: OSGi R4.3 - This isn't protected by the correct lock.
+ m_isActivationTriggered = true;
+ }
+ }
+ }
+
+ // Perform deferred activation without holding the class loader lock,
+ // if the class we are returning is the instigating class.
+ List deferredList = (List) m_deferredActivation.get();
+ if ((deferredList != null)
+ && (deferredList.size() > 0)
+ && ((Object[]) deferredList.get(0))[0].equals(name))
+ {
+ for (int i = deferredList.size() - 1; i >= 0; i--)
+ {
+ try
+ {
+ ((BundleImpl) ((Object[]) deferredList.get(i))[1]).getFramework().activateBundle(
+ (BundleImpl) ((Object[]) deferredList.get(i))[1], true);
+ }
+ catch (BundleException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+ deferredList.clear();
+ }
+ }
+ }
+
+ return clazz;
+ }
+
+ private Object[] definePackage(String pkgName)
+ {
+ String spectitle = (String) m_revision.getHeaders().get("Specification-Title");
+ String specversion = (String) m_revision.getHeaders().get("Specification-Version");
+ String specvendor = (String) m_revision.getHeaders().get("Specification-Vendor");
+ String impltitle = (String) m_revision.getHeaders().get("Implementation-Title");
+ String implversion = (String) m_revision.getHeaders().get("Implementation-Version");
+ String implvendor = (String) m_revision.getHeaders().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
+ {
+ if (m_dexFileClassLoadDex != null)
+ {
+ dexFile = m_dexFileClassLoadDex.invoke(null,
+ new Object[]{content.getFile().getAbsolutePath(),
+ content.getFile().getAbsolutePath() + ".dex", new Integer(0)});
+ }
+ else
+ {
+ 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;
+ }
+
+ @Override
+ public URL getResource(String name)
+ {
+ URL url = BundleWiringImpl.this.getResourceByDelegation(name);
+ if (m_useLocalURLs)
+ {
+ url = convertToLocalUrl(url);
+ }
+ return url;
+ }
+
+ @Override
+ protected URL findResource(String name)
+ {
+ return BundleWiringImpl.this.getResourceLocal(name);
+ }
+
+ // The findResources() method should only look at the revision 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.
+ @Override
+ protected Enumeration findResources(String name)
+ {
+ Enumeration urls = BundleWiringImpl.this.getResourcesByDelegation(name);
+ if (m_useLocalURLs)
+ {
+ urls = new ToLocalUrlEnumeration(urls);
+ }
+ return urls;
+ }
+
+ @Override
+ protected String findLibrary(String name)
+ {
+ // Remove leading slash, if present.
+ if (name.startsWith("/"))
+ {
+ name = name.substring(1);
+ }
+
+ String result = null;
+ // CONCURRENCY: In the long run, we might want to break this
+ // sync block in two to avoid manipulating the cache while
+ // holding the lock, but for now we will do it the simple way.
+ synchronized (this)
+ {
+ // Check to make sure we haven't already found this library.
+ for (int i = 0; (result == null) && (i < m_cachedLibs.length); i++)
+ {
+ if (m_cachedLibs[i][LIBNAME_IDX].equals(name))
+ {
+ result = (String) m_cachedLibs[i][LIBPATH_IDX];
+ }
+ }
+
+ // If we don't have a cached result, see if we have a matching
+ // native library.
+ if (result == null)
+ {
+ List<R4Library> libs = getNativeLibraries();
+ for (int libIdx = 0; (libs != null) && (libIdx < libs.size()); libIdx++)
+ {
+ if (libs.get(libIdx).match(m_configMap, name))
+ {
+ // Search bundle content first for native library.
+ result = m_revision.getContent().getEntryAsNativeLibrary(
+ libs.get(libIdx).getEntryName());
+ // If not found, then search fragments in order.
+ for (int i = 0;
+ (result == null) && (m_fragmentContents != null)
+ && (i < m_fragmentContents.length);
+ i++)
+ {
+ result = m_fragmentContents[i].getEntryAsNativeLibrary(
+ libs.get(libIdx).getEntryName());
+ }
+ }
+ }
+
+ // Remember the result for future requests.
+ if (result != null)
+ {
+ Object[][] tmp = new Object[m_cachedLibs.length + 1][];
+ System.arraycopy(m_cachedLibs, 0, tmp, 0, m_cachedLibs.length);
+ tmp[m_cachedLibs.length] = new Object[] { name, result };
+ m_cachedLibs = tmp;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return BundleWiringImpl.this.toString();
+ }
+ }
+
+ static URL convertToLocalUrl(URL url)
+ {
+ if (url.getProtocol().equals("bundle"))
+ {
+ try
+ {
+ url = ((URLHandlersBundleURLConnection)
+ url.openConnection()).getLocalURL();
+ }
+ catch (IOException ex)
+ {
+ // Ignore and add original url.
+ }
+ }
+ return url;
+ }
+
+ private static String diagnoseClassLoadError(
+ StatefulResolver resolver, BundleRevision revision, 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);
+ if (pkgName.length() == 0)
+ {
+ return null;
+ }
+
+ // First, get the bundle string of the revision doing the class loader.
+ String importer = revision.getBundle().toString();
+
+ // Next, check to see if the revision imports the package.
+ List<BundleWire> wires = (revision.getWiring() == null)
+ ? null : revision.getWiring().getProvidedWires(null);
+ for (int i = 0; (wires != null) && (i < wires.size()); i++)
+ {
+ if (wires.get(i).getCapability().getNamespace().equals(BundleCapabilityImpl.PACKAGE_NAMESPACE) &&
+ wires.get(i).getCapability().getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
+ {
+ String exporter = wires.get(i).getProviderWiring().getBundle().toString();
+
+ StringBuffer sb = new StringBuffer("*** Package '");
+ sb.append(pkgName);
+ sb.append("' is imported by bundle ");
+ sb.append(importer);
+ sb.append(" from bundle ");
+ sb.append(exporter);
+ sb.append(", but the exported package from bundle ");
+ sb.append(exporter);
+ 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(importer);
+ sb.append(" and/or that the exported package is correctly bundled in ");
+ sb.append(exporter);
+ sb.append(". ***");
+
+ return sb.toString();
+ }
+ }
+
+ // Next, check to see if the package was optionally imported and
+ // whether or not there is an exporter available.
+ List<BundleRequirement> reqs = revision.getWiring().getRequirements(null);
+/*
+* 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 revision.
+ if (resolver.isAllowedDynamicImport(revision, pkgName))
+ {
+ // Try to see if there is an exporter available.
+ Map<String, String> dirs = Collections.EMPTY_MAP;
+ Map<String, Object> attrs = new HashMap<String, Object>(1);
+ attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+ BundleRequirementImpl req = new BundleRequirementImpl(
+ revision, BundleCapabilityImpl.PACKAGE_NAMESPACE, dirs, attrs);
+ Set<BundleCapability> exporters = resolver.getCandidates(req, false);
+
+ BundleRevision provider = null;
+ try
+ {
+ provider = resolver.resolve(revision, pkgName);
+ }
+ catch (Exception ex)
+ {
+ provider = null;
+ }
+
+ String exporter = (exporters.isEmpty())
+ ? null : exporters.iterator().next().getRevision().getBundle().toString();
+
+ 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(importer);
+ sb.append(".");
+ if ((exporters.size() > 0) && (provider == null))
+ {
+ sb.append(" However, bundle ");
+ sb.append(exporter);
+ sb.append(" does export this package with attributes that do not match.");
+ }
+ sb.append(" ***");
+
+ return sb.toString();
+ }
+
+ // Next, check to see if there are any exporters for the package at all.
+ Map<String, String> dirs = Collections.EMPTY_MAP;
+ Map<String, Object> attrs = new HashMap<String, Object>(1);
+ attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+ BundleRequirementImpl req = new BundleRequirementImpl(
+ revision, BundleCapabilityImpl.PACKAGE_NAMESPACE, dirs, attrs);
+ Set<BundleCapability> exports = resolver.getCandidates(req, false);
+ if (exports.size() > 0)
+ {
+ boolean classpath = false;
+ try
+ {
+ m_secureAction.getClassLoader(BundleClassLoader.class).loadClass(name);
+ classpath = true;
+ }
+ catch (NoClassDefFoundError err)
+ {
+ // Ignore
+ }
+ catch (Exception ex)
+ {
+ // Ignore
+ }
+
+ String exporter = exports.iterator().next().getRevision().getBundle().toString();
+
+ StringBuffer sb = new StringBuffer("*** Class '");
+ sb.append(name);
+ sb.append("' was not found because bundle ");
+ sb.append(importer);
+ sb.append(" does not import '");
+ sb.append(pkgName);
+ sb.append("' even though bundle ");
+ sb.append(exporter);
+ 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(importer);
+ 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(importer);
+ sb.append(".");
+ }
+ sb.append(" ***");
+
+ return sb.toString();
+ }
+
+ // Next, try to see if the class is available from the system
+ // class loader.
+ try
+ {
+ m_secureAction.getClassLoader(BundleClassLoader.class).loadClass(name);
+
+ StringBuffer sb = new StringBuffer("*** Package '");
+ sb.append(pkgName);
+ sb.append("' is not imported by bundle ");
+ sb.append(importer);
+ 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(importer);
+ 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(importer);
+ 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();
+ }
+}
diff --git a/framework/src/main/java/org/apache/felix/framework/EntryFilterEnumeration.java b/framework/src/main/java/org/apache/felix/framework/EntryFilterEnumeration.java
index 51c17f8..7e039c7 100644
--- a/framework/src/main/java/org/apache/felix/framework/EntryFilterEnumeration.java
+++ b/framework/src/main/java/org/apache/felix/framework/EntryFilterEnumeration.java
@@ -43,7 +43,7 @@
{
m_bundle = bundle;
BundleRevision br = m_bundle.getCurrentRevision();
- List<BundleRevision> fragments = ((BundleRevisionImpl) br).getFragments();
+ List<BundleRevision> fragments = ((BundleWiringImpl) br.getWiring()).getFragments();
if (includeFragments && (fragments != null))
{
m_revisions = new ArrayList(fragments.size() + 1);
diff --git a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
index f463c3a..be07bbc 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -27,6 +27,7 @@
import java.security.AccessControlException;
import java.security.AllPermission;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
@@ -36,13 +37,15 @@
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
-
import org.apache.felix.framework.Felix.StatefulResolver;
+
+import org.apache.felix.framework.resolver.ResolverWire;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.StringMap;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.util.manifestparser.ManifestParser;
import org.apache.felix.framework.resolver.Content;
+import org.apache.felix.framework.util.manifestparser.R4Library;
import org.apache.felix.framework.wiring.BundleCapabilityImpl;
import org.osgi.framework.AdminPermission;
import org.osgi.framework.Bundle;
@@ -53,6 +56,7 @@
import org.osgi.framework.Version;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
/**
* The ExtensionManager class is used in several ways.
@@ -115,6 +119,7 @@
}
private final Logger m_logger;
+ private final Map m_configMap;
private final Map m_headerMap = new StringMap(false);
private final BundleRevision m_systemBundleRevision;
private List<BundleCapability> m_capabilities = null;
@@ -130,6 +135,7 @@
private ExtensionManager()
{
m_logger = null;
+ m_configMap = null;
m_systemBundleRevision = null;
m_extensions = new ArrayList();
m_extensionsCache = new Bundle[0];
@@ -148,19 +154,20 @@
* @param config the configuration to read properties from.
* @param systemBundleInfo the info to change if we need to add exports.
*/
- ExtensionManager(Logger logger, Felix felix)
+ ExtensionManager(Logger logger, Map configMap, Felix felix)
{
+ m_logger = logger;
+ m_configMap = configMap;
m_systemBundleRevision = new ExtensionManagerRevision(felix);
m_extensions = null;
m_extensionsCache = null;
m_names = null;
m_sourceToExtensions = null;
- m_logger = logger;
// TODO: FRAMEWORK - Not all of this stuff really belongs here, probably only exports.
// Populate system bundle header map.
m_headerMap.put(FelixConstants.BUNDLE_VERSION,
- felix.getConfig().get(FelixConstants.FELIX_VERSION_PROPERTY));
+ m_configMap.get(FelixConstants.FELIX_VERSION_PROPERTY));
m_headerMap.put(FelixConstants.BUNDLE_SYMBOLICNAME,
FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME);
m_headerMap.put(FelixConstants.BUNDLE_NAME, "System Bundle");
@@ -176,21 +183,21 @@
// 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) felix.getConfig().get(FelixConstants.FRAMEWORK_SYSTEMPACKAGES);
+ String syspkgs = (String) m_configMap.get(FelixConstants.FRAMEWORK_SYSTEMPACKAGES);
// If no system packages were specified, load our default value.
syspkgs = (syspkgs == null)
? Util.getDefaultProperty(logger, Constants.FRAMEWORK_SYSTEMPACKAGES)
: syspkgs;
syspkgs = (syspkgs == null) ? "" : syspkgs;
// If any extra packages are specified, then append them.
- String extra = (String) felix.getConfig().get(FelixConstants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
+ String extra = (String) m_configMap.get(FelixConstants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
syspkgs = (extra == null) ? syspkgs : syspkgs + "," + extra;
m_headerMap.put(FelixConstants.BUNDLE_MANIFESTVERSION, "2");
m_headerMap.put(FelixConstants.EXPORT_PACKAGE, syspkgs);
try
{
ManifestParser mp = new ManifestParser(
- m_logger, felix.getConfig(), m_systemBundleRevision, m_headerMap);
+ m_logger, m_configMap, m_systemBundleRevision, m_headerMap);
List<BundleCapability> caps = aliasSymbolicName(mp.getCapabilities());
setCapabilities(caps);
}
@@ -635,14 +642,17 @@
class ExtensionManagerRevision extends BundleRevisionImpl
{
private final Version m_version;
+ private volatile BundleWiring m_wiring;
+
ExtensionManagerRevision(Felix felix)
{
- super(m_logger, felix.getConfig(), felix, "0",
+ super(m_logger, m_configMap, felix, "0",
felix.getBootPackages(), felix.getBootPackageWildcards());
m_version = new Version((String)
- felix.getConfig().get(FelixConstants.FELIX_VERSION_PROPERTY));
+ m_configMap.get(FelixConstants.FELIX_VERSION_PROPERTY));
}
+ @Override
public Map getHeaders()
{
synchronized (ExtensionManager.this)
@@ -651,6 +661,7 @@
}
}
+ @Override
public List<BundleCapability> getDeclaredCapabilities(String namespace)
{
synchronized (ExtensionManager.this)
@@ -659,6 +670,85 @@
}
}
+ @Override
+ public String getSymbolicName()
+ {
+ return FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME;
+ }
+
+ @Override
+ public Version getVersion()
+ {
+ return m_version;
+ }
+
+ @Override
+ public void close()
+ {
+ // Nothing needed here.
+ }
+
+ @Override
+ public Content getContent()
+ {
+ return ExtensionManager.this;
+ }
+
+ @Override
+ public URL getEntry(String name)
+ {
+ // There is no content for the system bundle, so return null.
+ return null;
+ }
+
+ @Override
+ public boolean hasInputStream(int index, String urlPath)
+ {
+ return (getClass().getClassLoader().getResource(urlPath) != null);
+ }
+
+ @Override
+ public InputStream getInputStream(int index, String urlPath)
+ {
+ return getClass().getClassLoader().getResourceAsStream(urlPath);
+ }
+
+ @Override
+ public URL getLocalURL(int index, String urlPath)
+ {
+ return getClass().getClassLoader().getResource(urlPath);
+ }
+
+ @Override
+ public void resolve(
+ List<BundleRevision> fragments, List<ResolverWire> rws,
+ Map<ResolverWire, Set<String>> requiredPkgWires) throws Exception
+ {
+ m_wiring = new ExtensionManagerWiring(
+ m_logger, m_configMap, null, this, fragments, rws, requiredPkgWires);
+ }
+
+ @Override
+ public BundleWiring getWiring()
+ {
+ return m_wiring;
+ }
+ }
+
+ class ExtensionManagerWiring extends BundleWiringImpl
+ {
+ ExtensionManagerWiring(
+ Logger logger, Map configMap, StatefulResolver resolver,
+ BundleRevisionImpl revision, List<BundleRevision> fragments,
+ List<ResolverWire> resolverWires,
+ Map<ResolverWire, Set<String>> requiredPkgWires)
+ throws Exception
+ {
+ super(logger, configMap, resolver, revision,
+ fragments, resolverWires, requiredPkgWires);
+ }
+
+ @Override
public List<BundleCapability> getCapabilities(String namespace)
{
synchronized (ExtensionManager.this)
@@ -667,16 +757,13 @@
}
}
- public String getSymbolicName()
+ @Override
+ public List<R4Library> getNativeLibraries()
{
- return FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME;
+ return Collections.EMPTY_LIST;
}
- public Version getVersion()
- {
- return m_version;
- }
-
+ @Override
public Class getClassByDelegation(String name) throws ClassNotFoundException
{
Class clazz = null;
@@ -720,11 +807,13 @@
return clazz;
}
+ @Override
public URL getResourceByDelegation(String name)
{
return getClass().getClassLoader().getResource(name);
}
+ @Override
public Enumeration getResourcesByDelegation(String name)
{
try
@@ -737,53 +826,13 @@
}
}
- public Logger getLogger()
- {
- return m_logger;
- }
-
- public Map getConfig()
- {
- return null;
- }
-
- public StatefulResolver getResolver()
- {
- return null;
- }
-
- public void attachFragmentContents(Content[] fragmentContents)
- throws Exception
- {
- throw new UnsupportedOperationException("Should not be used!");
- }
-
- public void close()
+ @Override
+ public void dispose()
{
// Nothing needed here.
}
- public Content getContent()
- {
- return ExtensionManager.this;
- }
-
- public URL getEntry(String name)
- {
- // There is no content for the system bundle, so return null.
- return null;
- }
-
- public boolean hasInputStream(int index, String urlPath)
- {
- return (getClass().getClassLoader().getResource(urlPath) != null);
- }
-
- public InputStream getInputStream(int index, String urlPath)
- {
- return getClass().getClassLoader().getResourceAsStream(urlPath);
- }
-
+ @Override
public URL getLocalURL(int index, String urlPath)
{
return getClass().getClassLoader().getResource(urlPath);
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index db092bc..4889fa5 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -369,7 +369,7 @@
// Create the extension manager, which we will use as the
// revision for the system bundle.
- m_extensionManager = new ExtensionManager(m_logger, this);
+ m_extensionManager = new ExtensionManager(m_logger, m_configMap, this);
try
{
addRevision(m_extensionManager.getRevision());
@@ -1476,7 +1476,21 @@
{
return null;
}
- return ((BundleRevisionImpl) bundle.getCurrentRevision()).getResourceByDelegation(name);
+// TODO: OSGi R4.3 - Currently, we try to resolve resource requests in
+// findClassOrResourceByDelegation() and fall back to local resource
+// searching if it fails. Perhaps we should attempt the resolve here
+// and do the local searching here. This means we could get rid of
+// resolve attempts in findClassOrResourceByDelegation().
+ if (bundle.getCurrentRevision().getWiring() == null)
+ {
+ return ((BundleRevisionImpl) bundle.getCurrentRevision())
+ .getResourceLocal(name);
+ }
+ else
+ {
+ return ((BundleWiringImpl) bundle.getCurrentRevision().getWiring())
+ .getResourceByDelegation(name);
+ }
}
/**
@@ -1492,7 +1506,21 @@
{
return null;
}
- return ((BundleRevisionImpl) bundle.getCurrentRevision()).getResourcesByDelegation(name);
+// TODO: OSGi R4.3 - Currently, we try to resolve resource requests in
+// findResourcesByDelegation() and fall back to local resource
+// searching if it fails. Perhaps we should attempt the resolve here
+// and do the local searching here. This means we could get rid of
+// resolve attempts in findResourcesByDelegation().
+ if (bundle.getCurrentRevision().getWiring() == null)
+ {
+ return ((BundleRevisionImpl) bundle.getCurrentRevision())
+ .getResourcesLocal(name);
+ }
+ else
+ {
+ return ((BundleWiringImpl) bundle.getCurrentRevision().getWiring())
+ .getResourcesByDelegation(name);
+ }
}
/**
@@ -1651,7 +1679,8 @@
throw new ClassNotFoundException(name, ex);
}
}
- return ((BundleRevisionImpl) bundle.getCurrentRevision()).getClassByDelegation(name);
+ return ((BundleWiringImpl)
+ bundle.getCurrentRevision().getWiring()).getClassByDelegation(name);
}
/**
@@ -3216,8 +3245,8 @@
Class sbClass = null;
try
{
- sbClass = ((BundleRevisionImpl) m_extensionManager
- .getRevision()).getClassByDelegation(clazz.getName());
+ sbClass = ((BundleWiringImpl) m_extensionManager
+ .getRevision().getWiring()).getClassByDelegation(clazz.getName());
}
catch (ClassNotFoundException ex)
{
@@ -3412,24 +3441,19 @@
BundleCapabilityImpl.PACKAGE_NAMESPACE,
Collections.EMPTY_MAP,
attrs);
- Set<BundleCapability> providers =
- m_resolver.getCandidates(req, false);
- // We only want resolved capabilities.
- for (Iterator<BundleCapability> it = providers.iterator();
- it.hasNext(); )
- {
- if (it.next().getRevision().getWiring() == null)
- {
- it.remove();
- }
- }
+ Set<BundleCapability> providers = m_resolver.getCandidates(req, false);
// Search through the current providers to find the target revision.
- for (BundleCapability provider : providers)
+ // We only want resolved capabilities.
+ if (providers != null)
{
- if (provider == cap)
+ for (BundleCapability provider : providers)
{
- list.add(new ExportedPackageImpl(this, bundle, br, cap));
+ if ((provider.getRevision().getWiring() != null)
+ && (provider == cap))
+ {
+ list.add(new ExportedPackageImpl(this, bundle, br, cap));
+ }
}
}
}
@@ -3445,10 +3469,10 @@
// Get all dependent revisions from all exporter revisions.
List<BundleRevision> revisions = exporter.getRevisions();
- for (int modIdx = 0; modIdx < revisions.size(); modIdx++)
+ for (int revIdx = 0; revIdx < revisions.size(); revIdx++)
{
List<BundleRevision> dependents =
- ((BundleRevisionImpl) revisions.get(modIdx)).getDependents();
+ ((BundleRevisionImpl) revisions.get(revIdx)).getDependents();
for (int depIdx = 0;
(dependents != null) && (depIdx < dependents.size());
depIdx++)
@@ -3475,24 +3499,31 @@
{
// Include any importers that have wires to the specific
// exported package.
- List<BundleRevision> dependents =
- ((BundleRevisionImpl) expRevisions.get(expIdx)).getDependentImporters();
- for (int depIdx = 0; (dependents != null) && (depIdx < dependents.size()); depIdx++)
+ if (expRevisions.get(expIdx).getWiring() != null)
{
- BundleRevision providerRevision =
- ((BundleRevisionImpl) dependents.get(depIdx))
- .getImportedPackageSource(ep.getName());
- if ((providerRevision != null)
- && (providerRevision == expRevisions.get(expIdx)))
+ List<BundleRevision> dependents = ((BundleRevisionImpl)
+ expRevisions.get(expIdx)).getDependentImporters();
+ for (int depIdx = 0; (dependents != null) && (depIdx < dependents.size()); depIdx++)
+ {
+ if (dependents.get(depIdx).getWiring() != null)
+ {
+ BundleRevision providerRevision =
+ ((BundleWiringImpl) dependents.get(depIdx).getWiring())
+ .getImportedPackageSource(ep.getName());
+ if ((providerRevision != null)
+ && (providerRevision == expRevisions.get(expIdx)))
+ {
+ list.add(dependents.get(depIdx).getBundle());
+ }
+ }
+ }
+ dependents = ((BundleRevisionImpl)
+ expRevisions.get(expIdx)).getDependentRequirers();
+ for (int depIdx = 0; (dependents != null) && (depIdx < dependents.size()); depIdx++)
{
list.add(dependents.get(depIdx).getBundle());
}
}
- dependents = ((BundleRevisionImpl) expRevisions.get(expIdx)).getDependentRequirers();
- for (int depIdx = 0; (dependents != null) && (depIdx < dependents.size()); depIdx++)
- {
- list.add(dependents.get(depIdx).getBundle());
- }
}
// Return the results.
@@ -3818,8 +3849,8 @@
Class clazz;
try
{
- clazz = ((BundleRevisionImpl)
- impl.getCurrentRevision()).getClassByDelegation(className);
+ clazz = ((BundleWiringImpl)
+ impl.getCurrentRevision().getWiring()).getClassByDelegation(className);
}
catch (ClassNotFoundException ex)
{
@@ -4206,7 +4237,7 @@
// dynamically importing the package, which can happen if two
// threads are racing to do so. If we have an existing wire,
// then just return it instead.
- provider = ((BundleRevisionImpl)revision)
+ provider = ((BundleWiringImpl) revision.getWiring())
.getImportedPackageSource(pkgName);
if (provider == null)
{
@@ -4225,12 +4256,13 @@
// Dynamically add new wire to importing revision.
if (dynamicWire != null)
{
- ((BundleRevisionImpl) revision).addDynamicWire(dynamicWire);
+ ((BundleWiringImpl) revision.getWiring())
+ .addDynamicWire(dynamicWire);
m_logger.log(
Logger.LOG_DEBUG,
"DYNAMIC WIRE: " + dynamicWire);
- provider = ((BundleRevisionImpl) revision)
+ provider = ((BundleWiringImpl) revision.getWiring())
.getImportedPackageSource(pkgName);
}
}
@@ -4262,7 +4294,7 @@
// If the revision doesn't have dynamic imports, then just return
// immediately.
List<BundleRequirement> dynamics =
- ((BundleRevisionImpl) revision).getResolvedDynamicRequirements();
+ ((BundleWiringImpl) revision.getWiring()).getDynamicRequirements();
if ((dynamics == null) || dynamics.isEmpty())
{
return false;
@@ -4281,7 +4313,7 @@
// If this revision already imports or requires this package, then
// we cannot dynamically import it.
- if (((BundleRevisionImpl) revision).hasPackageSource(pkgName))
+ if (((BundleWiringImpl) revision.getWiring()).hasPackageSource(pkgName))
{
return false;
}
@@ -4361,64 +4393,54 @@
}
}
- ((BundleRevisionImpl) revision).setWires(resolverWires, requiredPkgWires);
-
- // Attach fragments, if any.
List<BundleRevision> fragments = hosts.get(revision);
- if (fragments != null)
+ try
{
- try
+// TODO: OSGi R4.3 - Technically, this is where the revision becomes resolved,
+// but we used to wait and mark it as resolved in the third phase below.
+ ((BundleRevisionImpl) revision).resolve(
+ fragments, resolverWires, requiredPkgWires);
+ }
+ catch (Exception ex)
+ {
+ // This is a fatal error, so undo everything and
+ // throw an exception.
+ for (Entry<BundleRevision, List<ResolverWire>> reentry : wireMap.entrySet())
{
- ((BundleRevisionImpl) revision).attachFragments(fragments);
- }
- catch (Exception ex)
- {
- // This is a fatal error, so undo everything and
- // throw an exception.
- for (Entry<BundleRevision, List<ResolverWire>> reentry : wireMap.entrySet())
+ revision = reentry.getKey();
+
+ // Undo wires.
+ try
{
- revision = reentry.getKey();
-
- // Undo wires.
- ((BundleRevisionImpl) revision).setWires(null, null);
-
- fragments = hosts.get(revision);
- if (fragments != null)
- {
- try
- {
- // Undo fragments.
- ((BundleRevisionImpl) revision).attachFragments(null);
- }
- catch (Exception ex2)
- {
- // We are in big trouble.
- RuntimeException rte = new RuntimeException(
- "Unable to clean up resolver failure.", ex2);
- m_logger.log(
- Logger.LOG_ERROR,
- rte.getMessage(), ex2);
- throw rte;
- }
-
- // Reindex host with no fragments.
- m_resolverState.addRevision(revision);
- }
+ ((BundleRevisionImpl) revision).resolve(null, null, null);
+ }
+ catch (Exception ex2)
+ {
+ // We are in big trouble.
+ RuntimeException rte = new RuntimeException(
+ "Unable to clean up resolver failure.", ex2);
+ m_logger.log(
+ Logger.LOG_ERROR,
+ rte.getMessage(), ex2);
+ throw rte;
}
- ResolveException re = new ResolveException(
- "Unable to attach fragments to " + revision,
- revision, null);
- re.initCause(ex);
- m_logger.log(
- Logger.LOG_ERROR,
- re.getMessage(), ex);
- throw re;
+ // Reindex host with no fragments.
+ m_resolverState.addRevision(revision);
}
- // Reindex host with attached fragments.
- m_resolverState.addRevision(revision);
+ ResolveException re = new ResolveException(
+ "Unable to resolve " + revision,
+ revision, null);
+ re.initCause(ex);
+ m_logger.log(
+ Logger.LOG_ERROR,
+ re.getMessage(), ex);
+ throw re;
}
+
+ // Reindex host with attached fragments.
+ m_resolverState.addRevision(revision);
}
// Third pass: Loop through the wire map to mark revision as resolved
@@ -4427,7 +4449,8 @@
{
BundleRevision revision = entry.getKey();
// Mark revision as resolved.
- ((BundleRevisionImpl) revision).setResolved();
+// TODO: OSGi R4.3 - See message above when we call BundleRevisionImpl.resolve().
+// ((BundleRevisionImpl) revision).setResolved();
// Update resolver state to remove substituted capabilities.
if (!Util.isFragment(revision))
{
@@ -4492,7 +4515,8 @@
BundleRevision revision = entry.getKey();
// Fire RESOLVED events for all fragments.
- List<BundleRevision> fragments = ((BundleRevisionImpl) revision).getFragments();
+ List<BundleRevision> fragments =
+ ((BundleWiringImpl) revision.getWiring()).getFragments();
for (int i = 0; (fragments != null) && (i < fragments.size()); i++)
{
fireBundleEvent(BundleEvent.RESOLVED, fragments.get(i).getBundle());
diff --git a/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java b/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java
index f19531f..73ae668 100644
--- a/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java
@@ -197,14 +197,13 @@
{
List<Bundle> list = new ArrayList<Bundle>();
// Iterate through revisions
- List<BundleRevision> revisions = ((BundleImpl) bundle).getRevisions();
- for (int modIdx = 0; modIdx < revisions.size(); modIdx++)
+ for (BundleRevision revision : ((BundleImpl) bundle).getRevisions())
{
// Get attached fragments.
- BundleRevisionImpl revision = (BundleRevisionImpl) revisions.get(modIdx);
- if (revision.isResolved())
+ if (revision.getWiring() != null)
{
- List<BundleRevision> fragments = revision.getFragments();
+ List<BundleRevision> fragments =
+ ((BundleWiringImpl) revision.getWiring()).getFragments();
for (int i = 0; (fragments != null) && (i < fragments.size()); i++)
{
Bundle b = fragments.get(i).getBundle();
@@ -230,14 +229,12 @@
{
List<Bundle> list = new ArrayList<Bundle>();
// Iterate through revisions
- List<BundleRevision> revisions = ((BundleImpl) bundle).getRevisions();
- for (int modIdx = 0; modIdx < revisions.size(); modIdx++)
+ for (BundleRevision revision : ((BundleImpl) bundle).getRevisions())
{
// Get hosts
- BundleRevisionImpl revision = (BundleRevisionImpl) revisions.get(modIdx);
- if (revision.isResolved())
+ if (revision.getWiring() != null)
{
- List<BundleWire> hostWires = revision.getWires();
+ List<BundleWire> hostWires = revision.getWiring().getRequiredWires(null);
for (int i = 0; (hostWires != null) && (i < hostWires.size()); i++)
{
Bundle b = hostWires.get(i).getProviderWiring().getBundle();
@@ -259,14 +256,12 @@
public RequiredBundle[] getRequiredBundles(String symbolicName)
{
List list = new ArrayList();
- Bundle[] bundles = m_felix.getBundles();
- for (int i = 0; i < bundles.length; i++)
+ for (Bundle bundle : m_felix.getBundles())
{
- BundleImpl impl = (BundleImpl) bundles[i];
if ((symbolicName == null)
- || (symbolicName.equals(impl.getCurrentRevision().getSymbolicName())))
+ || (symbolicName.equals(bundle.getSymbolicName())))
{
- list.add(new RequiredBundleImpl(m_felix, impl));
+ list.add(new RequiredBundleImpl(m_felix, (BundleImpl) bundle));
}
}
return (list.isEmpty())
diff --git a/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java b/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java
index b76ade8..bdb4ade 100644
--- a/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java
@@ -145,7 +145,7 @@
// If so, then the framework must have chosen to have the revision
// import rather than export the package, so we need to remove the
// corresponding package capability from the package capability set.
- for (BundleWire w : ((BundleRevisionImpl) br).getWires())
+ for (BundleWire w : br.getWiring().getRequiredWires(null))
{
if (w.getCapability().getNamespace().equals(BundleCapabilityImpl.PACKAGE_NAMESPACE))
{
@@ -274,7 +274,9 @@
{
// Next, try to resolve any native code, since the revision is
// not resolvable if its native code cannot be loaded.
- List<R4Library> libs = ((BundleRevisionImpl) revision).getNativeLibraries();
+// TODO: OSGi R4.3 - Is it sufficient to just check declared native libs here?
+// List<R4Library> libs = ((BundleWiringImpl) revision.getWiring()).getNativeLibraries();
+ List<R4Library> libs = ((BundleRevisionImpl) revision).getDeclaredNativeLibraries();
if (libs != null)
{
String msg = null;
diff --git a/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java b/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java
index f544c4a..02b5fd4 100644
--- a/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java
@@ -519,7 +519,8 @@
try
{
Class requestClass =
- ((BundleRevisionImpl) requesterRevision).getClassByDelegation(className);
+ ((BundleWiringImpl) requesterRevision.getWiring())
+ .getClassByDelegation(className);
allow = getRegistration().isClassAccessible(requestClass);
}
catch (Exception ex)
@@ -544,14 +545,15 @@
try
{
// Try to load class from requester.
- Class requestClass =((BundleRevisionImpl)
- requesterRevision).getClassByDelegation(className);
+ Class requestClass =((BundleWiringImpl)
+ requesterRevision.getWiring()).getClassByDelegation(className);
try
{
// If requester has access to the class, verify it is the
// same class as the provider.
- allow = (((BundleRevisionImpl) providerRevision)
- .getClassByDelegation(className) == requestClass);
+ allow = (((BundleWiringImpl)
+ providerRevision.getWiring())
+ .getClassByDelegation(className) == requestClass);
}
catch (Exception ex)
{
@@ -585,8 +587,9 @@
try
{
// Load the class from the requesting bundle.
- Class requestClass = ((BundleRevisionImpl)
- requesterRevision).getClassByDelegation(className);
+ Class requestClass = ((BundleWiringImpl)
+ requesterRevision.getWiring())
+ .getClassByDelegation(className);
// Get the service registration and ask it to check
// if the service object is assignable to the requesting
// bundle's class.
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java b/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
index 54f34c5..6092980 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
@@ -126,8 +126,8 @@
if (!((BundleRevisionImpl) m_targetRevision)
.hasInputStream(m_classPathIdx, url.getPath()))
{
- URL newurl = ((BundleRevisionImpl)
- m_targetRevision).getResourceByDelegation(url.getPath());
+ URL newurl = ((BundleWiringImpl)
+ m_targetRevision.getWiring()).getResourceByDelegation(url.getPath());
if (newurl == null)
{
throw new IOException("Resource does not exist: " + url);
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java b/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
index c6d6a5c..066a303 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
@@ -31,6 +31,7 @@
import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.felix.framework.BundleRevisionImpl;
+import org.apache.felix.framework.BundleWiringImpl;
import org.apache.felix.framework.resolver.Resolver.ResolverState;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.wiring.BundleCapabilityImpl;
@@ -990,8 +991,11 @@
System.out.println(" " + req + ": " + candidates);
}
}
+// TODO: OSGi R4.3 - We need to get dynamic requirements using public API
+// then we might not need to make the BundleWiringImpl and BundleRevisionImpl
+// classes public.
reqs = (br.getWiring() != null)
- ? ((BundleRevisionImpl) br).getResolvedDynamicRequirements()
+ ? ((BundleWiringImpl) br.getWiring()).getDynamicRequirements()
: ((BundleRevisionImpl) br).getDeclaredDynamicRequirements();
for (BundleRequirement req : reqs)
{
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
index 829098e..3110261 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
@@ -29,6 +29,7 @@
import java.util.Set;
import java.util.SortedSet;
import org.apache.felix.framework.BundleRevisionImpl;
+import org.apache.felix.framework.BundleWiringImpl;
import org.apache.felix.framework.Logger;
import org.apache.felix.framework.capabilityset.CapabilitySet;
import org.apache.felix.framework.util.Util;
@@ -62,7 +63,7 @@
Map<BundleRevision, List<ResolverWire>> wireMap = new HashMap<BundleRevision, List<ResolverWire>>();
Map<BundleRevision, Packages> revisionPkgMap = new HashMap<BundleRevision, Packages>();
- if (!((BundleRevisionImpl) revision).isResolved())
+ if (revision.getWiring() == null)
{
boolean retryFragments;
do
@@ -360,7 +361,7 @@
// If the revision doesn't have dynamic imports, then just return
// immediately.
List<BundleRequirement> dynamics =
- ((BundleRevisionImpl) revision).getResolvedDynamicRequirements();
+ ((BundleWiringImpl) revision.getWiring()).getDynamicRequirements();
if ((dynamics == null) || dynamics.isEmpty())
{
return null;
@@ -379,7 +380,7 @@
// If this revision already imports or requires this package, then
// we cannot dynamically import it.
- if (((BundleRevisionImpl) revision).hasPackageSource(pkgName))
+ if (((BundleWiringImpl) revision.getWiring()).hasPackageSource(pkgName))
{
return null;
}
@@ -466,7 +467,7 @@
if (revision.getWiring() != null)
{
// Use wires to get actual requirements and satisfying capabilities.
- for (BundleWire wire : ((BundleRevisionImpl) revision).getWires())
+ for (BundleWire wire : revision.getWiring().getRequiredWires(null))
{
// Wrap the requirement as a hosted requirement
// if it comes from a fragment, since we will need
@@ -496,7 +497,7 @@
// so check to see if there are candidates for any of its dynamic
// imports.
for (BundleRequirement req
- : ((BundleRevisionImpl) revision).getResolvedDynamicRequirements())
+ : ((BundleWiringImpl) revision.getWiring()).getDynamicRequirements())
{
// Get the candidates for the current requirement.
SortedSet<BundleCapability> candCaps =
@@ -1171,7 +1172,7 @@
// exports are substitutable.
if (revision.getWiring() != null)
{
- for (BundleWire wire : ((BundleRevisionImpl) revision).getWires())
+ for (BundleWire wire : revision.getWiring().getRequiredWires(null))
{
if (wire.getRequirement().getNamespace().equals(
BundleCapabilityImpl.PACKAGE_NAMESPACE))
diff --git a/framework/src/main/java/org/apache/felix/framework/util/Util.java b/framework/src/main/java/org/apache/felix/framework/util/Util.java
index 817ad66..8cc096f 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/Util.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/Util.java
@@ -329,17 +329,20 @@
public static BundleWire getWire(BundleRevision br, String name)
{
- List<BundleWire> wires = ((BundleRevisionImpl) br).getWires();
- if (wires != null)
+ if (br.getWiring() != null)
{
- for (BundleWire w : wires)
+ List<BundleWire> wires = br.getWiring().getRequiredWires(null);
+ if (wires != null)
{
- if (w.getCapability().getNamespace()
- .equals(BundleCapabilityImpl.PACKAGE_NAMESPACE) &&
- w.getCapability().getAttributes()
- .get(BundleCapabilityImpl.PACKAGE_ATTR).equals(name))
+ for (BundleWire w : wires)
{
- return w;
+ if (w.getCapability().getNamespace()
+ .equals(BundleCapabilityImpl.PACKAGE_NAMESPACE) &&
+ w.getCapability().getAttributes()
+ .get(BundleCapabilityImpl.PACKAGE_ATTR).equals(name))
+ {
+ return w;
+ }
}
}
}