blob: fbf7815197900c56b99cf8e2ecc560c33bd3593f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.plugins;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.internal.boot.*;
import org.eclipse.core.internal.runtime.Policy;
import org.eclipse.core.runtime.*;
/**
* 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 static boolean usePackagePrefixes = true;
public PluginClassLoader(URL[] codePath, URLContentFilter[] codeFilters, URL[] resourcePath, URLContentFilter[] resourceFilters, ClassLoader parent, PluginDescriptor descriptor) {
// create a class loader with the given classpath and filters. Also, the parent
// should 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);
this.descriptor = descriptor;
this.base = descriptor.getInstallURL();
this.prefixes = initializePrefixes(descriptor, getPrefixId());
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.
}
public static String[] initializePrefixes(IPluginDescriptor descriptor, String prefixId) {
// use the classloader.properties file as an over-ride to what
// appears in the plugin.xml
if (InternalBootLoader.useClassLoaderProperties()) {
String list = (String) prefixTable.get(prefixId);
if (list == null)
return null;
// if there is an entry in the file but no value then treat that as
// a "don't use any package prefixes"
if (list.trim().length() == 0) {
if (DEBUG_PROPERTIES)
System.out.println("Clearing prefixes for: " + descriptor.getUniqueIdentifier()); //$NON-NLS-1$
return null;
}
if (DEBUG_PROPERTIES)
System.out.println("Using prefixes for " + descriptor.getUniqueIdentifier() + " from classloader.properties: " + list); //$NON-NLS-1$ //$NON-NLS-2$
return getArrayFromList(list);
}
// setup the package prefixes to use
if (usePackagePrefixes) {
if (DEBUG_PACKAGE_PREFIXES)
System.out.println("Reading package prefixes for plug-in: " + descriptor.getUniqueIdentifier()); //$NON-NLS-1$
// collect all of the package prefixes for all of the runtime entries in the plugin.xml
Set set = new HashSet(5);
ILibrary[] libraries = descriptor.getRuntimeLibraries();
for (int i = 0; libraries != null && i < libraries.length; i++) {
String[] entries = libraries[i].getPackagePrefixes();
for (int j=0; entries != null && j < entries.length; j++)
set.add(entries[j]);
}
String[] prefixes = null;
if (set.size() != 0)
prefixes = (String[]) set.toArray(new String[set.size()]);
if (DEBUG_PACKAGE_PREFIXES) {
String list = arrayToString(prefixes);
System.out.println("Using the following prefixes: " + list); //$NON-NLS-1$
}
return prefixes;
}
return null;
}
static String arrayToString(String[] array) {
if (array == null)
return null;
StringBuffer buffer = new StringBuffer();
for (int i=0; i<array.length; i++) {
buffer.append(array[i]);
if (i != array.length - 1)
buffer.append(',');
}
return buffer.toString();
}
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()); //$NON-NLS-1$
descriptor.doPluginActivation();
} catch (CoreException e) {
if (DEBUG && DEBUG_SHOW_ACTIVATE && debugLoader())
debug("Activation failed for " + descriptor.getUniqueIdentifier()); //$NON-NLS-1$
throw new DelegatingLoaderException(Policy.bind("plugin.delegatingLoaderTrouble", descriptor.getUniqueIdentifier(), name), e); //$NON-NLS-1$
} finally {
if (DEBUG && DEBUG_SHOW_ACTIVATE && debugLoader())
debug("Exit activation for " + descriptor.getUniqueIdentifier()); //$NON-NLS-1$
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 internalFindClassParentsSelf(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. Note that the order of the tests is important, since
//descriptor.isPluginActivated() blocks while activation in progress,
//thus creating a potential deadlock situation.
if (pluginActivationInProgress || descriptor.isPluginActivated()) {
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);
// If the class is loaded in this classloader register it with
// the hot swap support. Need to do this regardless of visibility
// because the class was actually loaded.
if (result == null)
return null;
return checkClassVisibility(result, requestor, false);
} catch (ClassNotFoundException e) {
return null;
}
}
}
public PluginDescriptor getPluginDescriptor() {
return descriptor;
}
/**
* Returns the id to use to lookup class prefixes for this loader
*/
public String getPrefixId() {
return descriptor.getUniqueIdentifier();
}
/**
* Initializes imported classloaders with the classloaders of all resolved pre-
* requisites.
*/
public void initializeImportedLoaders() {
PluginDescriptor desc = getPluginDescriptor();
// prereqs will contain all *resolved* pre-requisites (except boot and runtime)
IPluginPrerequisite[] prereqs = desc.getPluginResolvedPrerequisites();
if (prereqs.length == 0)
return;
PluginRegistry registry = desc.getPluginRegistry();
DelegateLoader[] importedLoaders = new DelegateLoader[prereqs.length];
for (int i = 0; i < prereqs.length; i++) {
String prereqId = prereqs[i].getUniqueIdentifier();
desc = (PluginDescriptor) registry.getPluginDescriptor(prereqId, prereqs[i].getResolvedVersionIdentifier());
importedLoaders[i] = new DelegateLoader((DelegatingURLClassLoader) desc.getPluginClassLoader(true), prereqs[i].isExported());
}
setImportedLoaders(importedLoaders);
}
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) //$NON-NLS-1$
return false;
// check if plugin is permanently deactivated
if (descriptor.isPluginDeactivated()) {
String message = Policy.bind("plugin.deactivatedLoad", name, descriptor.getUniqueIdentifier()); //$NON-NLS-1$
throw new DelegatingLoaderException(message);
}
return true;
}
protected String getClassloaderId() {
return descriptor.getId();
}
}