Copyright (c) 2005, 2016 IBM Corporation and others.
This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
Contributors:
IBM Corporation - initial API and implementation
/*******************************************************************************
* Copyright (c) 2005, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.loader;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.PermissionCollection;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.osgi.container.ModuleRevision;
import org.eclipse.osgi.internal.debug.Debug;
import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
import org.eclipse.osgi.internal.loader.classpath.ClasspathEntry;
import org.eclipse.osgi.internal.loader.classpath.ClasspathManager;
import org.eclipse.osgi.signedcontent.SignedContent;
import org.eclipse.osgi.signedcontent.SignerInfo;
import org.eclipse.osgi.storage.BundleInfo.Generation;
import org.eclipse.osgi.storage.bundlefile.BundleFile;
import org.eclipse.osgi.storage.bundlefile.BundleFileWrapperChain;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleReference;
public abstract class ModuleClassLoader extends ClassLoader implements BundleReference {
public static class GenerationProtectionDomain extends ProtectionDomain implements BundleReference {
private final Generation generation;
public GenerationProtectionDomain(CodeSource codesource, PermissionCollection permissions, Generation generation) {
super(codesource, permissions);
this.generation = generation;
}
@Override
public Bundle getBundle() {
return generation.getRevision().getBundle();
}
}
A PermissionCollection for AllPermissions; shared across all ProtectionDomains when security is disabled
/**
* A PermissionCollection for AllPermissions; shared across all ProtectionDomains when security is disabled
*/
protected static final PermissionCollection ALLPERMISSIONS;
protected static final boolean REGISTERED_AS_PARALLEL;
static {
boolean registered;
try {
registered = ClassLoader.registerAsParallelCapable();
} catch (Throwable t) {
registered = false;
}
REGISTERED_AS_PARALLEL = registered;
}
static {
AllPermission allPerm = new AllPermission();
ALLPERMISSIONS = allPerm.newPermissionCollection();
if (ALLPERMISSIONS != null)
ALLPERMISSIONS.add(allPerm);
}
Holds the result of a defining a class.
/**
* Holds the result of a defining a class.
*
*/
public static class DefineClassResult {
The class object that either got defined or was found as previously loaded.
/**
* The class object that either got defined or was found as previously loaded.
*/
public final Class<?> clazz;
Set to true if the class object got defined; set to false if the class
did not get defined correctly or the class was found as a previously loaded
class.
/**
* Set to true if the class object got defined; set to false if the class
* did not get defined correctly or the class was found as a previously loaded
* class.
*/
public final boolean defined;
public DefineClassResult(Class<?> clazz, boolean defined) {
this.clazz = clazz;
this.defined = defined;
}
}
private final Map<String, Thread> classNameLocks = new HashMap<>(5);
private final Object pkgLock = new Object();
Constructs a new ModuleClassLoader.
Params: - parent – the parent classloader
/**
* Constructs a new ModuleClassLoader.
* @param parent the parent classloader
*/
public ModuleClassLoader(ClassLoader parent) {
super(parent);
}
Returns the generation of the host revision associated with this class loader
Returns: the generation for this class loader
/**
* Returns the generation of the host revision associated with this class loader
* @return the generation for this class loader
*/
protected abstract Generation getGeneration();
Returns the Debug object for the Framework instance
Returns: the Debug object for the Framework instance
/**
* Returns the Debug object for the Framework instance
* @return the Debug object for the Framework instance
*/
protected abstract Debug getDebug();
Returns the classpath manager for this class loader
Returns: the classpath manager for this class loader
/**
* Returns the classpath manager for this class loader
* @return the classpath manager for this class loader
*/
public abstract ClasspathManager getClasspathManager();
Returns the configuration for the Framework instance
Returns: the configuration for the Framework instance
/**
* Returns the configuration for the Framework instance
* @return the configuration for the Framework instance
*/
protected abstract EquinoxConfiguration getConfiguration();
Returns the bundle loader for this class loader
Returns: the bundle loader for this class loader
/**
* Returns the bundle loader for this class loader
* @return the bundle loader for this class loader
*/
public abstract BundleLoader getBundleLoader();
Returns true if this class loader implementation has been
registered with the JVM as a parallel class loader.
This requires Java 7 or later.
Returns: true if this class loader implementation has been
registered with the JVM as a parallel class loader; otherwise
false is returned.
/**
* Returns true if this class loader implementation has been
* registered with the JVM as a parallel class loader.
* This requires Java 7 or later.
* @return true if this class loader implementation has been
* registered with the JVM as a parallel class loader; otherwise
* false is returned.
*/
public abstract boolean isRegisteredAsParallel();
Loads a class for the bundle. First delegate.findClass(name) is called.
The delegate will query the system class loader, bundle imports, bundle
local classes, bundle hosts and fragments. The delegate will call
BundleClassLoader.findLocalClass(name) to find a class local to this
bundle.
Params: - name – the name of the class to load.
- resolve – indicates whether to resolve the loaded class or not.
Throws: - ClassNotFoundException – if the class is not found.
Returns: The Class object.
/**
* Loads a class for the bundle. First delegate.findClass(name) is called.
* The delegate will query the system class loader, bundle imports, bundle
* local classes, bundle hosts and fragments. The delegate will call
* BundleClassLoader.findLocalClass(name) to find a class local to this
* bundle.
* @param name the name of the class to load.
* @param resolve indicates whether to resolve the loaded class or not.
* @return The Class object.
* @throws ClassNotFoundException if the class is not found.
*/
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (getDebug().DEBUG_LOADER)
Debug.println("ModuleClassLoader[" + getBundleLoader() + "].loadClass(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
try {
// Just ask the delegate. This could result in findLocalClass(name) being called.
Class<?> clazz = getBundleLoader().findClass(name);
// resolve the class if asked to.
if (resolve)
resolveClass(clazz);
return (clazz);
} catch (Error | ClassNotFoundException e) {
// If the class is not found do not try to look for it locally.
// The delegate would have already done that for us.
if (getDebug().DEBUG_LOADER) {
Debug.println("ModuleClassLoader[" + getBundleLoader() + "].loadClass(" + name + ") failed."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Debug.printStackTrace(e);
}
throw e;
}
}
// preparing for Java 9
protected Class<?> findClass(String moduleName, String name) {
try {
return findLocalClass(name);
} catch (ClassNotFoundException e) {
return null;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return findLocalClass(name);
}
Gets a resource for the bundle. First delegate.findResource(name) is
called. The delegate will query the system class loader, bundle imports,
bundle local resources, bundle hosts and fragments. The delegate will
call BundleClassLoader.findLocalResource(name) to find a resource local
to this bundle.
Params: - name – The resource path to get.
Returns: The URL of the resource or null if it does not exist.
/**
* Gets a resource for the bundle. First delegate.findResource(name) is
* called. The delegate will query the system class loader, bundle imports,
* bundle local resources, bundle hosts and fragments. The delegate will
* call BundleClassLoader.findLocalResource(name) to find a resource local
* to this bundle.
* @param name The resource path to get.
* @return The URL of the resource or null if it does not exist.
*/
@Override
public URL getResource(String name) {
if (getDebug().DEBUG_LOADER) {
Debug.println("ModuleClassLoader[" + getBundleLoader() + "].getResource(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
URL url = getBundleLoader().findResource(name);
if (url != null)
return (url);
if (getDebug().DEBUG_LOADER) {
Debug.println("ModuleClassLoader[" + getBundleLoader() + "].getResource(" + name + ") failed."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
return (null);
}
// preparing for Java 9
protected URL findResource(String moduleName, String name) {
return findLocalResource(name);
}
@Override
protected URL findResource(String name) {
return findLocalResource(name);
}
Gets resources for the bundle. First delegate.findResources(name) is
called. The delegate will query the system class loader, bundle imports,
bundle local resources, bundle hosts and fragments. The delegate will
call BundleClassLoader.findLocalResources(name) to find a resource local
to this bundle.
Params: - name – The resource path to get.
Returns: The Enumeration of the resource URLs.
/**
* Gets resources for the bundle. First delegate.findResources(name) is
* called. The delegate will query the system class loader, bundle imports,
* bundle local resources, bundle hosts and fragments. The delegate will
* call BundleClassLoader.findLocalResources(name) to find a resource local
* to this bundle.
* @param name The resource path to get.
* @return The Enumeration of the resource URLs.
*/
@Override
public Enumeration<URL> getResources(String name) throws IOException {
if (getDebug().DEBUG_LOADER) {
Debug.println("ModuleClassLoader[" + getBundleLoader() + "].getResources(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
Enumeration<URL> result = getBundleLoader().findResources(name);
if (getDebug().DEBUG_LOADER) {
if (result == null || !result.hasMoreElements()) {
Debug.println("ModuleClassLoader[" + getBundleLoader() + "].getResources(" + name + ") failed."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
return result;
}
@Override
protected Enumeration<URL> findResources(String name) throws IOException {
return findLocalResources(name);
}
Finds a library for this bundle. Simply calls
manager.findLibrary(libname) to find the library.
Params: - libname – The library to find.
Returns: The absolution path to the library or null if not found
/**
* Finds a library for this bundle. Simply calls
* manager.findLibrary(libname) to find the library.
* @param libname The library to find.
* @return The absolution path to the library or null if not found
*/
@Override
protected String findLibrary(String libname) {
// let the manager find the library for us
return getClasspathManager().findLibrary(libname);
}
public ClasspathEntry createClassPathEntry(BundleFile bundlefile, Generation entryGeneration) {
return new ClasspathEntry(bundlefile, createProtectionDomain(bundlefile, entryGeneration), entryGeneration);
}
public DefineClassResult defineClass(String name, byte[] classbytes, ClasspathEntry classpathEntry) {
// Note that we must check findLoadedClass again here since no locks are held between
// calling findLoadedClass the first time and defineClass.
// This is to allow weavers to get called while holding no locks.
// See ClasspathManager.findLocalClass(String)
boolean defined = false;
Class<?> result = null;
if (isRegisteredAsParallel()) {
// lock by class name in this case
boolean initialLock = lockClassName(name);
try {
result = findLoadedClass(name);
if (result == null) {
result = defineClass(name, classbytes, 0, classbytes.length, classpathEntry.getDomain());
defined = true;
}
} finally {
if (initialLock) {
unlockClassName(name);
}
}
} else {
// lock by class loader instance in this case
synchronized (this) {
result = findLoadedClass(name);
if (result == null) {
result = defineClass(name, classbytes, 0, classbytes.length, classpathEntry.getDomain());
defined = true;
}
}
}
return new DefineClassResult(result, defined);
}
public Class<?> publicFindLoaded(String classname) {
if (isRegisteredAsParallel()) {
return findLoadedClass(classname);
}
synchronized (this) {
return findLoadedClass(classname);
}
}
public Package publicGetPackage(String pkgname) {
synchronized (pkgLock) {
return getPackage(pkgname);
}
}
public Package publicDefinePackage(String name, String specTitle, String specVersion, String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase) {
synchronized (pkgLock) {
Package pkg = getPackage(name);
return pkg != null ? pkg : definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
}
}
public URL findLocalResource(String resource) {
return getClasspathManager().findLocalResource(resource);
}
public Enumeration<URL> findLocalResources(String resource) {
return getClasspathManager().findLocalResources(resource);
}
public Class<?> findLocalClass(String classname) throws ClassNotFoundException {
return getClasspathManager().findLocalClass(classname);
}
Creates a ProtectionDomain which uses specified BundleFile and the permissions of the baseDomain
Params: - bundlefile – The source bundlefile the domain is for.
- domainGeneration – the source generation for the domain
Returns: a ProtectionDomain which uses specified BundleFile and the permissions of the baseDomain
/**
* Creates a ProtectionDomain which uses specified BundleFile and the permissions of the baseDomain
* @param bundlefile The source bundlefile the domain is for.
* @param domainGeneration the source generation for the domain
* @return a ProtectionDomain which uses specified BundleFile and the permissions of the baseDomain
*/
@SuppressWarnings("deprecation")
protected ProtectionDomain createProtectionDomain(BundleFile bundlefile, Generation domainGeneration) {
// create a protection domain which knows about the codesource for this classpath entry (bug 89904)
ProtectionDomain baseDomain = domainGeneration.getDomain();
try {
// use the permissions supplied by the domain passed in from the framework
PermissionCollection permissions;
if (baseDomain != null) {
permissions = baseDomain.getPermissions();
} else {
// no domain specified. Better use a collection that has all permissions
// this is done just incase someone sets the security manager later
permissions = ALLPERMISSIONS;
}
Certificate[] certs = null;
SignedContent signedContent = null;
if (bundlefile instanceof BundleFileWrapperChain) {
signedContent = ((BundleFileWrapperChain) bundlefile).getWrappedType(SignedContent.class);
}
if (getConfiguration().CLASS_CERTIFICATE && signedContent != null && signedContent.isSigned()) {
SignerInfo[] signers = signedContent.getSignerInfos();
if (signers.length > 0)
certs = signers[0].getCertificateChain();
}
File file = bundlefile.getBaseFile();
// Bug 477787: file will be null when the osgi.framework configuration property contains an invalid value.
return new GenerationProtectionDomain(file == null ? null : new CodeSource(file.toURL(), certs), permissions, getGeneration());
//return new ProtectionDomain(new CodeSource(bundlefile.getBaseFile().toURL(), certs), permissions);
} catch (MalformedURLException e) {
// Failed to create our own domain; just return the baseDomain
return baseDomain;
}
}
@Override
public Bundle getBundle() {
return getGeneration().getRevision().getBundle();
}
public List<URL> findEntries(String path, String filePattern, int options) {
return getClasspathManager().findEntries(path, filePattern, options);
}
public Collection<String> listResources(String path, String filePattern, int options) {
return getBundleLoader().listResources(path, filePattern, options);
}
public Collection<String> listLocalResources(String path, String filePattern, int options) {
return getClasspathManager().listLocalResources(path, filePattern, options);
}
@Override
public String toString() {
Bundle b = getBundle();
StringBuilder result = new StringBuilder(super.toString());
if (b == null)
return result.toString();
return result.append('[').append(b.getSymbolicName()).append(':').append(b.getVersion()).append("(id=").append(b.getBundleId()).append(")]").toString(); //$NON-NLS-1$//$NON-NLS-2$
}
public void loadFragments(Collection<ModuleRevision> fragments) {
getClasspathManager().loadFragments(fragments);
}
private boolean lockClassName(String classname) {
synchronized (classNameLocks) {
Object lockingThread = classNameLocks.get(classname);
Thread current = Thread.currentThread();
if (lockingThread == current)
return false;
boolean previousInterruption = Thread.interrupted();
try {
while (true) {
if (lockingThread == null) {
classNameLocks.put(classname, current);
return true;
}
classNameLocks.wait();
lockingThread = classNameLocks.get(classname);
}
} catch (InterruptedException e) {
previousInterruption = true;
// must not throw LinkageError or ClassNotFoundException here because that will cause all threads
// to fail to load the class (see bug 490902)
throw new Error("Interrupted while waiting for classname lock: " + classname, e); //$NON-NLS-1$
} finally {
if (previousInterruption) {
current.interrupt();
}
}
}
}
private void unlockClassName(String classname) {
synchronized (classNameLocks) {
classNameLocks.remove(classname);
classNameLocks.notifyAll();
}
}
public void close() {
getClasspathManager().close();
}
}