/******************************************************************************* * Copyright (c) 2000, 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.core.runtime; import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.Map; import org.eclipse.core.internal.runtime.*; import org.eclipse.osgi.util.NLS; import org.osgi.framework.*; /** * The abstract superclass of all plug-in runtime class * implementations. A plug-in subclasses this class and overrides * the appropriate life cycle methods in order to react to the life cycle * requests automatically issued by the platform. * For compatibility reasons, the methods called for those life cycle events * vary, please see the "Constructors and life cycle methods" section below. * *
* Conceptually, the plug-in runtime class represents the entire plug-in * rather than an implementation of any one particular extension the * plug-in declares. A plug-in is not required to explicitly * specify a plug-in runtime class; if none is specified, the plug-in * will be given a default plug-in runtime object that ignores all life * cycle requests (it still provides access to the corresponding * plug-in descriptor). *
*
* In the case of more complex plug-ins, it may be desirable
* to define a concrete subclass of Plugin
.
* However, just subclassing Plugin
is not
* sufficient. The name of the class must be explicitly configured
* in the plug-in's manifest (plugin.xml
) file
* with the class attribute of the <plugin>
element markup.
*
* Instances of plug-in runtime classes are automatically created * by the platform in the course of plug-in activation. For compatibility reasons, * the constructor used to create plug-in instances varies, please see the "Constructors * and life cycle methods" section below. *
* The concept of bundles underlies plug-ins. However it is safe to regard plug-ins * and bundles as synonyms. *
** Clients must never explicitly instantiate a plug-in runtime class. *
** A typical implementation pattern for plug-in runtime classes is to * provide a static convenience method to gain access to a plug-in's * runtime object. This way, code in other parts of the plug-in * implementation without direct access to the plug-in runtime object * can easily obtain a reference to it, and thence to any plug-in-wide * resources recorded on it. An example for Eclipse 3.0 follows: *
* package myplugin; * public class MyPluginClass extends Plugin { * private static MyPluginClass instance; * * public static MyPluginClass getInstance() { return instance; } * * public void MyPluginClass() { * super(); * instance = this; * // ... other initialization * } * // ... other methods * } ** In the above example, a call to
MyPluginClass.getInstance()
* will always return an initialized instance of MyPluginClass
.
*
* * Constructors and life cycle methods *
* If the plugin.xml of a plug-in indicates <?eclipse version="3.0"?> and its prerequisite
* list includes org.eclipse.core.runtime
, the default constructor of the plug-in
* class is used and {@link #start(BundleContext)} and {@link #stop(BundleContext)} are
* called as life cycle methods.
*
* If the plugin.xml of a plug-in indicates <?eclipse version="3.0"?> and its prerequisite list includes
* org.eclipse.core.runtime.compatibility
, the {@link #Plugin(IPluginDescriptor)}
* constructor is used and {@link #startup()} and {@link #shutdown()} are called as life cycle methods.
* Note that in this situation, start() is called before startup() and stop() is called
* after shutdown.
*
* If the plugin.xml of your plug-in does not indicate <?eclipse version="3.0"?> it is therefore * not a 3.0 plug-in. Consequently the {@link #Plugin(IPluginDescriptor)} is used and {@link #startup()} and * {@link #shutdown()} are called as life cycle methods. *
* Since Eclipse 3.0 APIs of the Plugin class can be called only when the Plugin is in an active state, i.e., * after it was started up and before it is shutdown. In particular, it means that Plugin APIs should not * be called from overrides of {@link #Plugin()}. *
*/ public abstract class Plugin implements BundleActivator { /** * String constant used for the default scope name for legacy * Eclipse plug-in preferences. The value ofPLUGIN_PREFERENCE_SCOPE
should
* match the InstanceScope's variable SCOPE from org.eclipse.core.runtime.preferences.
* The value is copied in this file to prevent unnecessary activation of
* the Preferences plugin on startup.
*
* @since 3.0
*/
public static String PLUGIN_PREFERENCE_SCOPE = "instance"; //$NON-NLS-1$
/**
* The bundle associated this plug-in
*/
private Bundle bundle;
/**
* The debug flag for this plug-in. The flag is false by default.
* It can be set to true either by the plug-in itself or in the platform
* debug options.
*/
private boolean debug = false;
/** The plug-in descriptor.
* @deprecated Marked as deprecated to suppress deprecation warnings.
*/
private IPluginDescriptor descriptor;
/**
* The base name (value "preferences"
) for the file which is used for
* overriding default preference values.
*
* @since 2.0
* @see #PREFERENCES_DEFAULT_OVERRIDE_FILE_NAME
*/
public static String PREFERENCES_DEFAULT_OVERRIDE_BASE_NAME = "preferences"; //$NON-NLS-1$
/**
* The name of the file (value "preferences.ini"
) in a
* plug-in's (read-only) directory that, when present, contains values that
* override the normal default values for this plug-in's preferences.
*
* The format of the file is as per java.io.Properties
where
* the keys are property names and values are strings.
*
null
* meaning not yet created and initialized.
*
* @since 2.0
*/
private Preferences preferences = null;
/**
* Creates a new plug-in runtime object. This method is called by the platform
* if this class is used as a BundleActivator
. This method is not
* needed/used if this plug-in requires the org.eclipse.core.runtime.compatibility plug-in.
* Subclasses of Plugin
* must call this method first in their constructors.
*
* The resultant instance is not managed by the runtime and
* so should be remembered by the client (typically using a Singleton pattern).
* Clients must never explicitly call this method.
*
* * Note: The class loader typically has monitors acquired during invocation of this method. It is * strongly recommended that this method avoid synchronized blocks or other thread locking mechanisms, * as this would lead to deadlock vulnerability. *
* * @since 3.0 */ public Plugin() { super(); } /** * Creates a new plug-in runtime object for the given plug-in descriptor. ** Instances of plug-in runtime classes are automatically created * by the platform in the course of plug-in activation. * Clients must never explicitly call this method. *
** Note: The class loader typically has monitors acquired during invocation of this method. It is * strongly recommended that this method avoid synchronized blocks or other thread locking mechanisms, * as this would lead to deadlock vulnerability. *
* * @param descriptor the plug-in descriptor * @see #getDescriptor() * @deprecated * In Eclipse 3.0 this constructor has been replaced by {@link #Plugin()}. * Implementations ofMyPlugin(IPluginDescriptor descriptor)
should be changed to
* MyPlugin()
and call super()
instead of super(descriptor)
.
* The MyPlugin(IPluginDescriptor descriptor)
constructor is called only for plug-ins
* which explicitly require the org.eclipse.core.runtime.compatibility plug-in.
*/
public Plugin(IPluginDescriptor descriptor) {
Assert.isNotNull(descriptor);
Assert.isTrue(!CompatibilityHelper.hasPluginObject(descriptor), NLS.bind(Messages.plugin_deactivatedLoad, this.getClass().getName(), descriptor.getUniqueIdentifier() + " is not activated")); //$NON-NLS-1$
this.descriptor = descriptor;
// on plugin start, find and start the corresponding bundle.
bundle = getInternalPlatform ().getBundle(descriptor.getUniqueIdentifier());
try {
// TODO Make start() non-static, so we can test exception handling
getInternalPlatform ().start(bundle);
} catch (BundleException e) {
String message = NLS.bind(Messages.plugin_startupProblems, descriptor.getUniqueIdentifier());
IStatus status = new Status(IStatus.ERROR, Platform.PI_RUNTIME, IStatus.ERROR, message, e);
getInternalPlatform ().log(status);
}
}
/**
* Allow tests to override the InternalPlatform with mock objects.
*
* @return
*/
protected InternalPlatform getInternalPlatform ()
{
return InternalPlatform.getDefault();
}
/**
* Returns a URL for the given path. Returns null
if the URL
* could not be computed or created.
*
* @param path path relative to plug-in installation location
* @return a URL for the given path or null
* @deprecated use {@link FileLocator#find(Bundle, IPath, Map)}
*/
public URL find(IPath path) {
// TODO Turn FileLocator into a singleton instead of using static methods
return FileLocator.find(bundle, path, null);
}
/**
* Returns a URL for the given path. Returns null
if the URL
* could not be computed or created.
*
* @param path file path relative to plug-in installation location
* @param override map of override substitution arguments to be used for
* any $arg$ path elements. The map keys correspond to the substitution
* arguments (eg. "$nl$" or "$os$"). The resulting
* values must be of type java.lang.String. If the map is null
,
* or does not contain the required substitution argument, the default
* is used.
* @return a URL for the given path or null
* @deprecated use {@link FileLocator#find(Bundle, IPath, Map)}
*/
public URL find(IPath path, Map override) {
// TODO Turn FileLocator into a singleton instead of using static methods
return FileLocator.find(bundle, path, override);
}
/**
* Returns the plug-in descriptor for this plug-in runtime object.
*
* @return the plug-in descriptor for this plug-in runtime object
* @deprecated
* IPluginDescriptor
was refactored in Eclipse 3.0.
* The getDescriptor()
method may only be called by plug-ins
* which explicitly require the org.eclipse.core.runtime.compatibility plug-in.
* See the comments on {@link IPluginDescriptor} and its methods for details.
*/
public IPluginDescriptor getDescriptor() {
if (descriptor != null)
return descriptor;
return initializeDescriptor(bundle.getSymbolicName());
}
/**
* Returns the log for this plug-in. If no such log exists, one is created.
*
* @return the log for this plug-in
* XXX change this into a LogMgr service that would keep track of the map. See if it can be a service factory.
*/
public ILog getLog() {
return getInternalPlatform ().getLog(bundle);
}
/**
* Returns the location in the local file system of the
* plug-in state area for this plug-in.
* If the plug-in state area did not exist prior to this call,
* it is created.
* * The plug-in state area is a file directory within the * platform's metadata area where a plug-in is free to create files. * The content and structure of this area is defined by the plug-in, * and the particular plug-in is solely responsible for any files * it puts there. It is recommended for plug-in preference settings and * other configuration parameters. *
* @throws IllegalStateException, when the system is running with no data area (-data @none), * or when a data area has not been set yet. * @return a local file system path * XXX Investigate the usage of a service factory (see also platform.getStateLocation) */ public IPath getStateLocation() throws IllegalStateException { return getInternalPlatform ().getStateLocation(bundle, true); } /** * Returns the preference store for this plug-in. ** Note that if an error occurs reading the preference store from disk, an empty * preference store is quietly created, initialized with defaults, and returned. *
*
* Calling this method may cause the preference store to be created and
* initialized. Subclasses which reimplement the
* initializeDefaultPluginPreferences
method have this opportunity
* to initialize preference default values, just prior to processing override
* default values imposed externally to this plug-in (specified for the product,
* or at platform start up).
*
* After settings in the preference store are changed (for example, with
* Preferences.setValue
or setToDefault
),
* savePluginPreferences
should be called to store the changed
* values back to disk. Otherwise the changes will be lost on plug-in
* shutdown.
*
* Plug-in preferences are not saved automatically on plug-in shutdown. *
* * @see Preferences#store(OutputStream, String) * @see Preferences#needsSaving() * @since 2.0 * XXX deprecate call flush on the node for this bundle on the instance scope */ public void savePluginPreferences() { // populate the "preferences" instvar. We still might // need to save them because someone else might have // made changes via the OSGi APIs. getPluginPreferences(); // Performance: isolate PreferenceForwarder and BackingStoreException into // an inner class to avoid class loading (and then activation of the Preferences plugin) // as the Plugin class is loaded. final Preferences preferencesCopy = preferences; Runnable innerCall = new Runnable() { public void run() { try { ((org.eclipse.core.internal.preferences.legacy.PreferenceForwarder) preferencesCopy).flush(); } catch (org.osgi.service.prefs.BackingStoreException e) { IStatus status = new Status(IStatus.ERROR, Platform.PI_RUNTIME, IStatus.ERROR, Messages.preferences_saveProblems, e); getInternalPlatform ().log(status); } } }; innerCall.run(); } /** * Initializes the default preferences settings for this plug-in. ** This method is called sometime after the preference store for this * plug-in is created. Default values are never stored in preference * stores; they must be filled in each time. This method provides the * opportunity to initialize the default values. *
** The default implementation of this method does nothing. A subclass that needs * to set default values for its preferences must reimplement this method. * Default values set at a later point will override any default override * settings supplied from outside the plug-in (product configuration or * platform start up). *
* @since 2.0 * @deprecated * This method has been refactored in the new preference mechanism * to handle the case where the runtime compatibility layer does not exist. The * contents of this method should be moved to the method named *initializeDefaultPreferences
in a separate subclass of
* {@link org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer}.
* This class should be contributed via the
* org.eclipse.core.runtime.preferences
extension point.
* * <extension point=&quo;org.eclipse.core.runtime.preferences&quo;> * <initializer class=&quo;com.example.MyPreferenceInitializer&quo;/> * </extension> * ... * package com.example; * public class MyPreferenceInitializer extends AbstractPreferenceInitializer { * public MyPreferenceInitializer() { * super(); * } * public void initializeDefaultPreferences() { * Preferences node = new DefaultScope().getNode("my.plugin.id"); * node.put(key, value); * } * } **/ protected void initializeDefaultPluginPreferences() { // default implementation of this method - spec'd to do nothing } /** * Internal method. This method is a hook for * initialization of default preference values. * It should not be called by clients. * * @since 3.0 */ public void internalInitializeDefaultPluginPreferences() { initializeDefaultPluginPreferences(); } /** * Returns whether this plug-in is in debug mode. * By default plug-ins are not in debug mode. A plug-in can put itself * into debug mode or the user can set an execution option to do so. *
* Note that the plug-in's debug flag is initialized when the * plug-in is started. The result of calling this method before the plug-in * has started is unspecified. *
* * @return whether this plug-in is in debug mode * XXX deprecate use the service and cache as needed */ public boolean isDebugging() { return debug; } /** * Returns an input stream for the specified file. The file path * must be specified relative this the plug-in's installation location. * * @param file path relative to plug-in installation location * @return an input stream * @exception IOException if the given path cannot be found in this plug-in * * @see #openStream(IPath,boolean) * @deprecated use {@link FileLocator#openStream(Bundle, IPath, boolean)} */ public InputStream openStream(IPath file) throws IOException { // TODO Turn FileLocator into a singleton instead of using static methods return FileLocator.openStream(bundle, file, false); } /** * Returns an input stream for the specified file. The file path * must be specified relative to this plug-in's installation location. * Optionally, the path specified may contain $arg$ path elements that can * be used as substitution arguments. If this option is used then the $arg$ * path elements are processed in the same way as {@link #find(IPath, Map)}. ** The caller must close the returned stream when done. *
* * @param file path relative to plug-in installation location * @param substituteArgstrue
to process substitution arguments,
* and false
for the file exactly as specified without processing any
* substitution arguments.
* @return an input stream
* @exception IOException if the given path cannot be found in this plug-in
* @deprecated use {@link FileLocator#openStream(Bundle, IPath, boolean)}
*/
public InputStream openStream(IPath file, boolean substituteArgs) throws IOException {
// TODO Turn FileLocator into a singleton instead of using static methods
return FileLocator.openStream(bundle, file, substituteArgs);
}
/**
* Sets whether this plug-in is in debug mode.
* By default plug-ins are not in debug mode. A plug-in can put itself
* into debug mode or the user can set a debug option to do so.
* * Note that the plug-in's debug flag is initialized when the * plug-in is started. The result of calling this method before the plug-in * has started is unspecified. *
* * @param value whether or not this plug-in is in debug mode * XXX deprecate use the service and cache as needed */ public void setDebugging(boolean value) { debug = value; } /** * Shuts down this plug-in and discards all plug-in state. ** This method should be re-implemented in subclasses that need to do something * when the plug-in is shut down. Implementors should call the inherited method * to ensure that any system requirements can be met. *
** Plug-in shutdown code should be robust. In particular, this method * should always make an effort to shut down the plug-in. Furthermore, * the code should not assume that the plug-in was started successfully, * as this method will be invoked in the event of a failure during startup. *
** Note 1: If a plug-in has been started, this method will be automatically * invoked by the platform when the platform is shut down. *
** Note 2: This method is intended to perform simple termination * of the plug-in environment. The platform may terminate invocations * that do not complete in a timely fashion. *
* Clients must never explicitly call this method. *
*
* @exception CoreException if this method fails to shut down
* this plug-in
* @deprecated
* In Eclipse 3.0 this method has been replaced by {@link Plugin#stop(BundleContext context)}.
* Implementations of shutdown()
should be changed to override
* stop(BundleContext context)
and call super.stop(context)
* instead of super.shutdown()
.
* The shutdown()
method is called only for plug-ins which explicitly require the
* org.eclipse.core.runtime.compatibility plug-in.
*/
public void shutdown() throws CoreException {
if (CompatibilityHelper.initializeCompatibility() == null)
return;
Throwable exception = null;
Method m;
try {
m = descriptor.getClass().getMethod("doPluginDeactivation", new Class[0]); //$NON-NLS-1$
m.invoke(descriptor, null);
} catch (SecurityException e) {
exception = e;
} catch (NoSuchMethodException e) {
exception = e;
} catch (IllegalArgumentException e) {
exception = e;
} catch (IllegalAccessException e) {
exception = e;
} catch (InvocationTargetException e) {
exception = e;
}
if (exception == null)
return;
String message = NLS.bind(Messages.plugin_shutdownProblems, descriptor.getUniqueIdentifier());
IStatus status = new Status(IStatus.ERROR, Platform.PI_RUNTIME, IStatus.ERROR, message, exception);
getInternalPlatform ().log(status);
}
/**
* Starts up this plug-in.
*
* This method should be overridden in subclasses that need to do something * when this plug-in is started. Implementors should call the inherited method * to ensure that any system requirements can be met. *
** If this method throws an exception, it is taken as an indication that * plug-in initialization has failed; as a result, the plug-in will not * be activated; moreover, the plug-in will be marked as disabled and * ineligible for activation for the duration. *
*
* Plug-in startup code should be robust. In the event of a startup failure,
* the plug-in's shutdown
method will be invoked automatically,
* in an attempt to close open files, etc.
*
* Note 1: This method is automatically invoked by the platform * the first time any code in the plug-in is executed. *
** Note 2: This method is intended to perform simple initialization * of the plug-in environment. The platform may terminate initializers * that do not complete in a timely fashion. *
** Note 3: The class loader typically has monitors acquired during invocation of this method. It is * strongly recommended that this method avoid synchronized blocks or other thread locking mechanisms, * as this would lead to deadlock vulnerability. *
* Clients must never explicitly call this method. *
*
* @exception CoreException if this plug-in did not start up properly
* @deprecated
* In Eclipse 3.0 this method has been replaced by {@link Plugin#start(BundleContext context)}.
* Implementations of startup()
should be changed to extend
* start(BundleContext context)
and call super.start(context)
* instead of super.startup()
.
* The startup()
method is called only for plug-ins which explicitly require the
* org.eclipse.core.runtime.compatibility plug-in.
*/
public void startup() throws CoreException {
}
/**
* Returns a string representation of the plug-in, suitable
* for debugging purposes only.
*/
public String toString() {
String name = bundle.getSymbolicName();
return name == null ? new Long(bundle.getBundleId()).toString() : name;
}
/**
* Starts up this plug-in.
*
* This method should be overridden in subclasses that need to do something * when this plug-in is started. Implementors should call the inherited method * at the first possible point to ensure that any system requirements can be met. *
** If this method throws an exception, it is taken as an indication that * plug-in initialization has failed; as a result, the plug-in will not * be activated; moreover, the plug-in will be marked as disabled and * ineligible for activation for the duration. *
*
* Plug-in startup code should be robust. In the event of a startup failure,
* the plug-in's shutdown
method will be invoked automatically,
* in an attempt to close open files, etc.
*
* Note 1: This method is automatically invoked by the platform * the first time any code in the plug-in is executed. *
** Note 2: This method is intended to perform simple initialization * of the plug-in environment. The platform may terminate initializers * that do not complete in a timely fashion. *
** Note 3: The class loader typically has monitors acquired during invocation of this method. It is * strongly recommended that this method avoid synchronized blocks or other thread locking mechanisms, * as this would lead to deadlock vulnerability. *
** Note 4: The supplied bundle context represents the plug-in to the OSGi framework. * For security reasons, it is strongly recommended that this object should not be divulged. *
** Note 5: This method and the {@link #stop(BundleContext)} may be called from separate threads, * but the OSGi framework ensures that both methods will not be called simultaneously. *
* Clients must never explicitly call this method. * * @param context the bundle context for this plug-in * @exception Exception if this plug-in did not start up properly * @since 3.0 */ public void start(BundleContext context) throws Exception { bundle = context.getBundle(); String symbolicName = bundle.getSymbolicName(); if (symbolicName != null) { String key = symbolicName + "/debug"; //$NON-NLS-1$ String value = getInternalPlatform ().getOption(key); this.debug = value == null ? false : value.equalsIgnoreCase("true"); //$NON-NLS-1$ } initializeDescriptor(symbolicName); } /** * @deprecated Marked as deprecated to suppress deprecation warnings. */ private IPluginDescriptor initializeDescriptor(String symbolicName) { if (CompatibilityHelper.initializeCompatibility() == null) return null; //This associate a descriptor to any real bundle that uses this to start if (symbolicName == null) return null; IPluginDescriptor tmp = CompatibilityHelper.getPluginDescriptor(symbolicName); //Runtime descriptor is never set to support dynamic re-installation of compatibility if (!symbolicName.equals(Platform.PI_RUNTIME)) descriptor = tmp; CompatibilityHelper.setPlugin(tmp, this); CompatibilityHelper.setActive(tmp); return tmp; } /** * Stops this plug-in. ** This method should be re-implemented in subclasses that need to do something * when the plug-in is shut down. Implementors should call the inherited method * as late as possible to ensure that any system requirements can be met. *
** Plug-in shutdown code should be robust. In particular, this method * should always make an effort to shut down the plug-in. Furthermore, * the code should not assume that the plug-in was started successfully, * as this method will be invoked in the event of a failure during startup. *
** Note 1: If a plug-in has been automatically started, this method will be automatically * invoked by the platform when the platform is shut down. *
** Note 2: This method is intended to perform simple termination * of the plug-in environment. The platform may terminate invocations * that do not complete in a timely fashion. *
** Note 3: The supplied bundle context represents the plug-in to the OSGi framework. * For security reasons, it is strongly recommended that this object should not be divulged. *
** Note 4: This method and the {@link #start(BundleContext)} may be called from separate threads, * but the OSGi framework ensures that both methods will not be called simultaneously. *
* Clients must never explicitly call this method. * * @param context the bundle context for this plug-in * @exception Exception if this method fails to shut down this plug-in * @since 3.0 */ public void stop(BundleContext context) throws Exception { // sub-classes to override } /** * Returns the bundle associated with this plug-in. * * @return the associated bundle * @since 3.0 */ public Bundle getBundle() { return bundle; } }