| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.core.internal.plugins; |
| |
| import java.io.IOException; |
| import java.lang.reflect.Constructor; |
| import java.net.URL; |
| import java.util.*; |
| import org.eclipse.core.internal.boot.PlatformURLHandler; |
| import org.eclipse.core.internal.runtime.*; |
| import org.eclipse.core.internal.runtime.InternalPlatform; |
| import org.eclipse.core.internal.runtime.Policy; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.osgi.service.resolver.BundleDescription; |
| import org.eclipse.osgi.service.resolver.BundleSpecification; |
| import org.eclipse.osgi.util.ManifestElement; |
| import org.osgi.framework.*; |
| |
| public class PluginDescriptor implements IPluginDescriptor { |
| |
| private static final String PLUGIN_CLASS = "Plugin-Class"; //$NON-NLS-1$ |
| protected Plugin pluginObject = null; // plugin object |
| private Bundle bundleOsgi; |
| |
| //The three following fields can't be replaced by a test to the bundle state. |
| private boolean active = false; // plugin is active |
| private volatile boolean activePending = false; // being activated |
| private boolean deactivated = false; // plugin deactivated due to startup errors |
| private ResourceBundle resources = null; |
| private PluginClassLoader classLoader; |
| |
| // constants |
| static final String PLUGIN_URL = PlatformURLHandler.PROTOCOL + PlatformURLHandler.PROTOCOL_SEPARATOR + "/" + PlatformURLPluginConnection.PLUGIN + "/"; //$NON-NLS-1$ //$NON-NLS-2$ |
| static final String VERSION_SEPARATOR = "_"; //$NON-NLS-1$ |
| |
| synchronized public void doPluginDeactivation() { |
| pluginObject = null; |
| active = false; |
| activePending = false; |
| deactivated = false; |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public IExtension getExtension(String id) { |
| IExtension[] exts = getExtensions(); |
| for (int i = 0; i < exts.length; i++) { |
| if (exts[i].getSimpleIdentifier().equals(id)) |
| return exts[i]; |
| } |
| return null; |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public IExtensionPoint getExtensionPoint(String extensionPointId) { |
| return InternalPlatform.getDefault().getRegistry().getExtensionPoint(getId(), extensionPointId); |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public IExtensionPoint[] getExtensionPoints() { |
| return InternalPlatform.getDefault().getRegistry().getExtensionPoints(getId()); |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public IExtension[] getExtensions() { |
| return org.eclipse.core.internal.runtime.InternalPlatform.getDefault().getRegistry().getExtensions(getId()); |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public URL getInstallURL() { |
| try { |
| return new URL(PLUGIN_URL + toString() + '/'); |
| } catch (IOException e) { |
| throw new IllegalStateException(); // unchecked |
| } |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public String getLabel() { |
| return (String) bundleOsgi.getHeaders().get(Constants.BUNDLE_NAME); |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public ClassLoader getPluginClassLoader() { |
| synchronized (this) { |
| if (classLoader == null) |
| classLoader = new PluginClassLoader(this); |
| } |
| return classLoader; |
| } |
| |
| public PluginRegistry getPluginRegistry() { |
| return (PluginRegistry) org.eclipse.core.internal.plugins.InternalPlatform.getPluginRegistry(); |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public String getProviderName() { |
| return (String) bundleOsgi.getHeaders().get(Constants.BUNDLE_VENDOR); |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public ResourceBundle getResourceBundle() throws MissingResourceException { |
| if (resources==null) |
| resources = ResourceTranslator.getResourceBundle(bundleOsgi); |
| return resources; |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public String getResourceString(String value) { |
| return ResourceTranslator.getResourceString(bundleOsgi, value); |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public String getResourceString(String value, ResourceBundle b) { |
| return ResourceTranslator.getResourceString(bundleOsgi, value, b); |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public ILibrary[] getRuntimeLibraries() { |
| Bundle[] allBundles; |
| Bundle[] fragments = InternalPlatform.getDefault().getFragments(bundleOsgi); |
| if (fragments != null) { |
| allBundles = new Bundle[fragments.length + 1]; |
| allBundles[0] = bundleOsgi; |
| System.arraycopy(fragments, 0, allBundles, 1, fragments.length); |
| } else |
| allBundles = new Bundle[] {bundleOsgi}; |
| ArrayList allLibraries = new ArrayList(); |
| for (int i = 0; i < allBundles.length; i++) |
| try { |
| ManifestElement[] classpathElements = ManifestElement.parseHeader(Constants.BUNDLE_CLASSPATH, (String) allBundles[i].getHeaders("").get(Constants.BUNDLE_CLASSPATH)); //$NON-NLS-1$ |
| if (classpathElements == null) |
| continue; |
| for (int j = 0; j < classpathElements.length; j++) |
| allLibraries.add(new Library(classpathElements[j].getValue())); |
| } catch (BundleException e) { |
| //Ignore because by the time we get here the errors will have already been logged. |
| } |
| return (ILibrary[]) allLibraries.toArray(new ILibrary[allLibraries.size()]); |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public String getUniqueIdentifier() { |
| return getId(); |
| } |
| |
| /** |
| * @see #toString() |
| */ |
| public static String getUniqueIdentifierFromString(String pluginString) { |
| int ix = pluginString.indexOf(VERSION_SEPARATOR); |
| return ix == -1 ? pluginString : pluginString.substring(0, ix); |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public PluginVersionIdentifier getVersionIdentifier() { |
| String version = (String) bundleOsgi.getHeaders("").get(Constants.BUNDLE_VERSION); //$NON-NLS-1$ |
| try { |
| return new PluginVersionIdentifier(version); |
| } catch (Exception e) { |
| return new PluginVersionIdentifier("1.0.0"); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * @see #toString() |
| */ |
| public static PluginVersionIdentifier getVersionIdentifierFromString(String pluginString) { |
| return new PluginVersionIdentifier(pluginString); |
| } |
| |
| public IPluginPrerequisite[] getPluginPrerequisites() { |
| BundleDescription description = Platform.getPlatformAdmin().getState(false).getBundle(bundleOsgi.getBundleId()); |
| BundleSpecification[] specs = description.getRequiredBundles(); |
| |
| IPluginPrerequisite[] resolvedPrerequisites = new IPluginPrerequisite[specs.length]; |
| for (int j = 0; j < specs.length; j++) |
| resolvedPrerequisites[j] = new PluginPrerequisite(specs[j]); |
| return resolvedPrerequisites; |
| } |
| |
| /** |
| * Returns true if the plugin is active or is currently in the process of being |
| * activated, and false otherwse. |
| * NOTE: This method is not synchronized because it is called from within a |
| * sync block in PluginClassLoader. |
| */ |
| boolean hasActivationStarted() { |
| return activePending || active; |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public synchronized boolean isPluginActivated() { |
| //note that this method is synchronized for good reason. |
| //During plugin activation, neither true nor false would be valid |
| //return values for this method, so it must block until activation |
| //completes. For example, returning false during activation |
| //would break the registry shutdown procedure, because a |
| //plugin being activated during shutdown would never be shut down. |
| return bundleOsgi.getState() == Bundle.ACTIVE; |
| } |
| |
| /* |
| * NOTE: This method is not synchronized because it is called from within a |
| * sync block in PluginClassLoader. |
| */ |
| public boolean isPluginDeactivated() { |
| return deactivated; |
| } |
| |
| private void logError(IStatus status) { |
| InternalPlatform.getDefault().getLog(org.eclipse.core.internal.runtime.InternalPlatform.getDefault().getBundleContext().getBundle()).log(status); |
| } |
| |
| /** |
| * Returns <code>true</code> if we should continue with the plugin activation. |
| */ |
| private boolean pluginActivationEnter() throws CoreException { |
| if (deactivated) { |
| // had permanent error on startup |
| String errorMsg = Policy.bind("plugin.pluginDisabled", getId()); //$NON-NLS-1$ |
| throwException(errorMsg, null); |
| } |
| if (active || activePending) { |
| // already up and running |
| return false; |
| } |
| activePending = true; |
| // go ahead and try to activate |
| return true; |
| } |
| |
| private void pluginActivationExit(boolean errorExit) { |
| if (errorExit) { |
| active = false; |
| deactivated = true; |
| } else |
| active = true; |
| // we are done with the activation |
| activePending = false; |
| } |
| |
| private void throwException(String message, Throwable exception) throws CoreException { |
| IStatus status = new Status(IStatus.ERROR, Platform.PI_RUNTIME, Platform.PLUGIN_ERROR, message, exception); |
| logError(status); |
| throw new CoreException(status); |
| } |
| |
| /** |
| * @see #getUniqueIdentifierFromString(String) |
| * @see #getVersionIdentifierFromString(String) |
| */ |
| public String toString() { |
| return getUniqueIdentifier() + VERSION_SEPARATOR + getVersionIdentifier().toString(); |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public final URL find(IPath path) { |
| URL result = FindSupport.find(bundleOsgi, path); |
| if (result != null) |
| try { |
| result = Platform.resolve(result); |
| } catch (IOException e) { |
| // if the URL cannot be resolved for some reason, return the original result. |
| } |
| return result; |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public final URL find(IPath path, Map override) { |
| URL result = FindSupport.find(bundleOsgi, path, override); |
| if (result != null) |
| try { |
| result = Platform.resolve(result); |
| } catch (IOException e) { |
| // if the URL cannot be resolved for some reason, return the original result. |
| } |
| return result; |
| } |
| |
| /** |
| * @see IPluginDescriptor |
| */ |
| public Plugin getPlugin() throws CoreException { |
| if (pluginObject == null) |
| doPluginActivation(); |
| return pluginObject; |
| } |
| |
| synchronized void doPluginActivation() throws CoreException { |
| //This class is only called when getPlugin() is invoked. |
| // It needs to handle the case where it is called multiple times during the activation |
| // processing itself (as a result of other classes from this |
| // plugin being directly referenced by the plugin class) |
| |
| // NOTE: there is a remote scenario where the plugin class can |
| // deadlock, if it starts separate thread(s) within its |
| // constructor or startup() method, and waits on those |
| // threads before returning (ie. calls join()). |
| |
| // sanity checking |
| if ((bundleOsgi.getState() & (Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE)) == 0) |
| throw new IllegalArgumentException(); |
| // plug-in hasn't been activated yet - start bundle |
| if (bundleOsgi.getState() == Bundle.RESOLVED) |
| try { |
| bundleOsgi.start(); |
| } catch (BundleException e) { |
| throwException(Policy.bind("plugin.startupProblems", e.toString()), e); //$NON-NLS-1$ |
| } |
| if (pluginObject != null) |
| return; |
| boolean errorExit = true; |
| // check if already activated or pending |
| if (pluginActivationEnter()) { |
| try { |
| internalDoPluginActivation(); |
| errorExit = false; |
| } finally { |
| pluginActivationExit(errorExit); |
| } |
| } else { |
| //Create a fake plugin object for all new bundles that do not use the Plugin class in their activator hierarchy |
| if (active && pluginObject == null) { |
| active = false; |
| pluginObject = new DefaultPlugin(this); |
| active = true; |
| } |
| } |
| |
| } |
| |
| private String getPluginClass() { |
| return (String) bundleOsgi.getHeaders("").get(PLUGIN_CLASS); //$NON-NLS-1$ |
| } |
| |
| private String getId() { |
| return bundleOsgi.getSymbolicName(); |
| } |
| |
| private void internalDoPluginActivation() throws CoreException { |
| String errorMsg; |
| // load the runtime class |
| String pluginClassName = getPluginClass(); |
| Class runtimeClass = null; |
| try { |
| if (pluginClassName == null || pluginClassName.equals("")) {//$NON-NLS-1$ |
| runtimeClass = DefaultPlugin.class; |
| pluginClassName = DefaultPlugin.class.getName(); |
| } |
| else |
| runtimeClass = bundleOsgi.loadClass(pluginClassName); |
| } catch (ClassNotFoundException e) { |
| errorMsg = Policy.bind("plugin.loadClassError", getId(), pluginClassName); //$NON-NLS-1$ |
| throwException(errorMsg, e); |
| } |
| |
| // find the correct constructor |
| Constructor construct = null; |
| try { |
| construct = runtimeClass.getConstructor(new Class[] {IPluginDescriptor.class}); |
| } catch (NoSuchMethodException eNoConstructor) { |
| errorMsg = Policy.bind("plugin.instantiateClassError", getId(), pluginClassName); //$NON-NLS-1$ |
| throwException(errorMsg, eNoConstructor); |
| } |
| |
| // create a new instance |
| try { |
| pluginObject = (Plugin) construct.newInstance(new Object[] {this}); |
| } catch (ClassCastException e) { |
| errorMsg = Policy.bind("plugin.notPluginClass", pluginClassName); //$NON-NLS-1$ |
| throwException(errorMsg, e); |
| } catch (Exception e) { |
| errorMsg = Policy.bind("plugin.instantiateClassError", getId(), pluginClassName); //$NON-NLS-1$ |
| throwException(errorMsg, e); |
| } |
| } |
| |
| public PluginDescriptor(org.osgi.framework.Bundle b) { |
| bundleOsgi = b; |
| if ((b.getState() & Bundle.ACTIVE) != 0) |
| active = true; |
| } |
| |
| public Bundle getBundle() { |
| return bundleOsgi; |
| } |
| |
| public void setPlugin(Plugin object) { |
| pluginObject = object; |
| } |
| |
| public synchronized void setActive() { |
| this.active = true; |
| } |
| |
| public boolean hasPluginObject() { |
| return pluginObject!=null; |
| } |
| } |