blob: c567961996421f9ae7c30457888caf6cd7130e9d [file] [log] [blame]
package org.eclipse.core.internal.runtime;
/*
* Licensed Materials - Property of IBM,
* WebSphere Studio Workbench
* (c) Copyright IBM Corp 2000
*/
import org.eclipse.core.boot.BootLoader;
import org.eclipse.core.boot.IPlatformRunnable;
import org.eclipse.core.internal.boot.*;
import org.eclipse.core.runtime.model.*;
import org.eclipse.core.internal.boot.PlatformURLHandlerFactory;
import org.eclipse.core.internal.boot.InternalBootLoader;
import org.eclipse.core.runtime.*;
import org.eclipse.core.internal.runtime.*;
import org.eclipse.core.internal.plugins.*;
import java.io.*;
import java.net.*;
import java.util.*;
/**
* Bootstrap class for the platform. It is responsible for setting up the
* platform class loader and passing control to the actual application class
*/
public final class InternalPlatform {
private static IAdapterManager adapterManager;
private static PluginRegistry registry;
private static Set logListeners = new HashSet(5);
private static Map logs = new HashMap(5);
private static PlatformLogListener platformLog = null;
private static PlatformMetaArea metaArea;
private static boolean initialized;
private static IPath location;
private static PluginClassLoader xmlClassLoader = null;
private static boolean debugEnabled = false;
private static boolean consoleLogEnabled = false;
private static ILogListener consoleLog = null;
private static Properties options = null;
private static AuthorizationDatabase keyring = null;
private static String keyringFile = null;
private static String password = "";
private static boolean inDevelopmentMode = false;
// default plugin data
private static final String PI_XML = "org.apache.xerces";
private static final String XML_VERSION = "1.2.1";
private static final String XML_JAR = "xerces.jar";
private static final String XML_LOCATION = "plugins/" + PI_XML + "/";
// execution options
private static final String OPTION_DEBUG = Platform.PI_RUNTIME + "/debug";
private static final String OPTION_DEBUG_PLUGINS = Platform.PI_RUNTIME + "/registry/debug";
// command line options
private static final String LOG = "-consolelog";
private static final String KEYRING = "-keyring";
private static final String PASSWORD = "-password";
private static final String DEV = "-dev";
// debug support: set in loadOptions()
public static boolean DEBUG = false;
public static boolean DEBUG_PLUGINS = false;
private static boolean inVAJ;
static {
try {
Class.forName("com.ibm.uvm.lang.ProjectClassLoader");
inVAJ = true;
} catch (Exception e) {
inVAJ = false;
}
}
private static boolean inVAME;
static {
try {
Class.forName("com.ibm.eclipse.core.VAME");
inVAME = true;
} catch (Exception e) {
inVAME = false;
}
}
/**
* Private constructor to block instance creation.
*/
private InternalPlatform() {
}
/**
* The runtime plug-in is not totally real due to bootstrapping problems.
* This method builds the required constructs to activate and install
* the runtime plug-in.
*/
private static void activateDefaultPlugins() throws CoreException {
// for now, simply do the default activation. This does not do the right thing
// wrt the plugin class loader.
PluginDescriptor descriptor = (PluginDescriptor) registry.getPluginDescriptor(Platform.PI_RUNTIME);
descriptor.setPluginClassLoader(PlatformClassLoader.getDefault());
descriptor.getPlugin();
descriptor = (PluginDescriptor) registry.getPluginDescriptor(PI_XML);
descriptor.setPluginClassLoader(xmlClassLoader);
xmlClassLoader.setPluginDescriptor(descriptor);
descriptor.getPlugin();
}
/**
* @see Platform
*/
public static void addAuthorizationInfo(URL serverUrl, String realm, String authScheme, Map info) throws CoreException {
keyring.addAuthorizationInfo(serverUrl, realm, authScheme, new HashMap(info));
keyring.save();
}
/**
* @see Platform#addLogListener
*/
public static void addLogListener(ILogListener listener) {
assertInitialized();
synchronized (logListeners) {
logListeners.add(listener);
}
}
/**
* @see Platform
*/
public static void addProtectionSpace(URL resourceUrl, String realm) throws CoreException {
keyring.addProtectionSpace(resourceUrl, realm);
keyring.save();
}
/**
* @see Platform
*/
public static URL asLocalURL(URL url) throws IOException {
URLConnection connection = url.openConnection();
if (!(connection instanceof PlatformURLConnection))
return url;
String file = connection.getURL().getFile();
if (file.endsWith("/") && !file.endsWith(PlatformURLHandler.JAR_SEPARATOR))
throw new IOException();
return ((PlatformURLConnection) connection).getURLAsLocal();
}
private static void assertInitialized() {
Assert.isTrue(initialized, Policy.bind("appNotInit", new String[] {}));
}
/**
* Creates and remembers a spoofed up class loader which loads the
* classes from a predefined XML plugin.
*/
private static void createXMLClassLoader() {
// create a plugin descriptor which is sufficient to be able to create
// the class loader through the normal channels.
Factory factory = new InternalFactory(null);
PluginDescriptor descriptor = (PluginDescriptor) factory.createPluginDescriptor();
descriptor.setEnabled(true);
descriptor.setId(PI_XML);
descriptor.setVersion(XML_VERSION);
try {
descriptor.setLocation(new URL(BootLoader.getInstallURL(), XML_LOCATION).toExternalForm());
} catch (MalformedURLException e) {
// ISSUE: What to do when this fails. It's pretty serious
}
LibraryModel lib = factory.createLibrary();
lib.setName(XML_JAR);
lib.setExports(new String[] { "*" });
descriptor.setRuntime(new LibraryModel[] { lib });
// use the fake plugin descriptor to create the desired class loader.
// Since this class loader will be used before the plugin registry is installed,
// ensure that the URLs on its class path are raw as opposed to eclipse:
xmlClassLoader = (PluginClassLoader) descriptor.getPluginClassLoader(false);
}
/**
* @see Platform
*/
public static void flushAuthorizationInfo(URL serverUrl, String realm, String authScheme) throws CoreException {
keyring.flushAuthorizationInfo(serverUrl, realm, authScheme);
keyring.save();
}
/**
* @see Platform#getAdapterManager
*/
public static IAdapterManager getAdapterManager() {
assertInitialized();
return adapterManager;
}
/**
* @see Platform
*/
public static Map getAuthorizationInfo(URL serverUrl, String realm, String authScheme) {
Map info = keyring.getAuthorizationInfo(serverUrl, realm, authScheme);
return info == null ? null : new HashMap(info);
}
public static boolean getBooleanOption(String option, boolean defaultValue) {
String optionValue = options.getProperty(option);
return (optionValue != null && optionValue.equalsIgnoreCase("true")) || defaultValue;
}
/**
* @see Platform
*/
public static String getDebugOption(String option) {
return debugEnabled ? options.getProperty(option) : null;
}
public static int getIntegerOption(String option, int defaultValue) {
String value = getDebugOption(option);
try {
return value == null ? defaultValue : Integer.parseInt(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
* @see Platform#getLocation
*/
public static IPath getLocation() {
assertInitialized();
return location;
}
/**
* Returns a log for the given plugin or <code>null</code> if none exists.
*/
public static ILog getLog(Plugin plugin) {
ILog result = (ILog) logs.get(plugin);
if (result != null)
return result;
result = new Log(plugin);
logs.put(plugin, result);
return result;
}
/**
* Returns the object which defines the location and organization
* of the platform's meta area.
*/
public static PlatformMetaArea getMetaArea() {
return metaArea;
}
/**
* @see Platform#getPlugin
*/
public static Plugin getPlugin(String id) {
assertInitialized();
IPluginDescriptor descriptor = getPluginRegistry().getPluginDescriptor(id);
if (descriptor == null)
return null;
try {
return descriptor.getPlugin();
} catch (CoreException e) {
return null;
}
}
/**
* @see Platform#getPluginRegistry
*/
public static IPluginRegistry getPluginRegistry() {
assertInitialized();
return registry;
}
/**
* @see Platform#getPluginStateLocation
*/
public static IPath getPluginStateLocation(Plugin plugin) {
assertInitialized();
IPath result = metaArea.getPluginStateLocation(plugin);
result.toFile().mkdirs();
return result;
}
/**
* @see Platform
*/
public static String getProtectionSpace(URL resourceUrl) {
return keyring.getProtectionSpace(resourceUrl);
}
public static Plugin getRuntimePlugin() {
try {
return getPluginRegistry().getPluginDescriptor(Platform.PI_RUNTIME).getPlugin();
} catch (CoreException e) {
return null;
}
}
private static void handleException(ISafeRunnable code, Throwable e) {
String message = Policy.bind("pluginProblems", new String[0]);
IStatus status = new Status(Status.WARNING, Platform.PI_RUNTIME, Platform.PLUGIN_ERROR, message, e);
getRuntimePlugin().getLog().log(status);
code.handleException(e);
}
/**
* Returns true if the platform is currently running in Development Mode. If it is, there are
* special procedures that should be taken when defining plug-in class paths.
*/
public static boolean inDevelopmentMode() {
return inDevelopmentMode || inVAJ() || inVAME();
}
/**
* Returns true if the platform is currently running in VA/Java. If it is, there are
* typically some special procedures
* that should be taken when dealing with plug-in activation and class loading.
*/
public static boolean inVAJ() {
return inVAJ;
}
/**
* Returns true if the platform is currently running in VA/ME. If it is, there are
* typically some special procedures
* that should be taken when dealing with plug-in activation and class loading.
*/
public static boolean inVAME() {
return inVAME;
}
/**
* Internal method for finding and returning a runnable instance of the
* given class as defined in the specified plug-in.
* The returned object is initialized with the supplied arguments.
* <p>
* This method is used by the platform boot loader; is must
* not be called directly by client code.
* </p>
* @see BootLoader
*/
public static IPlatformRunnable loaderGetRunnable(String applicationName) {
assertInitialized();
IExtension extension = registry.getExtension(Platform.PI_RUNTIME, Platform.PT_APPLICATIONS, applicationName);
if (extension == null)
return null;
IConfigurationElement[] configs = extension.getConfigurationElements();
if (configs.length == 0)
return null;
try {
IConfigurationElement config = configs[0];
return (IPlatformRunnable) config.createExecutableExtension("run");
} catch (CoreException e) {
if (DEBUG)
getRuntimePlugin().getLog().log(e.getStatus());
return null;
}
}
/**
* Internal method for finding and returning a runnable instance of the
* given class as defined in the specified plug-in.
* The returned object is initialized with the supplied arguments.
* <p>
* This method is used by the platform boot loader; is must
* not be called directly by client code.
* </p>
* @see BootLoader
*/
public static IPlatformRunnable loaderGetRunnable(String pluginId, String className, Object args) {
assertInitialized();
PluginDescriptor descriptor = (PluginDescriptor) registry.getPluginDescriptor(pluginId);
try {
return (IPlatformRunnable) descriptor.createExecutableExtension(className, args, null, null);
} catch (CoreException e) {
if (DEBUG)
getRuntimePlugin().getLog().log(e.getStatus());
return null;
}
}
/**
* Internal method for shutting down the platform. All active plug-ins
* are notified of the impending shutdown.
* The exact order of notification is unspecified;
* however, each plug-in is assured that it will be told to shut down
* before any of its prerequisites. Plug-ins are expected to free any
* shared resources they manage. Plug-ins should not store state at
* this time; a separate <b>save</b> lifecycle event preceding the
* shutdown notice tells plug-ins the right time to be saving their state.
* <p>
* On exit, the platform will no longer be initialized and any objects derived from
* or based on the running platform are invalidated.
* </p>
* <p>
* This method is used by the platform boot loader; is must
* not be called directly by client code.
* </p>
* @see BootLoader
*/
public static void loaderShutdown() {
assertInitialized();
registry.shutdown(null);
if (platformLog != null)
platformLog.shutdown();
initialized = false;
}
/**
* Internal method for starting up the platform. The platform is started at the
* given location. The plug-ins found at the supplied
* collection of plug-in locations are loaded into the newly started platform.
* <p>
* This method is used by the platform boot loader; is must
* not be called directly by client code.
* </p>
* @param pluginPath the list of places to look for plug-in specifications. This may
* identify individual plug-in files or directories containing directories which contain
* plug-in files.
* @param location the local filesystem location at which the newly started platform
* should be started. If the location does not contain the saved state of a platform,
* the appropriate structures are created on disk (if required).
* @param bootOptions the debug options loaded by the boot loader. If the argument
* is <code>null</code> then debugging enablement was not requested by the
* person starting the platform.
* @see BootLoader
*/
public static void loaderStartup(URL[] pluginPath, String locationString, Properties bootOptions, String[] args) throws CoreException {
processCommandLine(args);
setupMetaArea(locationString);
adapterManager = new AdapterManager();
loadOptions(bootOptions);
createXMLClassLoader();
MultiStatus problems = loadRegistry(pluginPath);
initialized = true;
// can't register url handlers until after the plugin registry is loaded
PlatformURLPluginHandlerFactory.startup();
activateDefaultPlugins();
// can't install the log or log problems until after the platform has been initialized.
platformLog = new PlatformLogListener();
addLogListener(platformLog);
if (consoleLogEnabled) {
consoleLog = new PlatformLogListener(System.out);
addLogListener(consoleLog);
}
if (!problems.isOK())
getRuntimePlugin().getLog().log(problems);
loadKeyring();
}
/**
* Opens the password database (if any) initally provided to the platform at startup.
*/
private static void loadKeyring() {
if (keyringFile != null)
try {
keyring = new AuthorizationDatabase(keyringFile, password);
} catch (CoreException e) {
log(e.getStatus());
}
if (keyring == null)
keyring = new AuthorizationDatabase();
}
static void loadOptions(Properties bootOptions) {
// If the boot loader passed <code>null</code> for the boot options, the user
// did not specify debug options so no debugging should be enabled.
if (bootOptions == null) {
debugEnabled = false;
return;
}
debugEnabled = true;
options = new Properties(bootOptions);
try {
InputStream input = new FileInputStream(InternalPlatform.getMetaArea().getOptionsLocation().toFile());
try {
options.load(input);
} finally {
input.close();
}
} catch (FileNotFoundException e) {
// Its not an error to not find the options file
} catch (IOException e) {
// Platform.RuntimePlugin.getLog().log();
}
// trim off all the blanks since properties files don't do that.
for (Iterator i = options.keySet().iterator(); i.hasNext();) {
Object key = i.next();
options.put(key, ((String) options.get(key)).trim());
}
DEBUG = getBooleanOption(OPTION_DEBUG, false);
DEBUG_PLUGINS = getBooleanOption(OPTION_DEBUG_PLUGINS, false);
InternalBootLoader.setupOptions();
}
/**
* Parses, resolves and rememberhs the plugin registry. The multistatus returned
* details any problems/issues encountered during this process.
*/
private static MultiStatus loadRegistry(URL[] pluginPath) {
MultiStatus problems = new MultiStatus(Platform.PI_RUNTIME, Platform.PARSE_PROBLEM, Policy.bind("registryProblems", new String[0]), null);
InternalFactory factory = new InternalFactory(problems);
registry = (PluginRegistry) parsePlugins(pluginPath, factory, DEBUG && DEBUG_PLUGINS);
IStatus resolveStatus = registry.resolve(true, true);
problems.merge(resolveStatus);
registry.markReadOnly();
registry.startup(null);
return problems;
}
/**
* @see Platform#log
*/
public static void log(final IStatus status) {
assertInitialized();
// create array to avoid concurrent access
ILogListener[] listeners;
synchronized (logListeners) {
listeners = (ILogListener[]) logListeners.toArray(new ILogListener[logListeners.size()]);
}
for (int i = 0; i < listeners.length; i++) {
try {
listeners[i].logging(status, Platform.PI_RUNTIME);
} catch (Exception e) {
} // no chance of exceptions for log listeners
}
}
/**
* @see Platform#parsePlugins
*/
public static PluginRegistryModel parsePlugins(URL[] pluginPath, Factory factory) {
return parsePlugins(pluginPath, factory, false);
}
/**
* @see Platform#parsePlugins
*/
public synchronized static PluginRegistryModel parsePlugins(URL[] pluginPath, Factory factory, boolean debug) {
// If the platform is not running then simply parse the registry. We don't need to play
// any funny class loader games as we assume the XML classes are on the class path
// This happens when we are running this code as part of a utility (as opposed to starting
// or inside the platform).
if (!(InternalBootLoader.isRunning() || InternalBootLoader.isStarting()))
return RegistryLoader.parseRegistry(pluginPath, factory, debug);
// If we are running the platform, we want to conserve class loaders.
// Temporarily install the xml class loader as a prerequisite of the platform class loader
// This allows us to find the xml classes. Be sure to reset the prerequisites after loading.
PlatformClassLoader.getDefault().setImports(new DelegatingURLClassLoader[] { xmlClassLoader });
try {
return RegistryLoader.parseRegistry(pluginPath, factory, debug);
} finally {
PlatformClassLoader.getDefault().setImports(null);
}
}
private static void processCommandLine(String[] args) {
for (int i = 0; i < args.length; i++) {
// check for args without parameters (i.e., a flag arg)
// look for the log flag
if (args[i].equalsIgnoreCase(LOG)) {
consoleLogEnabled = true;
continue;
}
// look for the development mode flag
if (args[i].equalsIgnoreCase(DEV)) {
inDevelopmentMode = true;
continue;
}
// check for args with parameters
if (i == args.length - 1 || args[i + 1].startsWith("-")) {
continue;
}
String arg = args[++i];
// look for the keyring file
if (args[i - 1].equalsIgnoreCase(KEYRING)) {
keyringFile = arg;
continue;
}
// look for the user password.
if (args[i - 1].equalsIgnoreCase(PASSWORD)) {
password = arg;
continue;
}
}
}
/**
* @see Platform#removeLogListener
*/
public static void removeLogListener(ILogListener listener) {
assertInitialized();
synchronized (logListeners) {
logListeners.remove(listener);
}
}
/**
* @see Platform
*/
public static URL resolve(URL url) throws IOException {
URLConnection connection = url.openConnection();
if (connection instanceof PlatformURLConnection)
return ((PlatformURLConnection) connection).getResolvedURL();
else
return url;
}
public static void run(ISafeRunnable code) {
Assert.isNotNull(code);
try {
code.run();
} catch (Exception e) {
handleException(code, e);
} catch (LinkageError e) {
handleException(code, e);
}
}
public static void setDebugOption(String option, String value) {
if (debugEnabled)
options.setProperty(option, value);
}
/**
* Sets the plug-in registry for the platform to the given value.
* This method should only be called by the registry loader
*/
public static void setPluginRegistry(IPluginRegistry value) {
registry = (PluginRegistry) value;
}
private static void setupMetaArea(String locationString) throws CoreException {
location = new Path(locationString);
// must create the meta area first as it defines all the other locations.
if (location.toFile().exists()) {
if (!location.toFile().isDirectory()) {
String message = Policy.bind("notDir", new String[] { location.toString()});
throw new CoreException(new Status(IStatus.ERROR, Platform.PI_RUNTIME, 13, message, null));
}
}
metaArea = new PlatformMetaArea(location);
metaArea.createLocation();
if (keyringFile == null)
keyringFile = metaArea.getLocation().append(PlatformMetaArea.F_KEYRING).toOSString();
}
}