| /******************************************************************************* |
| * Copyright (c) 2000, 2002 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 - Initial implementation |
| ******************************************************************************/ |
| package org.eclipse.core.internal.runtime; |
| |
| import java.io.*; |
| import java.net.*; |
| import java.util.*; |
| |
| import org.eclipse.core.boot.*; |
| import org.eclipse.core.internal.boot.*; |
| import org.eclipse.core.internal.plugins.*; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.core.runtime.model.*; |
| |
| /** |
| * 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; |
| // registry index - used to store last modified times for |
| // registry caching |
| // ASSUMPTION: Only the plugin registry in 'registry' above |
| // will be cached |
| private static Map regIndex = null; |
| |
| private static ArrayList logListeners = new ArrayList(5); |
| private static Map logs = new HashMap(5); |
| private static PlatformLogWriter platformLog = null; |
| private static PlatformMetaArea metaArea; |
| private static boolean initialized; |
| private static Runnable endOfInitializationHandler = null; |
| private static IPath location; |
| private static PluginClassLoader xmlClassLoader = null; |
| private static long cacheReadTimeStamp; |
| |
| 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 = ""; //$NON-NLS-1$ |
| private static boolean splashDown = false; |
| private static boolean cacheRegistry = true; |
| private static String pluginCustomizationFile = null; |
| |
| private static File lockFile = null; |
| private static RandomAccessFile lockRAF = null; |
| |
| /** |
| * Name of the plug-in customization file (value "plugin_customization.ini") |
| * located in the root of the primary feature plug-in and it's |
| * companion nl-specific file with externalized strings (value |
| * "plugin_customization.properties"). The companion file can |
| * be contained in any nl-specific subdirectories of the primary |
| * feature or any fragment of this feature. |
| */ |
| private static final String PLUGIN_CUSTOMIZATION_BASE_NAME = "plugin_customization"; //$NON-NLS-1$ |
| private static final String PLUGIN_CUSTOMIZATION_FILE_NAME = PLUGIN_CUSTOMIZATION_BASE_NAME + ".ini"; //$NON-NLS-1$ |
| |
| // default plugin data |
| private static final String PI_XML = "org.apache.xerces"; //$NON-NLS-1$ |
| private static final String PLUGINSDIR = "plugins/"; //$NON-NLS-1$ |
| private static final String XML_LOCATION = "plugins/" + PI_XML + "/"; //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| // execution options |
| private static final String OPTION_DEBUG = Platform.PI_RUNTIME + "/debug"; //$NON-NLS-1$ |
| private static final String OPTION_DEBUG_SYSTEM_CONTEXT = Platform.PI_RUNTIME + "/debug/context"; //$NON-NLS-1$ |
| private static final String OPTION_DEBUG_STARTUP = Platform.PI_RUNTIME + "/timing/startup"; //$NON-NLS-1$ |
| private static final String OPTION_DEBUG_SHUTDOWN = Platform.PI_RUNTIME + "/timing/shutdown"; //$NON-NLS-1$ |
| private static final String OPTION_DEBUG_PLUGINS = Platform.PI_RUNTIME + "/registry/debug"; //$NON-NLS-1$ |
| private static final String OPTION_DEBUG_PLUGINS_DUMP = Platform.PI_RUNTIME + "/registry/debug/dump"; //$NON-NLS-1$ |
| private static final String OPTION_DEBUG_PREFERENCES = Platform.PI_RUNTIME + "/preferences/debug"; //$NON-NLS-1$ |
| |
| // command line options |
| private static final String LOG = "-consolelog"; //$NON-NLS-1$ |
| private static final String KEYRING = "-keyring"; //$NON-NLS-1$ |
| protected static final String PASSWORD = "-password"; //$NON-NLS-1$ |
| private static final String NOREGISTRYCACHE = "-noregistrycache"; //$NON-NLS-1$ |
| private static final String PLUGIN_CUSTOMIZATION = "-plugincustomization"; //$NON-NLS-1$ |
| |
| // debug support: set in loadOptions() |
| public static boolean DEBUG = false; |
| public static boolean DEBUG_CONTEXT = false; |
| public static boolean DEBUG_PLUGINS = false; |
| public static boolean DEBUG_STARTUP = false; |
| public static boolean DEBUG_SHUTDOWN = false; |
| public static String DEBUG_PLUGINS_DUMP = null; |
| public static boolean DEBUG_PREFERENCES = false; |
| |
| private static final String KEY_PREFIX = "%"; //$NON-NLS-1$ |
| private static final String KEY_DOUBLE_PREFIX = "%%"; //$NON-NLS-1$ |
| |
| |
| /** |
| * 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); |
| DelegatingURLClassLoader loader = PlatformClassLoader.getDefault(); |
| descriptor.activateDefaultPlugins(loader); |
| descriptor.setPluginClassLoader(loader); |
| descriptor.getPlugin(); |
| |
| descriptor = (PluginDescriptor) registry.getPluginDescriptor(PI_XML, xmlClassLoader.getPluginDescriptor().getVersionIdentifier()); |
| descriptor.activateDefaultPlugins(xmlClassLoader); |
| 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) { |
| // replace if already exists (Set behaviour but we use an array |
| // since we want to retain order) |
| logListeners.remove(listener); |
| 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 { |
| if (!url.getProtocol().equals(PlatformURLHandler.PROTOCOL)) |
| return url; |
| URLConnection connection = url.openConnection(); |
| if (!(connection instanceof PlatformURLConnection)) |
| return url; |
| String file = connection.getURL().getFile(); |
| if (file.endsWith("/") && !file.endsWith(PlatformURLHandler.JAR_SEPARATOR)) //$NON-NLS-1$ |
| throw new IOException(); |
| return ((PlatformURLConnection) connection).getURLAsLocal(); |
| } |
| private static void assertInitialized() { |
| //avoid the Policy.bind if assertion is true |
| if (!initialized) |
| Assert.isTrue(false, Policy.bind("meta.appNotInit")); //$NON-NLS-1$ |
| } |
| /** |
| * Closes the open lock file handle, and makes a silent best |
| * attempt to delete the file. |
| */ |
| private static synchronized void clearLockFile() { |
| try { |
| if (lockRAF != null) { |
| lockRAF.close(); |
| lockRAF = null; |
| } |
| } catch (IOException e) { |
| //don't complain, we're making a best effort to clean up |
| } |
| if (lockFile != null) { |
| lockFile.delete(); |
| lockFile = null; |
| } |
| } |
| /** |
| * Creates a lock file in the meta-area that indicates the meta-area |
| * is in use, preventing other eclipse instances from concurrently |
| * using the same meta-area. |
| */ |
| private static synchronized void createLockFile() throws CoreException { |
| String lockLocation = metaArea.getLocation().append(PlatformMetaArea.F_LOCK_FILE).toOSString(); |
| lockFile = new File(lockLocation); |
| //if the lock file already exists, try to delete, |
| //assume failure means another eclipse has it open |
| if (lockFile.exists()) |
| lockFile.delete(); |
| if (lockFile.exists()) { |
| String message = Policy.bind("meta.inUse", lockLocation); //$NON-NLS-1$ |
| throw new CoreException(new Status(IStatus.ERROR, Platform.PI_RUNTIME, Platform.FAILED_WRITE_METADATA, message, null)); |
| } |
| try { |
| //open the lock file so other instances can't co-exist |
| lockRAF = new RandomAccessFile(lockFile, "rw"); //$NON-NLS-1$ |
| lockRAF.writeByte(0); |
| } catch (IOException e) { |
| String message = Policy.bind("meta.failCreateLock", lockLocation); //$NON-NLS-1$ |
| throw new CoreException(new Status(IStatus.ERROR, Platform.PI_RUNTIME, Platform.FAILED_WRITE_METADATA, message, e)); |
| } |
| } |
| |
| /** |
| * 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); |
| PlatformConfiguration config = InternalBootLoader.getCurrentPlatformConfiguration(); |
| PlatformConfiguration.BootDescriptor bd = config.getPluginBootDescriptor(PI_XML); |
| descriptor.setVersion(bd.getVersion()); |
| try { |
| URL url = bd.getPluginDirectoryURL(); |
| if (url == null) |
| url = new URL(BootLoader.getInstallURL(), XML_LOCATION); |
| descriptor.setLocation(url.toExternalForm()); |
| } catch (MalformedURLException e) { |
| // ISSUE: What to do when this fails. It's pretty serious |
| e.printStackTrace(); |
| } |
| |
| ArrayList libList = new ArrayList(); |
| String[] libs = bd.getLibraries(); |
| for (int i=0; i<libs.length; i++) { |
| LibraryModel lib = factory.createLibrary(); |
| lib.setName(libs[i]); |
| lib.setExports(new String[] { "*" }); //$NON-NLS-1$ |
| libList.add(lib); |
| } |
| descriptor.setRuntime((LibraryModel[]) libList.toArray(new LibraryModel[0])); |
| |
| // 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 endSplash() { |
| if (DEBUG) { |
| String startString = Platform.getDebugOption(Platform.OPTION_STARTTIME); |
| if (startString != null) |
| try { |
| long start = Long.parseLong(startString); |
| long end = System.currentTimeMillis(); |
| System.out.println("Startup complete: " + (end - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } catch (NumberFormatException e) { |
| //this is just debugging code -- ok to swallow exception |
| } |
| } |
| if (splashDown) |
| return; |
| |
| splashDown = true; |
| if (DelegatingURLClassLoader.MONITOR_PLUGINS) |
| PluginStats.setBooting(false); |
| if (DelegatingURLClassLoader.MONITOR_CLASSES) |
| ClassStats.setBooting(false); |
| run(endOfInitializationHandler); |
| } |
| |
| /** |
| * @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; |
| } |
| |
| /** |
| * Augments the plugin path with extra entries. |
| */ |
| private static URL[] getAugmentedPluginPath(URL[] pluginPath) { |
| |
| // ISSUE: this code needs to be reworked so that the platform |
| // does not have logical reference to plug-in-specific |
| // function |
| |
| IPath result = metaArea.getLocation().append(PlatformMetaArea.F_PLUGIN_DATA).append("org.eclipse.scripting").append("plugin.xml"); //$NON-NLS-1$ //$NON-NLS-2$ |
| String userScriptName = result.toString(); |
| URL userScriptUrl = null; |
| try { |
| userScriptUrl = new URL("file",null,0,userScriptName); //$NON-NLS-1$ |
| } catch(MalformedURLException e) { |
| return pluginPath; |
| } |
| |
| URL[] newPath = new URL[pluginPath.length+1]; |
| System.arraycopy(pluginPath,0,newPath,0, pluginPath.length); |
| newPath[newPath.length-1] = userScriptUrl; |
| return newPath; |
| } |
| /** |
| * @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 = getDebugOption(option); |
| return (optionValue != null && optionValue.equalsIgnoreCase("true")) || defaultValue; //$NON-NLS-1$ |
| } |
| /** |
| * @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) { |
| //failed to activate the plugin -- return null |
| log(e.getStatus()); |
| return null; |
| } |
| } |
| /** |
| * @see Platform#getPluginRegistry |
| */ |
| public static IPluginRegistry getPluginRegistry() { |
| assertInitialized(); |
| return registry; |
| } |
| /** |
| * @see Platform#getPluginStateLocation |
| */ |
| public static IPath getPluginStateLocation(IPluginDescriptor descriptor, boolean create) { |
| assertInitialized(); |
| IPath result = metaArea.getPluginStateLocation(descriptor); |
| if (create) |
| 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) { |
| //impossible for the runtime plugin to be missing |
| log(e.getStatus()); |
| return null; |
| } |
| } |
| private static void handleException(ISafeRunnable code, Throwable e) { |
| if (!(e instanceof OperationCanceledException)) { |
| // try to figure out which plugin caused the problem. Derive this from the class |
| // of the code arg. Attribute to the Runtime plugin if we can't figure it out. |
| Plugin plugin = getRuntimePlugin(); |
| try { |
| plugin = ((PluginClassLoader)code.getClass().getClassLoader()).getPluginDescriptor().getPlugin(); |
| } catch (ClassCastException e1) { |
| //ignore and attribute exception to runtime |
| } catch (CoreException e1) { |
| //ignore and attribute exception to runtime |
| } |
| String pluginId = plugin.getDescriptor().getUniqueIdentifier(); |
| String message = Policy.bind("meta.pluginProblems", pluginId); //$NON-NLS-1$ |
| IStatus status; |
| if (e instanceof CoreException) { |
| status = new MultiStatus(pluginId, Platform.PLUGIN_ERROR, message, e); |
| ((MultiStatus)status).merge(((CoreException)e).getStatus()); |
| } else { |
| status = new Status(Status.ERROR, pluginId, Platform.PLUGIN_ERROR, message, e); |
| } |
| plugin.getLog().log(status); |
| } |
| code.handleException(e); |
| } |
| /** |
| * 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"); //$NON-NLS-1$ |
| } catch (CoreException e) { |
| 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) { |
| 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); |
| clearLockFile(); |
| if (DEBUG_PLUGINS && DEBUG_PLUGINS_DUMP != null) { |
| // We are debugging so output the registry in XML |
| // format. |
| registry.debugRegistry(DEBUG_PLUGINS_DUMP); |
| } |
| |
| if (cacheRegistry) { |
| // Write the registry in cache format |
| try { |
| registry.saveRegistry(); |
| } catch (IOException e) { |
| String message = Policy.bind("meta.unableToWriteRegistry"); //$NON-NLS-1$ |
| IStatus status = new Status(IStatus.ERROR, Platform.PI_RUNTIME, Platform.PLUGIN_ERROR, message, e); |
| getRuntimePlugin().getLog().log(status); |
| if (DEBUG) |
| System.out.println(status.getMessage()); |
| } |
| } else { |
| // get rid of the cache file if it exists |
| registry.flushRegistry(); |
| } |
| 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, Runnable handler) throws CoreException { |
| endOfInitializationHandler = handler; |
| processCommandLine(args); |
| setupMetaArea(locationString); |
| createLockFile(); |
| 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(); |
| if (DEBUG_CONTEXT) |
| System.out.println("OS: " + BootLoader.getOS() + " WS: " + BootLoader.getWS() + //$NON-NLS-1$ //$NON-NLS-2$ |
| " NL: " + BootLoader.getNL() + " ARCH: " + BootLoader.getOSArch()); //$NON-NLS-1$ //$NON-NLS-2$ |
| // can't install the log or log problems until after the platform has been initialized. |
| platformLog = new PlatformLogWriter(metaArea.getLogLocation().toFile()); |
| addLogListener(platformLog); |
| if (consoleLogEnabled) { |
| consoleLog = new PlatformLogWriter(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) { |
| //try deleting the file and loading again - format may have changed |
| new java.io.File(keyringFile).delete(); |
| try { |
| keyring = new AuthorizationDatabase(keyringFile, password); |
| } catch (CoreException e) { |
| //don't bother logging a second failure |
| } |
| } |
| } |
| 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); |
| if (DEBUG) { |
| DEBUG_CONTEXT = getBooleanOption(OPTION_DEBUG_SYSTEM_CONTEXT, false); |
| DEBUG_STARTUP = getBooleanOption(OPTION_DEBUG_STARTUP, false); |
| DEBUG_SHUTDOWN = getBooleanOption(OPTION_DEBUG_SHUTDOWN, false); |
| DEBUG_PLUGINS = getBooleanOption(OPTION_DEBUG_PLUGINS, false); |
| DEBUG_PLUGINS_DUMP = getDebugOption(OPTION_DEBUG_PLUGINS_DUMP); |
| DEBUG_PREFERENCES = getBooleanOption(OPTION_DEBUG_PREFERENCES, 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("parse.registryProblems"), null); //$NON-NLS-1$ |
| InternalFactory factory = new InternalFactory(problems); |
| |
| IPath path = getMetaArea().getRegistryPath(); |
| File cacheFile = path.toFile(); |
| DataInputStream input = null; |
| registry = null; |
| // augment the plugin path with any additional platform entries |
| // (eg. user scripts) |
| URL[] augmentedPluginPath = getAugmentedPluginPath(pluginPath); |
| if (cacheFile.exists() && cacheRegistry) { |
| try { |
| input = new DataInputStream(new BufferedInputStream(new FileInputStream(path.toFile()))); |
| try { |
| long start = System.currentTimeMillis(); |
| RegistryCacheReader cacheReader = new RegistryCacheReader(factory); |
| registry = (PluginRegistry)cacheReader.readPluginRegistry(input, augmentedPluginPath, DEBUG && DEBUG_PLUGINS); |
| if (DEBUG) |
| System.out.println("Read registry cache: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } finally { |
| input.close(); |
| } |
| } catch (IOException ioe) { |
| IStatus status = new Status(IStatus.ERROR, Platform.PI_RUNTIME, Platform.PLUGIN_ERROR, Policy.bind("meta.unableToReadCache"), ioe); //$NON-NLS-1$ |
| problems.merge(status); |
| } |
| } |
| if (registry == null) { |
| clearRegIndex(); |
| if (cacheFile.exists()) { |
| // Delete the cache file so we know to re-write the |
| // cache when we shutdown. If the cache file exists |
| // on shutdown, we won't bother re-writing it. |
| if (!cacheFile.delete()) { |
| IStatus status = new Status(IStatus.WARNING, Platform.PI_RUNTIME, Platform.FAILED_DELETE_METADATA, Policy.bind("meta.unableToDeleteCache", cacheFile.getAbsolutePath()), null); //$NON-NLS-1$ |
| problems.merge(status); |
| } |
| } |
| long start = System.currentTimeMillis(); |
| InternalPlatform.setRegistryCacheTimeStamp(BootLoader.getCurrentPlatformConfiguration().getPluginsChangeStamp()); |
| registry = (PluginRegistry) parsePlugins(augmentedPluginPath, factory, DEBUG && DEBUG_PLUGINS); |
| IStatus resolveStatus = registry.resolve(true, true); |
| problems.merge(resolveStatus); |
| registry.markReadOnly(); |
| if (DEBUG) |
| System.out.println("Parse and resolve registry: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| registry.startup(null); |
| return problems; |
| } |
| /** |
| * Notifies all listeners of the platform log. This includes the console log, if |
| * used, and the platform log file. All Plugin log messages get funnelled |
| * through here as well. |
| */ |
| 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++) { |
| final ILogListener listener = listeners[i]; |
| ISafeRunnable code = new ISafeRunnable() { |
| public void run() throws Exception { |
| listener.logging(status, Platform.PI_RUNTIME); |
| } |
| public void handleException(Throwable e) { |
| } |
| }; |
| run(code); |
| } |
| } |
| /** |
| * @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 String[] processCommandLine(String[] args) { |
| int[] configArgs = new int[100]; |
| configArgs[0] = -1; // need to initialize the first element to something that could not be an index. |
| int configArgIndex = 0; |
| for (int i = 0; i < args.length; i++) { |
| boolean found = false; |
| // check for args without parameters (i.e., a flag arg) |
| |
| // look for the log flag |
| if (args[i].equalsIgnoreCase(LOG)) { |
| consoleLogEnabled = true; |
| found = true; |
| } |
| |
| // look for the no registry cache flag |
| if (args[i].equalsIgnoreCase(NOREGISTRYCACHE)) { |
| cacheRegistry = false; |
| found = true; |
| } |
| |
| // done checking for args. Remember where an arg was found |
| if (found) { |
| configArgs[configArgIndex++] = i; |
| continue; |
| } |
| // check for args with parameters |
| if (i == args.length - 1 || args[i + 1].startsWith("-")) //$NON-NLS-1$ |
| continue; |
| String arg = args[++i]; |
| |
| // look for the keyring file |
| if (args[i - 1].equalsIgnoreCase(KEYRING)) { |
| keyringFile = arg; |
| found = true; |
| } |
| |
| // look for the user password. |
| if (args[i - 1].equalsIgnoreCase(PASSWORD)) { |
| password = arg; |
| found = true; |
| } |
| |
| // look for the plug-in customization file |
| if (args[i - 1].equalsIgnoreCase(PLUGIN_CUSTOMIZATION)) { |
| pluginCustomizationFile = arg; |
| found = true; |
| } |
| |
| // done checking for args. Remember where an arg was found |
| if (found) { |
| configArgs[configArgIndex++] = i - 1; |
| configArgs[configArgIndex++] = i; |
| } |
| } |
| // remove all the arguments consumed by this argument parsing |
| if (configArgIndex == 0) |
| return args; |
| String[] passThruArgs = new String[args.length - configArgIndex]; |
| configArgIndex = 0; |
| int j = 0; |
| for (int i = 0; i < args.length; i++) { |
| if (i == configArgs[configArgIndex]) |
| configArgIndex++; |
| else |
| passThruArgs[j++] = args[i]; |
| } |
| return passThruArgs; |
| } |
| /** |
| * @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 { |
| if (!url.getProtocol().equals(PlatformURLHandler.PROTOCOL)) |
| return url; |
| 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); |
| } |
| } |
| private static void run(Runnable handler) { |
| // run end-of-initialization handler |
| if (handler == null) |
| return; |
| |
| final Runnable finalHandler = handler; |
| ISafeRunnable code = new ISafeRunnable() { |
| public void run() throws Exception { |
| finalHandler.run(); |
| } |
| public void handleException(Throwable e) { |
| // just continue ... the exception has already been logged by |
| // the platform (see handleException(ISafeRunnable) |
| } |
| }; |
| Platform.run(code); |
| } |
| 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); |
| if (!location.isAbsolute()) |
| location = new Path(System.getProperty("user.dir")).append(location); //$NON-NLS-1$ |
| // 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("meta.notDir", location.toString()); //$NON-NLS-1$ |
| throw new CoreException(new Status(IStatus.ERROR, Platform.PI_RUNTIME, Platform.FAILED_WRITE_METADATA, message, null)); |
| } |
| } |
| metaArea = new PlatformMetaArea(location); |
| metaArea.createLocation(); |
| if (keyringFile == null) |
| keyringFile = metaArea.getLocation().append(PlatformMetaArea.F_KEYRING).toOSString(); |
| } |
| public static void addLastModifiedTime (String pathKey, long lastModTime) { |
| if (regIndex == null) |
| regIndex = new HashMap(30); |
| regIndex.put(pathKey, new Long(lastModTime)); |
| } |
| public static Map getRegIndex() { |
| return regIndex; |
| } |
| public static void clearRegIndex() { |
| regIndex = null; |
| } |
| |
| /** |
| * Look for the companion preference translation file for a group |
| * of preferences. This method will attempt to find a companion |
| * ".properties" file first. This companion file can be in an |
| * nl-specific directory for this plugin or any of its fragments or |
| * it can be in the root of this plugin or the root of any of the |
| * plugin's fragments. This properties file can be used to translate |
| * preference values. |
| * |
| * @param pluginDescriptor the descriptor of the plugin |
| * who has the preferences |
| * @param basePrefFileName the base name of the preference file |
| * This base will be used to construct the name of the |
| * companion translation file. |
| * Example: If basePrefFileName is "plugin_customization", |
| * the preferences are in "plugin_customization.ini" and |
| * the translations are found in |
| * "plugin_customization.properties". |
| * @return the properties file |
| * |
| * @since 2.0 |
| */ |
| public static Properties getPreferenceTranslator (IPluginDescriptor pluginDescriptor, String basePrefFileName) { |
| URL pluginExternalURL = pluginDescriptor.find(new Path("$nl$").append(basePrefFileName + ".properties")); //$NON-NLS-1$ //$NON-NLS-2$ |
| if (pluginExternalURL == null) |
| return null; |
| |
| Properties propFile = new Properties(); |
| try { |
| InputStream input = new BufferedInputStream(new FileInputStream(pluginExternalURL.getFile())); |
| try { |
| propFile.load(input); |
| } finally { |
| if (input != null) |
| input.close(); |
| } |
| } catch (IOException e) { |
| if (DEBUG_PREFERENCES) { |
| System.out.println("Exception loading preference translations from file: " + pluginExternalURL); //$NON-NLS-1$ |
| e.printStackTrace(); |
| } |
| } |
| |
| return propFile; |
| } |
| |
| /** |
| * Takes a preference value and a related resource bundle and |
| * returns the translated version of this value (if one exists). |
| * |
| * @param value the preference value for potential translation |
| * @param bundle the bundle containing the translated values |
| * |
| * @since 2.0 |
| */ |
| public static String translatePreference (String value, Properties props) { |
| value = value.trim(); |
| if (props == null || value.startsWith(KEY_DOUBLE_PREFIX)) |
| return value; |
| if (value.startsWith(KEY_PREFIX)) { |
| |
| int ix = value.indexOf(" "); //$NON-NLS-1$ |
| String key = ix == -1 ? value : value.substring(0,ix); |
| String dflt = ix == -1 ? value : value.substring(ix+1); |
| return props.getProperty(key.substring(1), dflt); |
| } |
| return value; |
| } |
| |
| /** |
| * Applies primary feature-specific overrides to default preferences for the |
| * plug-in with the given id. |
| * <p> |
| * Note that by the time this method is called, the default settings |
| * for the plug-in itself should have already have been filled in. |
| * </p> |
| * |
| * @param id the unique identifier of the plug-in |
| * @param preferences the preference store for the specified plug-in |
| * |
| * @since 2.0 |
| */ |
| public static void applyPrimaryFeaturePluginDefaultOverrides( |
| String id, |
| Preferences preferences) { |
| |
| // carefully navigate to the plug-in for the primary feature |
| IPlatformConfiguration cfg = BootLoader.getCurrentPlatformConfiguration(); |
| if (cfg == null) { |
| // bail if we don't seem to have one for whatever reason (!) |
| if (DEBUG_PREFERENCES) { |
| System.out.println("Plugin preferences unable to find a platform configuration"); //$NON-NLS-1$ |
| } |
| return; |
| } |
| String primaryFeaturePluginId = cfg.getPrimaryFeatureIdentifier(); |
| if (primaryFeaturePluginId == null) { |
| // bail if we don't seem to have one of these (!) |
| if (DEBUG_PREFERENCES) { |
| System.out.println("Plugin preferences unable to find a primary feature plugin id"); //$NON-NLS-1$ |
| } |
| return; |
| } |
| IPluginDescriptor primaryFeatureDescriptor = getPluginRegistry().getPluginDescriptor(primaryFeaturePluginId); |
| if (primaryFeatureDescriptor == null) { |
| // bail if primary feature is missing (!) |
| if (DEBUG_PREFERENCES) { |
| System.out.println("Plugin preferences unable to find a primary feature"); //$NON-NLS-1$ |
| } |
| return; |
| } |
| // locate plug-in customization file within primary feature plug-in (or fragment) |
| URL pluginCustomizationURL = primaryFeatureDescriptor.find(new Path(PLUGIN_CUSTOMIZATION_FILE_NAME)); |
| if (pluginCustomizationURL == null) { |
| if (DEBUG_PREFERENCES) { |
| System.out.println("Preferences file " + PLUGIN_CUSTOMIZATION_FILE_NAME + " not found."); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| return; |
| } |
| if (DEBUG_PREFERENCES) { |
| System.out.println("Loading preferences from " + pluginCustomizationURL); //$NON-NLS-1$ |
| } |
| // Now see if we have a file with externalized strings for |
| // this preference file |
| Properties props = getPreferenceTranslator(primaryFeatureDescriptor, PLUGIN_CUSTOMIZATION_BASE_NAME); |
| // apply any defaults for the given plug-in |
| applyPluginDefaultOverrides(pluginCustomizationURL, id, preferences, props); |
| } |
| |
| /** |
| * Applies command line-supplied overrides to default preferences for the |
| * plug-in with the given id. |
| * <p> |
| * Note that by the time this method is called, the default settings |
| * for the plug-in itself should have already have been filled in, along |
| * with any default overrides supplied by the primary feature. |
| * </p> |
| * |
| * @param id the unique identifier of the plug-in |
| * @param preferences the preference store for the specified plug-in |
| * |
| * @since 2.0 |
| */ |
| public static void applyCommandLinePluginDefaultOverrides( |
| String id, |
| Preferences preferences) { |
| |
| if (pluginCustomizationFile == null) { |
| // no command line overrides to process |
| if (DEBUG_PREFERENCES) { |
| System.out.println("Command line argument -pluginCustomization not used."); //$NON-NLS-1$ |
| } |
| return; |
| } |
| |
| try { |
| URL pluginCustomizationURL = new File(pluginCustomizationFile).toURL(); |
| if (DEBUG_PREFERENCES) { |
| System.out.println("Loading preferences from " + pluginCustomizationURL); //$NON-NLS-1$ |
| } |
| applyPluginDefaultOverrides(pluginCustomizationURL, id, preferences, null); |
| } catch (MalformedURLException e) { |
| // fail silently |
| if (DEBUG_PREFERENCES) { |
| System.out.println("MalformedURLException creating URL for plugin customization file " //$NON-NLS-1$ |
| + pluginCustomizationFile); |
| e.printStackTrace(); |
| } |
| return; |
| } |
| } |
| |
| /** |
| * Applies overrides to default preferences for the plug-in with the given id. |
| * The data is contained in the <code>java.io.Properties</code> style file at |
| * the given URL. The property names consist of "/'-separated plug-in id and |
| * name of preference; e.g., "com.example.myplugin/mypref". |
| * |
| * @param propertiesURL the URL of a <code>java.io.Properties</code> style file |
| * @param id the unique identifier of the plug-in |
| * @param preferences the preference store for the specified plug-in |
| * |
| * @since 2.0 |
| */ |
| private static void applyPluginDefaultOverrides( |
| URL propertiesURL, |
| String id, |
| Preferences preferences, |
| Properties props) { |
| |
| // read the java.io.Properties file at the given URL |
| Properties overrides = new Properties(); |
| SafeFileInputStream in = null; |
| |
| try { |
| File inFile = new File(propertiesURL.getFile()); |
| if (!inFile.exists()) { |
| // We don't have a preferences file to worry about |
| if (DEBUG_PREFERENCES) { |
| System.out.println("Preference file " + //$NON-NLS-1$ |
| propertiesURL + " not found."); //$NON-NLS-1$ |
| } |
| return; |
| } |
| |
| in = new SafeFileInputStream(inFile); |
| if (in == null) { |
| // fail quietly |
| if (DEBUG_PREFERENCES) { |
| System.out.println("Failed to open " + //$NON-NLS-1$ |
| propertiesURL); |
| } |
| return; |
| } |
| overrides.load(in); |
| } catch (IOException e) { |
| // cannot read ini file - fail silently |
| if (DEBUG_PREFERENCES) { |
| System.out.println("IOException reading preference file " + //$NON-NLS-1$ |
| propertiesURL); |
| e.printStackTrace(); |
| } |
| return; |
| } finally { |
| try { |
| if (in != null) { |
| in.close(); |
| } |
| } catch (IOException e) { |
| // ignore problems closing file |
| if (DEBUG_PREFERENCES) { |
| System.out.println("IOException closing preference file " + //$NON-NLS-1$ |
| propertiesURL); |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| for (Iterator it = overrides.entrySet().iterator(); it.hasNext();) { |
| Map.Entry entry = (Map.Entry) it.next(); |
| String qualifiedKey = (String) entry.getKey(); |
| // Keys consist of "/'-separated plug-in id and name of preference |
| // e.g., "com.example.myplugin/mypref" |
| int s = qualifiedKey.indexOf('/'); |
| if (s < 0 || s == 0 || s == qualifiedKey.length() - 1) { |
| // skip mangled entry |
| continue; |
| } |
| // plug-in id is non-empty string before "/" |
| String pluginId = qualifiedKey.substring(0, s); |
| if (pluginId.equals(id)) { |
| // override property in the given plug-in |
| // plig-in-specified property name is non-empty string after "/" |
| String propertyName = qualifiedKey.substring(s + 1); |
| String value = (String) entry.getValue(); |
| value = translatePreference(value, props); |
| preferences.setDefault(propertyName, value); |
| } |
| } |
| if (DEBUG_PREFERENCES) { |
| System.out.println("Preferences now set as follows:"); //$NON-NLS-1$ |
| String[] prefNames = preferences.propertyNames(); |
| for (int i = 0; i < prefNames.length; i++) { |
| String value = preferences.getString(prefNames[i]); |
| System.out.println("\t" + prefNames[i] + " = " + value); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| prefNames = preferences.defaultPropertyNames(); |
| for (int i = 0; i < prefNames.length; i++) { |
| String value = preferences.getDefaultString(prefNames[i]); |
| System.out.println("\tDefault values: " + prefNames[i] + " = " + value); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| } |
| public static long getRegistryCacheTimeStamp() { |
| return cacheReadTimeStamp; |
| } |
| public static void setRegistryCacheTimeStamp(long timeStamp) { |
| cacheReadTimeStamp = timeStamp; |
| } |
| } |