blob: ae179313f1f819a4da0f91f5661527c68ccc72dc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 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.runtime.adaptor;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.osgi.framework.adaptor.*;
import org.eclipse.osgi.framework.adaptor.core.AdaptorElementFactory;
import org.eclipse.osgi.framework.console.CommandProvider;
import org.eclipse.osgi.framework.debug.Debug;
import org.eclipse.osgi.framework.debug.DebugOptions;
import org.eclipse.osgi.framework.internal.defaultadaptor.DefaultAdaptor;
import org.eclipse.osgi.framework.internal.defaultadaptor.DefaultBundleData;
import org.eclipse.osgi.framework.log.FrameworkLog;
import org.eclipse.osgi.framework.log.FrameworkLogEntry;
import org.eclipse.osgi.framework.stats.StatsManager;
import org.eclipse.osgi.internal.resolver.StateImpl;
import org.eclipse.osgi.internal.resolver.StateManager;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.osgi.service.pluginconversion.PluginConverter;
import org.eclipse.osgi.service.resolver.*;
import org.eclipse.osgi.service.urlconversion.URLConverter;
import org.osgi.framework.*;
/**
* Internal class.
*/
public class EclipseAdaptor extends DefaultAdaptor {
public static final String PROP_CLEAN = "osgi.clean"; //$NON-NLS-1$
public static final String PROP_EXITONERROR = "eclipse.exitOnError"; //$NON-NLS-1$
static final String F_LOG = ".log"; //$NON-NLS-1$
//TODO rename it to Eclipse-PluginClass
public static final String PLUGIN_CLASS = "Plugin-Class"; //$NON-NLS-1$
public static final String ECLIPSE_AUTOSTART = "Eclipse-AutoStart"; //$NON-NLS-1$
//TODO rename constant to ECLIPSE_AUTOSTART_EXCEPTIONS
public static final String ECLIPSE_AUTOSTART_EXCEPTIONS = "exceptions"; //$NON-NLS-1$
public static final String SAXFACTORYNAME = "javax.xml.parsers.SAXParserFactory"; //$NON-NLS-1$
public static final String DOMFACTORYNAME = "javax.xml.parsers.DocumentBuilderFactory"; //$NON-NLS-1$
private static final String RUNTIME_ADAPTOR = FRAMEWORK_SYMBOLICNAME + "/eclipseadaptor"; //$NON-NLS-1$
private static final String OPTION_STATE_READER = RUNTIME_ADAPTOR + "/state/reader";//$NON-NLS-1$
private static final String OPTION_RESOLVER = RUNTIME_ADAPTOR + "/resolver/timing"; //$NON-NLS-1$
private static final String OPTION_PLATFORM_ADMIN = RUNTIME_ADAPTOR + "/debug/platformadmin"; //$NON-NLS-1$
private static final String OPTION_PLATFORM_ADMIN_RESOLVER = RUNTIME_ADAPTOR + "/debug/platformadmin/resolver"; //$NON-NLS-1$
private static final String OPTION_MONITOR_PLATFORM_ADMIN = RUNTIME_ADAPTOR + "/resolver/timing"; //$NON-NLS-1$
private static final String OPTION_RESOLVER_READER = RUNTIME_ADAPTOR + "/resolver/reader/timing"; //$NON-NLS-1$
private static final String OPTION_CONVERTER = RUNTIME_ADAPTOR + "/converter/debug"; //$NON-NLS-1$
private static final String OPTION_LOCATION = RUNTIME_ADAPTOR + "/debug/location"; //$NON-NLS-1$
public static final byte BUNDLEDATA_VERSION = 10;
public static final byte NULL = 0;
public static final byte OBJECT = 1;
private static EclipseAdaptor instance;
private long timeStamp = 0;
private String installURL = null;
private boolean exitOnError = true;
private BundleStopper stopper;
/*
* Should be instantiated only by the framework (through reflection).
*/
public EclipseAdaptor(String[] args) {
super(args);
instance = this;
setDebugOptions();
}
public static EclipseAdaptor getDefault() {
return instance;
}
public void initialize(EventPublisher eventPublisher) {
if (Boolean.getBoolean(EclipseAdaptor.PROP_CLEAN))
cleanOSGiCache();
super.initialize(eventPublisher);
}
public void initializeMetadata() {
// do nothing here; metadata is already initialized by readHeaders.
}
protected void initBundleStoreRootDir() {
File configurationLocation = LocationManager.getOSGiConfigurationDir();
if (configurationLocation != null) {
bundleStoreRootDir = new File(configurationLocation, LocationManager.BUNDLES_DIR);
bundleStore = bundleStoreRootDir.getAbsolutePath();
} else {
// last resort just default to "bundles"
bundleStore = LocationManager.BUNDLES_DIR;
bundleStoreRootDir = new File(bundleStore);
}
/* store bundleStore back into adaptor properties for others to see */
properties.put(BUNDLE_STORE, bundleStoreRootDir.getAbsolutePath());
}
protected FrameworkLog createFrameworkLog() {
if (frameworkLog != null)
return frameworkLog;
return EclipseStarter.createFrameworkLog();
}
protected StateManager createStateManager() {
readHeaders();
checkLocationAndReinitialize();
File stateLocation = LocationManager.getConfigurationFile(LocationManager.STATE_FILE);
if (!stateLocation.isFile()) { //NOTE this check is redundant since it is done in StateManager, however it is more convenient to have it here
Location parentConfiguration = null;
if ((parentConfiguration = LocationManager.getConfigurationLocation().getParentLocation()) != null) {
stateLocation = new File(parentConfiguration.getURL().getFile(), FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME + '/' + LocationManager.STATE_FILE);
}
}
stateManager = new StateManager(stateLocation, timeStamp);
stateManager.setInstaller(new EclipseBundleInstaller());
StateImpl systemState = stateManager.getSystemState();
if (systemState != null)
return stateManager;
systemState = stateManager.createSystemState();
BundleData[] installedBundles = getInstalledBundles();
if (installedBundles == null)
return stateManager;
StateObjectFactory factory = stateManager.getFactory();
for (int i = 0; i < installedBundles.length; i++) {
BundleData toAdd = (BundleData) installedBundles[i];
try {
Dictionary manifest = toAdd.getManifest();
BundleDescription newDescription = factory.createBundleDescription(manifest, toAdd.getLocation(), toAdd.getBundleID());
systemState.addBundle(newDescription);
} catch (BundleException be) {
// just ignore bundle datas with invalid manifests
}
}
// we need the state resolved
systemState.setTimeStamp(timeStamp);
systemState.resolve();
return stateManager;
}
public void shutdownStateManager() {
try {
File stateLocation = LocationManager.getConfigurationFile(LocationManager.STATE_FILE);
stateManager.shutdown(stateLocation); //$NON-NLS-1$
} catch (IOException e) {
frameworkLog.log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e));
}
}
private void cleanOSGiCache() {
File osgiConfig = LocationManager.getOSGiConfigurationDir();
if (!rm(osgiConfig)) {
// TODO log error?
}
}
private void checkLocationAndReinitialize() {
if (installURL == null) {
installURL = EclipseStarter.getSysPath(); //TODO This reference to the starter should be avoided
return;
}
if (!EclipseStarter.getSysPath().equals(installURL)) {
//delete the metadata file and the framework file when the location of the basic bundles has changed
LocationManager.getConfigurationFile(LocationManager.BUNDLE_DATA_FILE).delete();
LocationManager.getConfigurationFile(LocationManager.STATE_FILE).delete();
installURL = EclipseStarter.getSysPath();
}
}
private void readHeaders() {
InputStream bundleDataStream = findBundleDataFile();
if (bundleDataStream == null)
return;
try {
DataInputStream in = new DataInputStream(new BufferedInputStream(bundleDataStream));
try {
if (in.readByte() == BUNDLEDATA_VERSION) {
timeStamp = in.readLong();
installURL = in.readUTF();
initialBundleStartLevel = in.readInt();
nextId = in.readLong();
}
} finally {
in.close();
}
} catch (IOException e) {
if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
Debug.println("Error reading framework metadata: " + e.getMessage()); //$NON-NLS-1$
Debug.printStackTrace(e);
}
}
}
public AdaptorElementFactory getElementFactory() {
if (elementFactory == null)
elementFactory = new EclipseElementFactory();
return elementFactory;
}
public void frameworkStart(BundleContext context) throws BundleException {
super.frameworkStart(context);
Bundle bundle = context.getBundle();
Location location;
// Less than optimal reference to EclipseStarter here. Not sure how we can make the location
// objects available. They are needed very early in EclipseStarter but these references tie
// the adaptor to that starter.
location = LocationManager.getUserLocation();
Hashtable properties = new Hashtable(1);
if (location != null) {
properties.put("type", LocationManager.PROP_USER_AREA); //$NON-NLS-1$
context.registerService(Location.class.getName(), location, properties);
}
location = LocationManager.getInstanceLocation();
if (location != null) {
properties.put("type", LocationManager.PROP_INSTANCE_AREA); //$NON-NLS-1$
context.registerService(Location.class.getName(), location, properties);
}
location = LocationManager.getConfigurationLocation();
if (location != null) {
properties.put("type", LocationManager.PROP_CONFIG_AREA); //$NON-NLS-1$
context.registerService(Location.class.getName(), location, properties);
}
location = LocationManager.getInstallLocation();
if (location != null) {
properties.put("type", LocationManager.PROP_INSTALL_AREA); //$NON-NLS-1$
context.registerService(Location.class.getName(), location, properties);
}
register(org.eclipse.osgi.service.environment.EnvironmentInfo.class.getName(), EnvironmentInfo.getDefault(), bundle);
register(PlatformAdmin.class.getName(), stateManager, bundle);
register(PluginConverter.class.getName(), new PluginConverterImpl(context), bundle);
register(URLConverter.class.getName(), new URLConverterImpl(), bundle);
register(CommandProvider.class.getName(), new EclipseCommandProvider(context), bundle);
register(FrameworkLog.class.getName(), getFrameworkLog(), bundle);
register(org.eclipse.osgi.service.localization.BundleLocalization.class.getName(), new BundleLocalizationImpl(), bundle);
registerEndorsedXMLParser();
}
private void setDebugOptions() {
DebugOptions options = DebugOptions.getDefault();
// may be null if debugging is not enabled
if (options == null)
return;
StateManager.DEBUG = options != null;
StateManager.DEBUG_READER = options.getBooleanOption(OPTION_RESOLVER_READER, false);
StateManager.MONITOR_PLATFORM_ADMIN = options.getBooleanOption(OPTION_MONITOR_PLATFORM_ADMIN, false);
StateManager.DEBUG_PLATFORM_ADMIN = options.getBooleanOption(OPTION_PLATFORM_ADMIN, false);
StateManager.DEBUG_PLATFORM_ADMIN_RESOLVER = options.getBooleanOption(OPTION_PLATFORM_ADMIN_RESOLVER, false);
PluginConverterImpl.DEBUG = options.getBooleanOption(OPTION_CONVERTER, false);
BasicLocation.DEBUG = options.getBooleanOption(OPTION_LOCATION, false);
}
private void registerEndorsedXMLParser() {
try {
Class.forName(SAXFACTORYNAME);
context.registerService(SAXFACTORYNAME, new SaxParsingService(), new Hashtable());
Class.forName(DOMFACTORYNAME);
context.registerService(DOMFACTORYNAME, new DomParsingService(), new Hashtable());
} catch (ClassNotFoundException e) {
// In case the JAXP API is not on the boot classpath
String message = EclipseAdaptorMsg.formatter.getString("ECLIPSE_ADAPTOR_ERROR_XML_SERVICE"); //$NON-NLS-1$
getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, message, 0, e, null));
}
}
private class SaxParsingService implements ServiceFactory {
public Object getService(Bundle bundle, ServiceRegistration registration) {
return SAXParserFactory.newInstance();
}
public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
//Do nothing.
}
}
private class DomParsingService implements ServiceFactory {
public Object getService(Bundle bundle, ServiceRegistration registration) {
return DocumentBuilderFactory.newInstance();
}
public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
//Do nothing.
}
}
public void frameworkStop(BundleContext context) throws BundleException {
saveMetaData();
super.frameworkStop(context);
printStats();
PluginParser.releaseXMLParsing();
}
private void printStats() {
DebugOptions debugOptions = DebugOptions.getDefault();
if (debugOptions == null)
return;
String registryParsing = debugOptions.getOption("org.eclipse.core.runtime/registry/parsing/timing/value"); //$NON-NLS-1$
if (registryParsing != null)
EclipseAdaptorMsg.debug("Time spent in registry parsing: " + registryParsing); //$NON-NLS-1$
String packageAdminResolution = debugOptions.getOption("debug.packageadmin/timing/value"); //$NON-NLS-1$
if (packageAdminResolution != null)
System.out.println("Time spent in package admin resolve: " + packageAdminResolution); //$NON-NLS-1$
String constraintResolution = debugOptions.getOption("org.eclipse.core.runtime.adaptor/resolver/timing/value"); //$NON-NLS-1$
if (constraintResolution != null)
System.out.println("Time spent resolving the dependency system: " + constraintResolution); //$NON-NLS-1$
}
private InputStream findBundleDataFile() {
File metadata = LocationManager.getConfigurationFile(LocationManager.BUNDLE_DATA_FILE);
InputStream bundleDataStream = null;
if (metadata.isFile()) {
try {
bundleDataStream = new FileInputStream(metadata);
} catch (FileNotFoundException e1) {
//this can not happen since it is tested before entering here.
}
} else {
Location parentConfiguration = null;
if ((parentConfiguration = LocationManager.getConfigurationLocation().getParentLocation()) != null) {
try {
bundleDataStream = new URL(parentConfiguration.getURL(), FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME + '/' + LocationManager.BUNDLE_DATA_FILE).openStream();
} catch (MalformedURLException e1) {
//This will not happen since all the URLs are derived by us and we are GODS!
} catch (IOException e1) {
//That's ok we will regenerate the .bundleData
}
}
}
return bundleDataStream;
}
/**
* @see org.eclipse.osgi.framework.adaptor.FrameworkAdaptor#getInstalledBundles()
*/
public BundleData[] getInstalledBundles() {
InputStream bundleDataStream = findBundleDataFile();
if (bundleDataStream == null)
return null;
try {
DataInputStream in = new DataInputStream(new BufferedInputStream(bundleDataStream));
try {
if (in.readByte() != BUNDLEDATA_VERSION)
return null;
// skip timeStamp - was read by readTimeStamp
in.readLong();
in.readUTF();
in.readInt();
in.readLong();
int bundleCount = in.readInt();
ArrayList result = new ArrayList(bundleCount);
long id = -1;
State state = stateManager.getSystemState();
long stateTimeStamp = state.getTimeStamp();
for (int i = 0; i < bundleCount; i++) {
try {
try {
id = in.readLong();
if (id != 0) {
EclipseBundleData data = (EclipseBundleData) getElementFactory().createBundleData(this, id);
loadMetaDataFor(data, in);
data.initializeExistingBundle();
if (Debug.DEBUG && Debug.DEBUG_GENERAL)
Debug.println("BundleData created: " + data); //$NON-NLS-1$
result.add(data);
}
} catch (NumberFormatException e) {
// should never happen
}
} catch (IOException e) {
state.removeBundle(id);
if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
Debug.println("Error reading framework metadata: " + e.getMessage()); //$NON-NLS-1$
Debug.printStackTrace(e);
}
}
}
if (stateTimeStamp != state.getTimeStamp())
state.resolve(false); //time stamp changed force a full resolve
return (BundleData[]) result.toArray(new BundleData[result.size()]);
} finally {
in.close();
}
} catch (IOException e) {
if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
Debug.println("Error reading framework metadata: " + e.getMessage()); //$NON-NLS-1$
Debug.printStackTrace(e);
}
}
return null;
}
protected void loadMetaDataFor(EclipseBundleData data, DataInputStream in) throws IOException {
byte flag = in.readByte();
if (flag == NULL)
return;
data.setLocation(readString(in, false));
data.setFileName(readString(in, false));
data.setSymbolicName(readString(in, false));
data.setVersion(new Version(readString(in, false)));
data.setActivator(readString(in, false));
data.setAutoStart(in.readBoolean());
int exceptionsCount = in.readInt();
String[] autoStartExceptions = exceptionsCount > 0 ? new String[exceptionsCount] : null;
for (int i = 0; i < exceptionsCount; i++)
autoStartExceptions[i] = in.readUTF();
data.setAutoStartExceptions(autoStartExceptions);
data.setPluginClass(readString(in, false));
data.setClassPath(readString(in, false));
data.setNativePaths(readString(in, false));
data.setExecutionEnvironment(readString(in, false));
data.setDynamicImports(readString(in, false));
data.setGeneration(in.readInt());
data.setStartLevel(in.readInt());
data.setStatus(in.readInt());
data.setReference(in.readBoolean());
data.setFragment(in.readBoolean());
data.setManifestTimeStamp(in.readLong());
data.setManifestType(in.readByte());
}
public void saveMetaDataFor(DefaultBundleData data) throws IOException {
if (!((EclipseBundleData) data).isAutoStartable()) {
timeStamp--; //Change the value of the timeStamp, as a marker that something changed.
}
}
public void persistInitialBundleStartLevel(int value) {
// Change the value of the timeStamp, as a marker that something changed.
timeStamp--;
}
public void persistNextBundleID(long value) {
// Do nothing the timeStamp will have changed because the state will be updated.
}
protected void saveMetaDataFor(BundleData data, DataOutputStream out) throws IOException {
if (data.getBundleID() == 0 || !(data instanceof DefaultBundleData)) {
out.writeByte(NULL);
return;
}
EclipseBundleData bundleData = (EclipseBundleData) data;
out.writeByte(OBJECT);
writeStringOrNull(out, bundleData.getLocation());
writeStringOrNull(out, bundleData.getFileName());
writeStringOrNull(out, bundleData.getSymbolicName());
writeStringOrNull(out, bundleData.getVersion().toString());
writeStringOrNull(out, bundleData.getActivator());
out.writeBoolean(bundleData.isAutoStart());
String[] autoStartExceptions = bundleData.getAutoStartExceptions();
if (autoStartExceptions == null)
out.writeInt(0);
else {
out.writeInt(autoStartExceptions.length);
for (int i = 0; i < autoStartExceptions.length; i++)
out.writeUTF(autoStartExceptions[i]);
}
writeStringOrNull(out, bundleData.getPluginClass());
writeStringOrNull(out, bundleData.getClassPath());
writeStringOrNull(out, bundleData.getNativePathsString());
writeStringOrNull(out, bundleData.getExecutionEnvironment());
writeStringOrNull(out, bundleData.getDynamicImports());
out.writeInt(bundleData.getGeneration());
out.writeInt(bundleData.getStartLevel());
out.writeInt(bundleData.getPersistentStatus());
out.writeBoolean(bundleData.isReference());
out.writeBoolean(bundleData.isFragment());
out.writeLong(bundleData.getManifestTimeStamp());
out.writeByte(bundleData.getManifestType());
}
private String readString(DataInputStream in, boolean intern) throws IOException {
byte type = in.readByte();
if (type == NULL)
return null;
if (intern)
return in.readUTF().intern();
else
return in.readUTF();
}
private void writeStringOrNull(DataOutputStream out, String string) throws IOException {
if (string == null)
out.writeByte(NULL);
else {
out.writeByte(OBJECT);
out.writeUTF(string);
}
}
public void saveMetaData() {
File metadata = LocationManager.getConfigurationFile(LocationManager.BUNDLE_DATA_FILE);
// the cache and the state match
if (timeStamp == stateManager.getSystemState().getTimeStamp())
return;
try {
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(metadata)));
try {
out.writeByte(BUNDLEDATA_VERSION);
out.writeLong(stateManager.getSystemState().getTimeStamp());
out.writeUTF(installURL);
out.writeInt(initialBundleStartLevel);
out.writeLong(nextId);
Bundle[] bundles = context.getBundles();
out.writeInt(bundles.length);
for (int i = 0; i < bundles.length; i++) {
long id = bundles[i].getBundleId();
out.writeLong(id);
if (id != 0) {
BundleData data = ((org.eclipse.osgi.framework.internal.core.AbstractBundle) bundles[i]).getBundleData();
saveMetaDataFor(data, out);
}
}
} finally {
out.close();
}
} catch (IOException e) {
frameworkLog.log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e));
}
}
public BundleWatcher getBundleWatcher() {
return StatsManager.getDefault();
}
protected BundleContext getContext() {
return context;
}
public void frameworkStopping(BundleContext context) {
super.frameworkStopping(context);
stopper = new BundleStopper();
stopper.stopBundles();
}
public void handleRuntimeError(Throwable error) {
try {
// check the prop each time this happens (should NEVER happen!)
exitOnError = Boolean.valueOf(System.getProperty(PROP_EXITONERROR, "true")).booleanValue(); //$NON-NLS-1$
String message = EclipseAdaptorMsg.formatter.getString("ECLIPSE_ADAPTOR_RUNTIME_ERROR"); //$NON-NLS-1$
FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, message, 0, error, null);
frameworkLog.log(logEntry);
} catch (Throwable t) {
// we may be in a currupted state and must be able to handle any errors (ie OutOfMemoryError)
// that may occur when handling the first error; this is REALLY the last resort.
try {
error.printStackTrace();
t.printStackTrace();
} catch (Throwable t1) {
// if we fail that then we are beyond help.
}
} finally {
// do the exit outside the try block just incase another runtime error was thrown while logging
if (exitOnError)
System.exit(13);
}
}
protected void setLog(FrameworkLog log) {
frameworkLog = log;
}
public BundleStopper getBundleStopper() {
return stopper;
}
}