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; } }