package org.eclipse.core.internal.boot; | |
/* | |
* Licensed Materials - Property of IBM, | |
* WebSphere Studio Workbench | |
* (c) Copyright IBM Corp 2000 | |
*/ | |
import java.net.*; | |
import java.util.*; | |
import java.io.*; | |
import java.security.CodeSource; | |
import java.security.ProtectionDomain; | |
import org.eclipse.core.boot.BootLoader; | |
public abstract class DelegatingURLClassLoader extends URLClassLoader { | |
// loader base | |
protected URL base; | |
// delegation chain | |
protected DelegateLoader[] imports = null; | |
// extra resource class loader | |
protected URLClassLoader resourceLoader = null; | |
// filter table | |
private Hashtable filterTable = new Hashtable(); | |
// development mode class path additions | |
public static String devClassPath = null; | |
// native library loading | |
private static String libPrefix; | |
private static String libSuffix; | |
private static final String WIN_LIBRARY_PREFIX = ""; | |
private static final String WIN_LIBRARY_SUFFIX = ".dll"; | |
private static final String UNIX_LIBRARY_PREFIX = "lib"; | |
private static final String UNIX_LIBRARY_SUFFIX = ".so"; | |
private Hashtable nativeLibs = new Hashtable(); | |
private static final int BUF_SIZE = 32768; | |
// control class load tracing | |
public static boolean DEBUG = false; | |
public static boolean DEBUG_SHOW_CREATE = true; | |
public static boolean DEBUG_SHOW_ACTIVATE = true; | |
public static boolean DEBUG_SHOW_ACTIONS = true; | |
public static boolean DEBUG_SHOW_SUCCESS = true; | |
public static boolean DEBUG_SHOW_FAILURE = true; | |
public static String[] DEBUG_FILTER_CLASS = new String[0]; | |
public static String[] DEBUG_FILTER_LOADER = new String[0]; | |
public static String[] DEBUG_FILTER_RESOURCE = new String[0]; | |
public static String[] DEBUG_FILTER_NATIVE = new String[0]; | |
// DelegateLoader. Represents a single class loader this loader delegates to. | |
protected static class DelegateLoader { | |
private DelegatingURLClassLoader loader; | |
private boolean isExported; | |
public DelegateLoader(DelegatingURLClassLoader loader, boolean isExported) { | |
this.loader = loader; | |
this.isExported = isExported; | |
} | |
public Class loadClass(String name, DelegatingURLClassLoader current, DelegatingURLClassLoader requestor, Vector seen) { | |
if (isExported || current == requestor) | |
return loader.loadClass(name, false, requestor, seen, false); | |
else | |
return null; | |
} | |
public URL findResource(String name, DelegatingURLClassLoader current, DelegatingURLClassLoader requestor, Vector seen) { | |
if (isExported || current == requestor) | |
return loader.findResource(name, requestor, seen); | |
else | |
return null; | |
} | |
public Enumeration findResources(String name, DelegatingURLClassLoader current, DelegatingURLClassLoader requestor, Vector seen) { | |
if (isExported || current == requestor) | |
return loader.findResources(name, requestor, seen); | |
else | |
return null; | |
} | |
} | |
// unchecked DelegatingLoaderException | |
protected static class DelegatingLoaderException extends RuntimeException { | |
Exception e = null; | |
public DelegatingLoaderException() { | |
super(); | |
} | |
public DelegatingLoaderException(String message) { | |
super(message); | |
} | |
public DelegatingLoaderException(String message, Exception e) { | |
super(message); | |
this.e = e; | |
} | |
public Throwable getException() { | |
return e; | |
} | |
} | |
public DelegatingURLClassLoader(URL[] codePath, URLContentFilter[] codeFilters, URL[] resourcePath, URLContentFilter[] resourceFilters, ClassLoader parent) { | |
super(codePath, parent); | |
initialize(); | |
if (resourcePath != null) | |
resourceLoader = new ResourceLoader(resourcePath); | |
if (codePath != null) { | |
if (codeFilters == null || codeFilters.length != codePath.length) | |
throw new DelegatingLoaderException(); | |
for (int i = 0; i < codePath.length; i++) { | |
if (codeFilters[i] != null) | |
filterTable.put(codePath[i], codeFilters[i]); | |
} | |
} | |
if (resourcePath != null) { | |
if (resourceFilters == null || resourceFilters.length != resourcePath.length) | |
throw new DelegatingLoaderException(); | |
for (int i = 0; i < resourcePath.length; i++) { | |
if (resourceFilters[i] != null) | |
filterTable.put(resourcePath[i], resourceFilters[i]); | |
} | |
} | |
} | |
/** | |
* Returns the given class or <code>null</code> if the class is not visible to the | |
* given requestor. The <code>inCache</code> flag controls how this action is | |
* reported if in debug mode. | |
*/ | |
protected Class checkClassVisibility(Class result, DelegatingURLClassLoader requestor, boolean inCache) { | |
if (result == null) | |
return null; | |
if (isClassVisible(result, requestor)) { | |
if (DEBUG && DEBUG_SHOW_SUCCESS && debugClass(result.getName())) | |
debug("found " + result.getName() + " in " + (inCache ? "cache" : getURLforClass(result).toExternalForm())); | |
} else { | |
if (DEBUG && DEBUG_SHOW_ACTIONS && debugClass(result.getName())) | |
debug("skip " + result.getName() + " in " + (inCache ? "cache" : getURLforClass(result).toExternalForm())); | |
return null; | |
} | |
return result; | |
} | |
/** | |
* Returns the given resource URL or <code>null</code> if the resource is not visible to the | |
* given requestor. | |
*/ | |
protected URL checkResourceVisibility(String name, URL result, DelegatingURLClassLoader requestor) { | |
if (result == null) | |
return null; | |
if (isResourceVisible(name, result, requestor)) { | |
if (DEBUG && DEBUG_SHOW_SUCCESS && debugResource(name)) | |
debug("found " + result); | |
} else { | |
if (DEBUG && DEBUG_SHOW_ACTIONS && debugResource(name)) | |
debug("skip " + result); | |
result = null; | |
} | |
return result; | |
} | |
protected void debug(String s) { | |
System.out.println(toString()+"^"+Integer.toHexString(Thread.currentThread().hashCode())+" "+s); | |
} | |
protected boolean debugClass(String name) { | |
if (debugLoader()) { | |
return debugMatchesFilter(name,DEBUG_FILTER_CLASS); | |
} | |
return false; | |
} | |
protected void debugConstruction() { | |
if (DEBUG && DEBUG_SHOW_CREATE && debugLoader()) { | |
URL[] urls = getURLs(); | |
debug("Class Loader Created"); | |
debug("> baseURL=" + base); | |
if (urls == null || urls.length == 0) | |
debug("> empty search path"); | |
else { | |
URLContentFilter filter; | |
for (int i = 0; i < urls.length; i++) { | |
debug("> searchURL=" + urls[i].toString()); | |
filter = (URLContentFilter) filterTable.get(urls[i]); | |
if (filter != null) | |
debug("> export=" + filter.toString()); | |
} | |
} | |
} | |
} | |
protected String debugId() { | |
return ""; | |
} | |
protected boolean debugLoader() { | |
return debugMatchesFilter(debugId(),DEBUG_FILTER_LOADER); | |
} | |
private boolean debugMatchesFilter(String name, String[] filter) { | |
if (filter.length==0) return false; | |
for (int i=0; i<filter.length; i++) { | |
if (filter[i].equals("*")) return true; | |
if (name.startsWith(filter[i])) return true; | |
} | |
return false; | |
} | |
protected boolean debugNative(String name) { | |
if (debugLoader()) { | |
return debugMatchesFilter(name,DEBUG_FILTER_NATIVE); | |
} | |
return false; | |
} | |
protected boolean debugResource(String name) { | |
if (debugLoader()) { | |
return debugMatchesFilter(name,DEBUG_FILTER_RESOURCE); | |
} | |
return false; | |
} | |
/** | |
* Looks for the requested class in the parent of this loader using | |
* standard Java protocols. If the parent is null then the system class | |
* loader is consulted. <code>null</code> is returned if the class could | |
* not be found. | |
*/ | |
protected Class findClassParents(String name, boolean resolve) { | |
try { | |
ClassLoader parent = getParent(); | |
if (parent == null) | |
return findSystemClass(name); | |
return parent.loadClass(name); | |
} catch (ClassNotFoundException e) { | |
} | |
return null; | |
} | |
/** | |
* Finds and loads the class with the specified name from the URL search | |
* path. Any URLs referring to JAR files are loaded and opened as needed | |
* until the class is found. Search on the parent chain and then self. | |
* | |
* Subclasses should implement this method. | |
* | |
* @param name the name of the class | |
* @param resolve whether or not to resolve the class if found | |
* @param requestor class loader originating the request | |
* @param checkParents whether the parent of this loader should be consulted | |
* @return the resulting class | |
*/ | |
protected abstract Class findClassParentsSelf(final String name, boolean resolve, DelegatingURLClassLoader requestor, boolean checkParents); | |
/** | |
* Finds and loads the class with the specified name from the URL search | |
* path. Any URLs referring to JAR files are loaded and opened as needed | |
* until the class is found. This method consults only the platform class loader. | |
* | |
* @param name the name of the class | |
* @param resolve whether or not to resolve the class if found | |
* @param requestor class loader originating the request | |
* @param checkParents whether the parent of this loader should be consulted | |
* @return the resulting class | |
*/ | |
protected Class findClassPlatform(String name, boolean resolve, DelegatingURLClassLoader requestor, boolean checkParents) { | |
DelegatingURLClassLoader platform = PlatformClassLoader.getDefault(); | |
if (this == platform) | |
return null; | |
return platform.findClassParentsSelf(name, resolve, requestor, false); | |
} | |
/** | |
* Finds and loads the class with the specified name from the URL search | |
* path. Any URLs referring to JAR files are loaded and opened as needed | |
* until the class is found. This method considers only the classes loadable | |
* by its explicit prerequisite loaders. | |
* | |
* @param name the name of the class | |
* @param requestor class loader originating the request | |
* @param seen list of delegated class loaders already searched | |
* @return the resulting class | |
*/ | |
protected Class findClassPrerequisites(final String name, DelegatingURLClassLoader requestor, Vector seen) { | |
if (imports == null) | |
return null; | |
if (seen == null) | |
seen = new Vector(); // guard against delegation loops | |
seen.addElement(this); | |
// Grab onto the imports value to protect against concurrent write. | |
DelegateLoader[] loaders = imports; | |
for (int i = 0; i < loaders.length; i++) { | |
Class result = loaders[i].loadClass(name, this, requestor, seen); | |
if (result != null) | |
return result; | |
} | |
return null; | |
} | |
/** | |
* Finds the resource with the specified name on the URL search path. | |
* This method is used specifically to find the file containing a class to verify | |
* that the class exists without having to load it. | |
* Returns a URL for the resource. Searches only this loader's classpath. | |
* <code>null</code> is returned if the resource cannot be found. | |
* | |
* @param name the name of the resource | |
*/ | |
protected URL findClassResource(String name) { | |
return super.findResource(name); | |
} | |
/** | |
* Returns the absolute path name of a native library. The VM | |
* invokes this method to locate the native libraries that belong | |
* to classes loaded with this class loader. If this method returns | |
* <code>null</code>, the VM searches the library along the path | |
* specified as the <code>java.library.path</code> property. | |
* | |
* @param libname the library name | |
* @return the absolute path of the native library | |
*/ | |
protected String findLibrary(String libname) { | |
if (DEBUG && DEBUG_SHOW_ACTIONS && debugNative(libname)) | |
debug("findLibrary(" + libname + ")"); | |
if (base == null) | |
return null; | |
File libFile = null; | |
String osLibFileName = libPrefix + libname + libSuffix; | |
if (base.getProtocol().equals(PlatformURLHandler.FILE) || base.getProtocol().equals(PlatformURLHandler.VA)) { | |
// directly access library | |
String libFileName = (base.getFile() + osLibFileName).replace('/', File.separatorChar); | |
libFile = new File(libFileName); | |
} else | |
if (base.getProtocol().equals(PlatformURLHandler.PROTOCOL)) | |
// access library through eclipse URL | |
libFile = getNativeLibraryAsLocal(osLibFileName); | |
if (libFile == null) | |
return null; | |
if (!libFile.exists()) { | |
if (DEBUG && DEBUG_SHOW_FAILURE && debugNative(libname)) | |
debug("not found " + libname); | |
return null; // can't find the file | |
} | |
if (DEBUG && DEBUG_SHOW_SUCCESS && debugNative(libname)) | |
debug("found " + libname + " as " + libFile.getAbsolutePath()); | |
return libFile.getAbsolutePath(); | |
} | |
/** | |
* Finds the resource with the specified name on the URL search path. | |
* Returns a URL for the resource. If resource is not found in own | |
* URL search path, delegates search to prerequisite loaders. | |
* Null is returned if none of the loaders find the resource. | |
* | |
* @param name the name of the resource | |
*/ | |
public URL findResource(String name) { | |
return findResource(name, this, null); | |
} | |
/** | |
* Delegated resource access call. | |
* Does not check prerequisite loader parent chain. | |
*/ | |
protected URL findResource(String name, DelegatingURLClassLoader requestor, Vector seen) { | |
// guard against delegation loops | |
if (seen != null && seen.contains(this)) | |
return null; | |
if (DEBUG && DEBUG_SHOW_ACTIONS && debugResource(name)) | |
debug("findResource(" + name + ")"); | |
// check the normal class path for self | |
URL result = super.findResource(name); | |
result = checkResourceVisibility(name, result, requestor); | |
if (result != null) | |
return result; | |
// check our extra resource path if any | |
if (resourceLoader != null) { | |
result = resourceLoader.findResource(name); | |
result = checkResourceVisibility(name, result, requestor); | |
if (result != null) | |
return result; | |
} | |
// delegate down the prerequisite chain if we haven't found anything yet. | |
if (imports != null) { | |
if (seen == null) | |
seen = new Vector(); // guard against delegation loops | |
seen.addElement(this); | |
for (int i = 0; i < imports.length && result == null; i++) | |
result = imports[i].findResource(name, this, requestor, seen); | |
} | |
return result; | |
} | |
/** | |
* Returns an Enumeration of URLs representing all of the resources | |
* on the URL search path having the specified name. | |
* | |
* @param name the resource name | |
*/ | |
public Enumeration findResources(String name) throws IOException { | |
return findResources(name, this, null); | |
} | |
/** | |
* Delegated call to locate all named resources. | |
* Does not check prerequisite loader parent chain. | |
*/ | |
private Enumeration findResources(String name, DelegatingURLClassLoader requestor, Vector seen) { | |
// guard against delegation loops | |
if (seen != null && seen.contains(this)) | |
return null; | |
if (DEBUG && DEBUG_SHOW_ACTIONS && debugResource(name)) | |
debug("findResources(" + name + ")"); | |
// check own URL search path | |
Enumeration e = null; | |
try { | |
e = super.findResources(name); | |
} catch (IOException ioe) { | |
} | |
ResourceEnumeration result = new ResourceEnumeration(name, e, this, requestor); | |
// delegate down the prerequisite chain | |
if (imports != null) { | |
if (seen == null) | |
seen = new Vector(); // guard against delegation loops | |
seen.addElement(this); | |
for (int i = 0; i < imports.length; i++) | |
result.add(imports[i].findResources(name, this, requestor, seen)); | |
} | |
return result; | |
} | |
private File getNativeLibraryAsLocal(String osname) { | |
File result = null; | |
try { | |
URL liburl = new URL(base, osname); | |
PlatformURLConnection c = (PlatformURLConnection) liburl.openConnection(); | |
URL localName = c.getURLAsLocal(); | |
result = new File(localName.getFile()); | |
} catch (IOException e) { | |
} | |
return result; | |
} | |
public URL getResource(String name) { | |
if (DEBUG && DEBUG_SHOW_ACTIONS && debugResource(name)) | |
debug("getResource(" + name + ")"); | |
URL result = super.getResource(name); | |
if (result == null) { | |
if (DEBUG && DEBUG_SHOW_FAILURE && debugResource(name)) | |
debug("not found " + name); | |
} | |
return result; | |
} | |
private URL getURLforClass(Class clazz) { | |
ProtectionDomain pd = clazz.getProtectionDomain(); | |
if (pd != null) { | |
CodeSource cs = pd.getCodeSource(); | |
if (cs != null) | |
return cs.getLocation(); | |
} | |
if (DEBUG && DEBUG_SHOW_ACTIONS && debugClass(clazz.getName())) | |
debug("*** " + clazz.getName()); | |
return null; | |
} | |
private void initialize() { | |
if (BootLoader.getOS().equals(BootLoader.OS_WIN32)) { | |
libPrefix = WIN_LIBRARY_PREFIX; | |
libSuffix = WIN_LIBRARY_SUFFIX; | |
} else { | |
libPrefix = UNIX_LIBRARY_PREFIX; | |
libSuffix = UNIX_LIBRARY_SUFFIX; | |
} | |
} | |
public void initializeImportedLoaders() { | |
} | |
/** | |
* check to see if class is visible (exported) | |
*/ | |
boolean isClassVisible(Class clazz, DelegatingURLClassLoader requestor) { | |
URL lib = getURLforClass(clazz); | |
if (lib == null) | |
return true; // have a system class (see comment below) | |
URLContentFilter filter = (URLContentFilter) filterTable.get(lib); | |
if (filter == null) { | |
// This code path is being executed because some VMs (eg. Sun JVM) | |
// return from the class cache classes that were not loaded | |
// by this class loader. Consequently we do not find the | |
// corresponding jar filter. This appears to be a performance | |
// optimization that we are defeating with our filtering scheme. | |
// We return the class if it is a system class (see above). Otherwise | |
// we reject the class which caused the load to be | |
// delegated down the prerequisite chain until we find the | |
// correct loader. | |
if (DEBUG && DEBUG_SHOW_ACTIONS && debugClass(clazz.getName())) | |
debug("*** Unable to find library filter for " + clazz.getName() + " from " + lib); | |
return false; | |
} else | |
return filter.isClassVisible(clazz, this, requestor); | |
} | |
/** | |
* check to see if resource is visible (exported) | |
*/ | |
boolean isResourceVisible(String name, URL resource, DelegatingURLClassLoader requestor) { | |
URL lib = null; | |
String file = resource.getFile(); | |
try { | |
lib = new URL(resource.getProtocol(), resource.getHost(), file.substring(0, file.length() - name.length())); | |
} catch (MalformedURLException e) { | |
if (DEBUG) | |
debug("Unable to determine resource lib for " + name + " from " + resource); | |
return false; | |
} | |
URLContentFilter filter = (URLContentFilter) filterTable.get(lib); | |
if (filter == null) { | |
if (DEBUG) | |
debug("Unable to find library filter for " + name + " from " + lib); | |
return false; | |
} else | |
return filter.isResourceVisible(name, this, requestor); | |
} | |
/** | |
* Non-delegated load call. This method is not synchronized. Implementations of | |
* findClassParentsSelf, and perhaps others, should synchronize themselves as | |
* required. Synchronizing this method is too coarse-grained. It results in plugin | |
* activation being synchronized and may cause deadlock. | |
*/ | |
protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { | |
if (DEBUG && DEBUG_SHOW_ACTIONS && debugClass(name)) | |
debug("loadClass(" + name + ")"); | |
Class result = loadClass(name, resolve, this, null, true); | |
if (result == null) { | |
if (DEBUG && DEBUG_SHOW_FAILURE && debugClass(name)) | |
debug("not found " + name); | |
throw new ClassNotFoundException(name); | |
} | |
return result; | |
} | |
/** | |
* Delegated load call. This method is not synchronized. Implementations of | |
* findClassParentsSelf, and perhaps others, should synchronize themselves as | |
* required. Synchronizing this method is too coarse-grained. It results in plugin | |
* activation being synchronized and may cause deadlock. | |
*/ | |
private Class loadClass(String name, boolean resolve, DelegatingURLClassLoader requestor, Vector seen, boolean checkParents) { | |
// guard against delegation loops | |
if (seen != null && seen.contains(this)) | |
return null; | |
// look in the parents and self | |
Class result = findClassParentsSelf(name, resolve, requestor, checkParents); | |
// search platform | |
if (result == null) | |
result = findClassPlatform(name, resolve, requestor, false); | |
// search prerequisites | |
if (result == null) | |
result = findClassPrerequisites(name, requestor, seen); | |
// if we found a class, consider resolving it | |
if (result != null && resolve) | |
resolveClass(result); | |
return result; | |
} | |
protected void setImportedLoaders(DelegateLoader[] loaders) { | |
imports = loaders; | |
if(DEBUG && DEBUG_SHOW_CREATE && debugLoader()) { | |
debug("Imports"); | |
if (imports==null || imports.length==0) debug("> none"); | |
else { | |
for (int i=0; i<imports.length; i++) { | |
debug("> " + imports[i].loader.toString() + " export=" + imports[i].isExported); | |
} | |
} | |
} | |
} | |
public String toString() { | |
return "Loader [" + debugId() + "]"; | |
} | |
} |