blob: f9859bbf758c1fc3707f2a039b066e4f5e21c45a [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.update.internal.configurator;
import java.io.*;
import java.net.*;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.osgi.service.datalocation.*;
import org.eclipse.osgi.service.debug.*;
import org.eclipse.update.configurator.*;
import org.osgi.framework.*;
import org.osgi.service.packageadmin.*;
import org.osgi.service.startlevel.*;
import org.osgi.util.tracker.*;
public class ConfigurationActivator implements BundleActivator, IBundleGroupProvider, IConfigurationConstants {
public static String PI_CONFIGURATOR = "org.eclipse.update.configurator";
public static final String INSTALL_LOCATION = "osgi.installLocation";
public static final String LAST_CONFIG_STAMP = "last.config.stamp";
public static final String NAME_SPACE = "org.eclipse.update";
// debug options
public static String OPTION_DEBUG = PI_CONFIGURATOR + "/debug";
// debug values
public static boolean DEBUG = false;
// os
private static boolean isWindows = System.getProperty("os.name").startsWith("Win");
private static BundleContext context;
private ServiceTracker platformTracker;
private ServiceRegistration configurationFactorySR;
private IPlatform platform;
private PlatformConfiguration configuration;
// Install location
private static URL installURL;
// Location of the configuration data
private Location configLocation;
//Need to store that because it is not provided by the platformConfiguration
private long lastTimeStamp;
// Singleton
private static ConfigurationActivator configurator;
public ConfigurationActivator() {
configurator = this;
}
public void start(BundleContext ctx) throws Exception {
context = ctx;
loadOptions();
initialize();
//Short cut, if the configuration has not changed
String application = configuration.getApplicationIdentifier();
String product = configuration.getPrimaryFeatureIdentifier();
if (canRunWithCachedData()) {
Utils.debug("Same last time stamp *****");
if (System.getProperty(ECLIPSE_APPLICATION) == null && application != null) {
Utils.debug("no eclipse.application, setting it and returning");
System.setProperty(ECLIPSE_APPLICATION, application);
}
if (System.getProperty(ECLIPSE_PRODUCT) == null && product != null) {
Utils.debug("no eclipse.product, setting it and returning");
System.setProperty(ECLIPSE_PRODUCT, product);
}
return;
}
Utils.debug("Starting update configurator...");
installBundles();
platform.registerBundleGroupProvider(this);
}
private void initialize() throws Exception {
platform = acquirePlatform();
if (platform==null)
throw new Exception("Can not start");
installURL = platform.getInstallURL();
configLocation = platform.getConfigurationLocation();
// create the name space directory for update (configuration/org.eclipse.update)
if (!configLocation.isReadOnly()) {
try {
URL privateURL = new URL(configLocation.getURL(), NAME_SPACE);
File f = new File(privateURL.getFile());
if(!f.exists())
f.mkdirs();
} catch (MalformedURLException e1) {
// ignore
}
}
configurationFactorySR = context.registerService(IPlatformConfigurationFactory.class.getName(), new PlatformConfigurationFactory(), null);
configuration = getPlatformConfiguration(installURL, configLocation);
if (configuration == null)
throw Utils.newCoreException("Cannot create configuration in " + configLocation.getURL(), null);
DataInputStream stream = null;
try {
stream = new DataInputStream(new URL(configLocation.getURL(),NAME_SPACE+'/'+LAST_CONFIG_STAMP).openStream());
lastTimeStamp = stream.readLong();
} catch (Exception e) {
lastTimeStamp = configuration.getChangeStamp() - 1;
} finally {
if (stream != null)
try {
stream.close();
} catch (IOException e1) {
Utils.log(e1.getLocalizedMessage());
}
}
}
public void stop(BundleContext ctx) throws Exception {
// quick fix (hack) for bug 47861
try {
PlatformConfiguration.shutdown();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
platform = null;
releasePlatform();
configurationFactorySR.unregister();
}
public boolean installBundles() {
Utils.debug("Installing bundles...");
ServiceReference reference = context.getServiceReference(StartLevel.class.getName());
StartLevel start = null;
if (reference != null)
start = (StartLevel) context.getService(reference);
try {
// Get the list of cached bundles and compare with the ones to be installed.
// Uninstall all the cached bundles that do not appear on the new list
Bundle[] cachedBundles = context.getBundles();
URL[] plugins = configuration.getPluginPath();
Bundle[] bundlesToUninstall = getBundlesToUninstall(cachedBundles, plugins);
for (int i=0; i<bundlesToUninstall.length; i++) {
try {
if (DEBUG)
Utils.debug("Uninstalling " + bundlesToUninstall[i].getLocation());
bundlesToUninstall[i].uninstall();
} catch (Exception e) {
Utils.log("Could not uninstall unused bundle " + bundlesToUninstall[i].getLocation());
}
}
// starts the list of bundles to refresh with all currently unresolved bundles (see bug 50680)
List toRefresh = getUnresolvedBundles();
for (int i = 0; i < plugins.length; i++) {
String location = plugins[i].toExternalForm();
try {
// TODO this is only because of PDE writing "plugin.xml" in platform.xml
if(location.endsWith(".xml"))
location = location.substring(0, location.lastIndexOf('/')+1);
//
location = "reference:" + location;
if (!isInstalled(location)) {
if (DEBUG)
Utils.debug("Installing " + location);
Bundle target = context.installBundle(location);
// any new bundle should be refreshed as well
toRefresh.add(target);
if (start != null)
start.setBundleStartLevel(target, 4);
}
} catch (Exception e) {
if ((location.indexOf("org.eclipse.core.boot") == -1) && (location.indexOf("org.eclipse.osgi") == -1)) {
Utils.log(Utils.newStatus("Ignoring bundle at: " + location, e));
}
}
}
context.ungetService(reference);
refreshPackages((Bundle[]) toRefresh.toArray(new Bundle[toRefresh.size()]));
if (System.getProperty(ECLIPSE_APPLICATION) == null && configuration.getApplicationIdentifier() != null)
System.setProperty(ECLIPSE_APPLICATION, configuration.getApplicationIdentifier());
if (System.getProperty(ECLIPSE_PRODUCT) == null && configuration.getPrimaryFeatureIdentifier() != null)
System.setProperty(ECLIPSE_PRODUCT, configuration.getPrimaryFeatureIdentifier());
// keep track of the last config successfully processed
writePlatformConfigurationTimeStamp();
return true;
} catch (Exception e) {
return false;
} finally {
releasePlatform();
}
}
private List getUnresolvedBundles() {
Bundle[] allBundles = context.getBundles();
List unresolved = new ArrayList();
for (int i = 0; i < allBundles.length; i++)
if (allBundles[i].getState() == Bundle.INSTALLED)
unresolved.add(allBundles[i]);
return unresolved;
}
private Bundle[] getBundlesToUninstall(Bundle[] cachedBundles, URL[] newPlugins) {
ArrayList bundlesToUninstall = new ArrayList();
for (int i=0; i<cachedBundles.length; i++) {
if (cachedBundles[i].getBundleId() == 0)
continue; // skip the system bundle
String cachedBundleLocation = cachedBundles[i].getLocation();
boolean found = false;
for (int j=0; !found && j<newPlugins.length; j++) {
String newPluginLocation = newPlugins[j].toExternalForm();
// TODO this is only because of PDE writing "plugin.xml" in platform.xml
if(newPluginLocation.endsWith(".xml"))
newPluginLocation = newPluginLocation.substring(0, newPluginLocation.lastIndexOf('/')+1);
//
newPluginLocation = "reference:" + newPluginLocation;
if (newPluginLocation.equals(cachedBundleLocation))
found = true;
else if (isWindows && newPluginLocation.equalsIgnoreCase(cachedBundleLocation))
found = true;
}
if (!found)
bundlesToUninstall.add(cachedBundles[i]);
}
return (Bundle[])bundlesToUninstall.toArray(new Bundle[bundlesToUninstall.size()]);
}
/**
* This is a major hack to try to get the reconciler application running. However we should find a way to not run it.
* @param args
* @param metaPath
* @return
*/
private PlatformConfiguration getPlatformConfiguration(URL installURL, Location configLocation) {
try {
PlatformConfiguration.startup(installURL, configLocation);
} catch (Exception e) {
if (platformTracker != null) {
String message = e.getMessage();
if (message == null)
message = "";
Utils.log(Utils.newStatus(message, e));
}
}
return PlatformConfiguration.getCurrent();
}
/**
* Do PackageAdmin.refreshPackages() in a synchronous way. After installing
* all the requested bundles we need to do a refresh and want to ensure that
* everything is done before returning.
* @param bundles
*/
private void refreshPackages(Bundle[] bundles) {
if (bundles.length == 0)
return;
ServiceReference packageAdminRef = context.getServiceReference(PackageAdmin.class.getName());
PackageAdmin packageAdmin = null;
if (packageAdminRef != null) {
packageAdmin = (PackageAdmin) context.getService(packageAdminRef);
if (packageAdmin == null)
return;
}
// TODO this is such a hack it is silly. There are still cases for race conditions etc
// but this should allow for some progress...
// (patch from John A.)
final boolean[] flag = new boolean[] {false};
FrameworkListener listener = new FrameworkListener() {
public void frameworkEvent(FrameworkEvent event) {
if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED)
synchronized (flag) {
flag[0] = true;
flag.notifyAll();
}
}
};
context.addFrameworkListener(listener);
packageAdmin.refreshPackages(bundles);
synchronized (flag) {
while (!flag[0]) {
try {
flag.wait();
} catch (InterruptedException e) {
}
}
}
context.removeFrameworkListener(listener);
context.ungetService(packageAdminRef);
}
/*
* location ends in / (later we will support jar as well)
*/
private boolean isInstalled(String location) {
Bundle[] installed = context.getBundles();
for (int i = 0; i < installed.length; i++) {
Bundle bundle = installed[i];
String bundleLocation = bundle.getLocation();
if (location.equals(bundleLocation))
return true;
else if (isWindows && location.equalsIgnoreCase(bundleLocation))
return true;
}
return false;
}
private void writePlatformConfigurationTimeStamp() {
DataOutputStream stream = null;
try {
if (configLocation.isReadOnly())
return;
String configArea = configLocation.getURL().getFile();
lastTimeStamp = configuration.getChangeStamp();
stream = new DataOutputStream(new FileOutputStream(configArea +File.separator+ NAME_SPACE+ File.separator+ LAST_CONFIG_STAMP));
stream.writeLong(lastTimeStamp);
} catch (Exception e) {
Utils.log(e.getLocalizedMessage());
} finally {
if (stream != null)
try {
stream.close();
} catch (IOException e1) {
Utils.log(e1.getLocalizedMessage());
}
}
}
private IPlatform acquirePlatform() {
if (platformTracker == null) {
platformTracker = new ServiceTracker(context, IPlatform.class.getName(), null);
platformTracker.open();
}
IPlatform result = (IPlatform) platformTracker.getService();
return result;
}
private void releasePlatform() {
if (platformTracker == null)
return;
platformTracker.close();
platformTracker = null;
}
private void loadOptions() {
// all this is only to get the application args
DebugOptions service = null;
ServiceReference reference = context.getServiceReference(DebugOptions.class.getName());
if (reference != null)
service = (DebugOptions) context.getService(reference);
if (service == null)
return;
try {
DEBUG = service.getBooleanOption(OPTION_DEBUG, false);
} finally {
// we have what we want - release the service
context.ungetService(reference);
}
}
private boolean canRunWithCachedData() {
return !"true".equals(System.getProperty("osgi.checkConfiguration")) &&
System.getProperties().get("osgi.dev") == null &&
lastTimeStamp==configuration.getChangeStamp();
}
public static BundleContext getBundleContext() {
return context;
}
public static URL getInstallURL() {
if (installURL == null)
try {
installURL = new URL((String) System.getProperty(INSTALL_LOCATION)); //$NON-NLS-1$
} catch (MalformedURLException e) {
//This can't fail because the location was set coming in
}
return installURL;
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IBundleGroupProvider#getName()
*/
public String getName() {
return Messages.getString("BundleGroupProvider");
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IBundleGroupProvider#getBundleGroups()
*/
public IBundleGroup[] getBundleGroups() {
if (configuration == null)
return new IBundleGroup[0];
else {
// TODO handle unmanaged plugins later
return (IBundleGroup[])configuration.getConfiguredFeatureEntries();
}
}
public static void setConfigurator(ConfigurationActivator configurator) {
ConfigurationActivator.configurator = configurator;
}
public static ConfigurationActivator getConfigurator() {
return configurator;
}
static Bundle getBundle() {
return ConfigurationActivator.getBundle();
}
}