blob: 5bfcc0cf084d7727e7b2826feb1c6256f8837831 [file] [log] [blame]
package org.eclipse.update.configurator;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.osgi.service.environment.DebugOptions;
import org.eclipse.osgi.service.environment.EnvironmentInfo;
import org.osgi.framework.*;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.startlevel.StartLevel;
import org.osgi.util.tracker.ServiceTracker;
public class ConfigurationActivator implements BundleActivator {
private final static String DEFAULT_CONVERTER = "org.eclipse.update.configurator.migration.PluginConverter"; //$NON-NLS-1$
public static String PI_CONFIGURATOR = "org.eclipse.update.configurator";
// debug options
public static String OPTION_DEBUG = PI_CONFIGURATOR + "/debug";
public static String OPTION_DEBUG_CONVERTER = PI_CONFIGURATOR
+ "/converter/debug";
// debug values
public static boolean DEBUG = false;
public static boolean DEBUG_CONVERTER = false;
private static BundleContext context;
private ServiceTracker platformTracker;
private ServiceRegistration configurationFactorySR;
private String[] allArgs;
// location used to put the generated manfests
private String cacheLocation = (String) System.getProperties().get(
"osgi.manifest.cache"); //PASCAL Need to set this value somewhere (probably from boot)
private IPluginConverter converter;
private Set ignore;
private BundleListener reconcilerListener;
public void start(BundleContext ctx) throws Exception {
context = ctx;
loadOptions();
if (DEBUG)
System.out.println("Starting update configurator...");
computeIgnoredBundles();
loadConverter();
obtainArgs();
installBundles();
}
private void computeIgnoredBundles() {
String ignoreList = System
.getProperty("eclipse.ignore",
"org.eclipse.osgi,org.eclipse.core.boot,org.eclipse.core.runtime.adaptor");
ignore = new HashSet();
StringTokenizer tokenizer = new StringTokenizer(ignoreList, ",");
while (tokenizer.hasMoreTokens())
ignore.add(tokenizer.nextToken().trim());
}
private boolean shouldIgnore(String bundleName) {
if (ignore == null)
return false;
StringTokenizer tokenizer = new StringTokenizer(bundleName, "._");
String partialName = "";
while (tokenizer.hasMoreTokens()) {
partialName += tokenizer.nextToken();
if (ignore.contains(partialName))
return true;
partialName += ".";
}
return false;
}
private void loadConverter() {
// TODO look at making this an extension
String converterClassName = System.getProperty(
"eclipse.manifestconverter", DEFAULT_CONVERTER);
if (converterClassName == null)
return;
Class converterClass;
try {
converterClass = Class.forName(converterClassName);
} catch (ClassNotFoundException e) {
return;
}
try {
converter = (IPluginConverter) converterClass.newInstance();
} catch (InstantiationException e1) {
return;
} catch (IllegalAccessException e1) {
return;
} catch (ClassCastException cce) {
return;
}
}
private void obtainArgs() {
// all this is only to get the application args
EnvironmentInfo envInfo = null;
ServiceReference envInfoSR = context
.getServiceReference(EnvironmentInfo.class.getName());
if (envInfoSR != null)
envInfo = (EnvironmentInfo) context.getService(envInfoSR);
if (envInfo == null)
throw new IllegalStateException();
this.allArgs = envInfo.getAllArgs();
// we have what we want - release the service
context.ungetService(envInfoSR);
}
public void stop(BundleContext ctx) throws Exception {
releasePlatform();
configurationFactorySR.unregister();
}
private void releasePlatform() {
if (platformTracker == null)
return;
platformTracker.close();
platformTracker = null;
}
private IPlatform acquirePlatform() {
if (platformTracker == null) {
platformTracker = new ServiceTracker(context, IPlatform.class
.getName(), null);
platformTracker.open();
}
IPlatform result = (IPlatform) platformTracker.getService();
while (result == null) {
try {
platformTracker.waitForService(1000);
result = (IPlatform) platformTracker.getService();
} catch (InterruptedException ie) {
}
}
return result;
}
private void installBundles() {
IPlatform platform = acquirePlatform();
String metaPath = platform.getLocation().append(".metadata").toOSString();
URL installURL = platform.getInstallURL();
ServiceReference reference = context.getServiceReference(StartLevel.class.getName());
StartLevel start = null;
if (reference != null)
start = (StartLevel) context.getService(reference);
try {
configurationFactorySR = context.registerService(IPlatformConfigurationFactory.class.getName(), new PlatformConfigurationFactory(), null);
PlatformConfiguration config = getPlatformConfiguration(allArgs, metaPath, installURL);
URL[] plugins = config.getPluginPath();
ArrayList installed = new ArrayList(plugins.length);
for (int i = 0; i < plugins.length; i++) {
try {
String location = plugins[i].toExternalForm();
checkOrGenerateManifest(location);
location = "reference:" + location.substring(0, location.lastIndexOf('/'));
if (!isInstalled(location)) {
try {
Bundle target = context.installBundle(location);
installed.add(target);
if (start != null)
start.setBundleStartLevel(target, 4);
} catch (Exception e) {
System.err.println("Ignoring bundle at: " + location);
System.err.println(e.getMessage());
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
context.ungetService(reference);
refreshPackages((Bundle[]) installed.toArray(new Bundle[installed.size()]));
if (System.getProperty("eclipse.application") == null || System.getProperty("eclipse.application").equals(PlatformConfiguration.RECONCILER_APP))
System.setProperty("eclipse.application", config.getApplicationIdentifier());
// if (config.getApplicationIdentifier().equals(PlatformConfiguration.RECONCILER_APP) ) {
// reconcilerListener = reconcilerListener();
// context.addBundleListener(reconcilerListener);
// }
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
releasePlatform();
}
} private BundleListener reconcilerListener() {
return new BundleListener() {
public void bundleChanged(BundleEvent event) {
String buid = event.getBundle().getUniqueId();
if (event.getType() == BundleEvent.STOPPED && buid != null
&& buid.equals("org.eclipse.update.core"))
runPostReconciler();
}
};
}
private void runPostReconciler() {
Runnable postReconciler = new Runnable() {
public void run() {
try {
Bundle apprunner = context
.getBundle("org.eclipse.core.applicationrunner");
apprunner.stop();
context.removeBundleListener(reconcilerListener);
try {
PlatformConfiguration.shutdown();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
installBundles();
apprunner.start();
} catch (BundleException be) {
be.printStackTrace();
}
}
};
new Thread(postReconciler, "Post reconciler").start();
}
/**
* @param location
*/
private void checkOrGenerateManifest(String pluginManifestLocationURL) {
if (converter == null)
return;
String pluginManifestLocation = null;
try {
pluginManifestLocation = new URL(pluginManifestLocationURL)
.getPath();
} catch (MalformedURLException e) {
return;
}
File pluginDir = new File(pluginManifestLocation).getParentFile();
if (shouldIgnore(pluginDir.getName()))
return;
File manifest = new File(pluginDir, "META-INF/MANIFEST.MF");
if (manifest.exists())
return;
// bail if the install location is not writable and we don't know where else to write to
if (cacheLocation == null)
return;
File generationLocation = new File(cacheLocation,
computeFileName(pluginDir.getPath()) + ".MF");
if (generationLocation.exists())
return;
if (!converter.convertManifest(pluginDir, generationLocation))
System.out.println(pluginDir + " manifest generation failed");
}
/*
* Derives a file name corresponding to a path:
* c:\autoexec.bat -> c__autoexec.bat
*/
private String computeFileName(String filePath) {
StringBuffer newName = new StringBuffer(filePath);
for (int i = 0; i < filePath.length(); i++) {
char c = newName.charAt(i);
if (c == ':' || c == '/' || c == '\\')
newName.setCharAt(i, '_');
}
return newName.toString();
}
/**
* 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(String[] args,
String metaPath, URL installURL) {
try {
PlatformConfiguration.startup(args, null, null, metaPath,
installURL);
} catch (Exception e) {
if (platformTracker != null) {
String message = e.getMessage();
if (message == null)
message = "";
IStatus status = new Status(IStatus.ERROR,
IPlatform.PI_RUNTIME, IStatus.OK, message, e);
((IPlatform) platformTracker.getService()).getLog(
context.getBundle()).log(status);
}
}
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...
final Object semaphore = new Object();
FrameworkListener listener = new FrameworkListener() {
public void frameworkEvent(FrameworkEvent event) {
if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED)
synchronized (semaphore) {
semaphore.notifyAll();
}
}
};
context.addFrameworkListener(listener);
packageAdmin.refreshPackages(bundles);
synchronized (semaphore) {
try {
semaphore.wait();
} catch (InterruptedException e) {
}
}
context.removeFrameworkListener(listener);
context.ungetService(packageAdminRef);
}
private boolean isInstalled(String location) {
Bundle[] installed = context.getBundles();
for (int i = 0; i < installed.length; i++) {
Bundle bundle = installed[i];
if (location.equalsIgnoreCase(bundle.getLocation()))
return true;
}
return false;
}
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);
if (!DEBUG)
return;
DEBUG_CONVERTER = service.getBooleanOption(OPTION_DEBUG_CONVERTER,
false);
} finally {
// we have what we want - release the service
context.ungetService(reference);
}
}
public static BundleContext getBundleContext() {
return context;
}
}