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