blob: 937e57072225c73fa34be25c91cb9e7168a28675 [file] [log] [blame]
package org.eclipse.core.internal.plugins;
/*
* Licensed Materials - Property of IBM,
* WebSphere Studio Workbench
* (c) Copyright IBM Corp 2000
*/
import org.eclipse.core.runtime.*;
import org.eclipse.core.internal.boot.*;
import java.io.File;
import java.util.*;
import java.net.URL;
/**
* Plugin class loader.
*
* Handle loading of classes from a plugin. Configures load path based
* on the plugin <runtime> specification. Configures delegate loaders
* based on the <requires> specification. Configures
* the parent loader to be the default instance of
* PlatformClassLoader (assumes to be initialized by this point).
*
*/
public final class PluginClassLoader extends DelegatingURLClassLoader {
private PluginDescriptor descriptor;
private boolean pluginActivationInProgress = false;
private boolean loadInProgress = false;
public PluginClassLoader(URL[] codePath, URLContentFilter[] codeFilters, URL[] resourcePath, URLContentFilter[] resourceFilters, PlatformClassLoader parent, PluginDescriptor descriptor) {
// create a class loader with the given classpath and filters. Also, set the parent
// to be the parent of the platform class loader. This allows us to decouple standard
// parent loading from platform loading.
super(codePath, codeFilters, resourcePath, resourceFilters, parent.getParent());
this.descriptor = descriptor;
base = descriptor.getInstallURL();
debugConstruction(); // must have initialized loader
// Note: initializeImportedLoaders() is called by PluginDescriptor.getPluginClassLoader().
// The split between construction and initialization is needed
// to correctly handle the case where the user defined loops in
// the prerequisite definitions.
}
protected void activatePlugin(String name) {
try {
// pluginActivationInProgress = true;
// the in-progress flag is set when we detect that activation will be required.
// be sure to unset it here.
if (DEBUG && DEBUG_SHOW_ACTIVATE && debugLoader())
debug("Attempting to activate " + descriptor.getUniqueIdentifier());
descriptor.doPluginActivation();
} catch (CoreException e) {
if (DEBUG && DEBUG_SHOW_ACTIVATE && debugLoader())
debug("Activation failed for " + descriptor.getUniqueIdentifier());
throw new DelegatingLoaderException("Plugin " + descriptor.getUniqueIdentifier() + " activation failed while loading class " + name, e);
} finally {
if (DEBUG && DEBUG_SHOW_ACTIVATE && debugLoader())
debug("Exit activation for " + descriptor.getUniqueIdentifier());
pluginActivationInProgress = false;
}
}
public String debugId() {
return descriptor.toString();
}
/**
* 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 Class findClassParentsSelf(final String name, boolean resolve, DelegatingURLClassLoader requestor, boolean checkParents) {
Class result = null;
synchronized (this) {
// check the cache. If we find something, check to see if its visible.
// If it is, return it. If not, return null if we are not checking parents. There is
// no point in looking in self as the class was already in the cache.
result = findLoadedClass(name);
if (result != null) {
result = checkClassVisibility(result, requestor, true);
if (result != null || !checkParents)
return result;
}
// if it wasn't in the cache or was not visible, check the parents (if requested)
if (checkParents) {
result = findClassParents(name, resolve);
if (result != null)
return result;
}
// if activation is not going to be required, try the load here. This is
// a short circuit so we don't fall through to the other sync block and do
// more work.
if (descriptor.isPluginActivated() || pluginActivationInProgress) {
try {
result = super.findClass(name);
} catch (ClassNotFoundException e) {
return null;
}
return checkClassVisibility(result, requestor, false);
}
// Check to see if we would find the class if we looked. If so,
// activation is required. If not, don't bother, just return null
if (shouldLookForClass(name))
// leave a dropping to discourage others from trying to do activation.
// This flag will be cleared once activation is complete.
pluginActivationInProgress = true;
else
return null;
}
// If we will find the class and the plugin is not yet activated, go ahead and do it now.
// Note that this MUST be done outside the sync block to avoid deadlock if
// plugin activation forks threads etc.
activatePlugin(name);
// By now the plugin is activated and we need to sycn and retry the
// class load.
synchronized (this) {
result = findLoadedClass(name);
if (result != null)
return checkClassVisibility(result, requestor, true);
// do search/load in this class loader
try {
result = super.findClass(name);
return checkClassVisibility(result, requestor, false);
} catch (ClassNotFoundException e) {
return null;
}
}
}
public PluginDescriptor getPluginDescriptor() {
return descriptor;
}
public void initializeImportedLoaders() {
PluginDescriptor desc = getPluginDescriptor();
IPluginPrerequisite[] prereqs = desc.getPluginPrerequisites();
if (prereqs.length == 0)
return;
PluginRegistry registry = desc.getPluginRegistry();
ArrayList require = new ArrayList();
for (int i = 0; i < prereqs.length; i++) {
desc = (PluginDescriptor) registry.getPluginDescriptor(prereqs[i].getUniqueIdentifier(), prereqs[i].getResolvedVersionIdentifier());
// can be null if the prereq was optional and did not exst.
if (desc != null)
require.add(new DelegateLoader((DelegatingURLClassLoader) desc.getPluginClassLoader(true), prereqs[i].isExported()));
}
if (require.isEmpty())
return;
setImportedLoaders((DelegateLoader[]) require.toArray(new DelegateLoader[require.size()]));
}
public void setPluginDescriptor(PluginDescriptor value) {
descriptor = value;
}
protected boolean shouldLookForClass(String name) {
// check if requested class is in loader search path
// Note: this check is suboptimal. It results in additional
// loader overhead until the plugin is activated. The reason
// the check is performed here is because
// (1) plug-in activation needs to be performed prior to
// the requested class load (done in findClass(String))
// (2) the check cannot be added to the "right" spot inside private
// implementation of URLClassLoader
String resource = name.replace('.', '/');
if (findClassResource(resource + ".class") == null)
return false;
// check if plugin is permanently deactivated
if (descriptor.isPluginDeactivated())
throw new DelegatingLoaderException("Attempt to load class " + name + " from deactivated plug-in " + descriptor.getUniqueIdentifier());
return true;
}
}