| /******************************************************************************* |
| * 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.lang.reflect.Constructor; |
| import java.net.*; |
| import java.util.*; |
| import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor; |
| import org.eclipse.osgi.framework.internal.core.OSGi; |
| 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.service.datalocation.Location; |
| import org.eclipse.osgi.service.resolver.*; |
| import org.eclipse.osgi.service.runnable.ParameterizedRunnable; |
| import org.osgi.framework.*; |
| import org.osgi.service.packageadmin.PackageAdmin; |
| import org.osgi.service.startlevel.StartLevel; |
| import org.osgi.util.tracker.ServiceTracker; |
| |
| /** |
| * Special startup class for the Eclipse Platform. This class cannot be |
| * instantiated; all functionality is provided by static methods. |
| * <p> |
| * The Eclipse Platform makes heavy use of Java class loaders for loading |
| * plug-ins. Even the Eclispe Runtime itself and the OSGi framework need |
| * to be loaded by special class loaders. The upshot is that a |
| * client program (such as a Java main program, a servlet) cannot |
| * reference any part of Eclipse directly. Instead, a client must use this |
| * loader class to start the platform, invoking functionality defined |
| * in plug-ins, and shutting down the platform when done. |
| * </p> |
| * <p>Note that the fields on this class are not API. </p> |
| * @since 3.0 |
| */ |
| public class EclipseStarter { |
| private static FrameworkAdaptor adaptor; |
| private static BundleContext context; |
| private static ServiceTracker applicationTracker; |
| private static boolean initialize = false; |
| public static boolean debug = false; |
| private static boolean running = false; |
| |
| // command line arguments |
| private static final String CLEAN = "-clean"; //$NON-NLS-1$ |
| private static final String CONSOLE = "-console"; //$NON-NLS-1$ |
| private static final String CONSOLE_LOG = "-consoleLog"; //$NON-NLS-1$ |
| private static final String DEBUG = "-debug"; //$NON-NLS-1$ |
| private static final String INITIALIZE = "-initialize"; //$NON-NLS-1$ |
| private static final String DEV = "-dev"; //$NON-NLS-1$ |
| private static final String WS = "-ws"; //$NON-NLS-1$ |
| private static final String OS = "-os"; //$NON-NLS-1$ |
| private static final String ARCH = "-arch"; //$NON-NLS-1$ |
| private static final String NL = "-nl"; //$NON-NLS-1$ |
| private static final String CONFIGURATION = "-configuration"; //$NON-NLS-1$ |
| private static final String USER = "-user"; //$NON-NLS-1$ |
| // this is more of an Eclipse argument but this OSGi implementation stores its |
| // metadata alongside Eclipse's. |
| private static final String DATA = "-data"; //$NON-NLS-1$ |
| |
| // System properties |
| public static final String PROP_BUNDLES = "osgi.bundles"; //$NON-NLS-1$ |
| public static final String PROP_BUNDLES_STARTLEVEL = "osgi.bundles.defaultStartLevel"; //$NON-NLS-1$ |
| public static final String PROP_INITIAL_STARTLEVEL = "osgi.startLevel"; //$NON-NLS-1$ |
| public static final String PROP_DEBUG = "osgi.debug"; //$NON-NLS-1$ |
| public static final String PROP_DEV = "osgi.dev"; //$NON-NLS-1$ |
| public static final String PROP_CLEAN = "osgi.clean"; //$NON-NLS-1$ |
| public static final String PROP_CONSOLE = "osgi.console"; //$NON-NLS-1$ |
| public static final String PROP_CONSOLE_CLASS = "osgi.consoleClass"; //$NON-NLS-1$ |
| public static final String PROP_CHECK_CONFIG = "osgi.checkConfiguration"; //$NON-NLS-1$ |
| public static final String PROP_OS = "osgi.os"; //$NON-NLS-1$ |
| public static final String PROP_WS = "osgi.ws"; //$NON-NLS-1$ |
| public static final String PROP_NL = "osgi.nl"; //$NON-NLS-1$ |
| public static final String PROP_ARCH = "osgi.arch"; //$NON-NLS-1$ |
| public static final String PROP_ADAPTOR = "osgi.adaptor"; //$NON-NLS-1$ |
| public static final String PROP_SYSPATH = "osgi.syspath"; //$NON-NLS-1$ |
| public static final String PROP_LOGFILE = "osgi.logfile"; //$NON-NLS-1$ |
| |
| public static final String PROP_EXITCODE = "eclipse.exitcode"; //$NON-NLS-1$ |
| public static final String PROP_EXITDATA = "eclipse.exitdata"; //$NON-NLS-1$ |
| public static final String PROP_CONSOLE_LOG = "eclipse.consoleLog"; //$NON-NLS-1$ |
| private static final String PROP_VM = "eclipse.vm"; //$NON-NLS-1$ |
| private static final String PROP_VMARGS = "eclipse.vmargs"; //$NON-NLS-1$ |
| private static final String PROP_COMMANDS = "eclipse.commands"; //$NON-NLS-1$ |
| |
| private static final String FILE_SCHEME = "file:"; //$NON-NLS-1$ |
| private static final String FILE_PROTOCOL = "file"; //$NON-NLS-1$ |
| private static final String REFERENCE_SCHEME = "reference:"; //$NON-NLS-1$ |
| private static final String REFERENCE_PROTOCOL = "reference"; //$NON-NLS-1$ |
| private static final String INITIAL_LOCATION = "initial@"; //$NON-NLS-1$ |
| /** string containing the classname of the adaptor to be used in this framework instance */ |
| protected static final String DEFAULT_ADAPTOR_CLASS = "org.eclipse.core.runtime.adaptor.EclipseAdaptor"; //$NON-NLS-1$ |
| |
| // Console information |
| protected static final String DEFAULT_CONSOLE_CLASS = "org.eclipse.osgi.framework.internal.core.FrameworkConsole"; //$NON-NLS-1$ |
| private static final String CONSOLE_NAME = "OSGi Console"; //$NON-NLS-1$ |
| |
| private static FrameworkLog log; |
| |
| /** |
| * Launches the platform and runs a single application. The application is either identified |
| * in the given arguments (e.g., -application <app id>) or in the <code>eclipse.application</code> |
| * System property. This convenience method starts |
| * up the platform, runs the indicated application, and then shuts down the |
| * platform. The platform must not be running already. |
| * |
| * @param args the command line-style arguments used to configure the platform |
| * @param endSplashHandler the block of code to run to tear down the splash |
| * screen or <code>null</code> if no tear down is required |
| * @return the result of running the application |
| * @throws Exception if anything goes wrong |
| */ |
| public static Object run(String[] args, Runnable endSplashHandler) throws Exception { |
| if (running) |
| throw new IllegalStateException(EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_ALREADY_RUNNING")); //$NON-NLS-1$ |
| boolean startupFailed = true; |
| try { |
| startup(args, endSplashHandler); |
| startupFailed = false; |
| return run(null); |
| } catch (Throwable e) { |
| // ensure the splash screen is down |
| if (endSplashHandler != null) |
| endSplashHandler.run(); |
| // may use startupFailed to understand where the error happened |
| FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, startupFailed ? EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_STARTUP_ERROR") : EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_APP_ERROR"), 1, e, null); //$NON-NLS-1$//$NON-NLS-2$ |
| if (log != null) { |
| log.log(logEntry); |
| logUnresolvedBundles(context.getBundles()); |
| } else |
| // TODO desperate measure - ideally, we should write this to disk (a la Main.log) |
| e.printStackTrace(); |
| } finally { |
| try { |
| shutdown(); |
| } catch (Throwable e) { |
| FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_SHUTDOWN_ERROR"), 1, e, null); //$NON-NLS-1$ |
| if (log != null) |
| log.log(logEntry); |
| else |
| // TODO desperate measure - ideally, we should write this to disk (a la Main.log) |
| e.printStackTrace(); |
| } |
| } |
| // we only get here if an error happened |
| System.getProperties().put(PROP_EXITCODE, "13"); //$NON-NLS-1$ |
| System.getProperties().put(PROP_EXITDATA, EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_ERROR_CHECK_LOG", log.getFile().getPath())); //$NON-NLS-1$ |
| return null; |
| } |
| |
| /** |
| * Returns true if the platform is already running, false otherwise. |
| * @return whether or not the platform is already running |
| */ |
| public static boolean isRunning() { |
| return running; |
| } |
| |
| protected static FrameworkLog createFrameworkLog() { |
| FrameworkLog frameworkLog; |
| String logFileProp = System.getProperty(EclipseStarter.PROP_LOGFILE); |
| if (logFileProp != null) { |
| frameworkLog = new EclipseLog(new File(logFileProp)); |
| } else { |
| Location location = LocationManager.getConfigurationLocation(); |
| File configAreaDirectory = null; |
| if (location != null) |
| // TODO assumes the URL is a file: url |
| configAreaDirectory = new File(location.getURL().getFile()); |
| |
| if (configAreaDirectory != null) { |
| String logFileName = Long.toString(System.currentTimeMillis()) + EclipseAdaptor.F_LOG; |
| File logFile = new File(configAreaDirectory, logFileName); |
| System.getProperties().put(EclipseStarter.PROP_LOGFILE, logFile.getAbsolutePath()); |
| frameworkLog = new EclipseLog(logFile); |
| } else |
| frameworkLog = new EclipseLog(); |
| } |
| if ("true".equals(System.getProperty(EclipseStarter.PROP_CONSOLE_LOG))) //$NON-NLS-1$ |
| frameworkLog.setConsoleLog(true); |
| return frameworkLog; |
| } |
| |
| /** |
| * Starts the platform and sets it up to run a single application. The application is either identified |
| * in the given arguments (e.g., -application <app id>) or in the <code>eclipse.application</code> |
| * System property. The platform must not be running already. |
| * <p> |
| * The given runnable (if not <code>null</code>) is used to tear down the splash screen if required. |
| * </p> |
| * @param args the arguments passed to the application |
| * @throws Exception if anything goes wrong |
| */ |
| public static void startup(String[] args, Runnable endSplashHandler) throws Exception { |
| if (running) |
| throw new IllegalStateException(EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_ALREADY_RUNNING")); //$NON-NLS-1$ |
| long start = 0; |
| processCommandLine(args); |
| LocationManager.initializeLocations(); |
| log = createFrameworkLog(); |
| loadConfigurationInfo(); |
| loadDefaultProperties(); |
| finalizeProperties(); |
| adaptor = createAdaptor(); |
| ((EclipseAdaptor) adaptor).setLog(log); |
| OSGi osgi = new OSGi(adaptor); |
| osgi.launch(); |
| String console = System.getProperty(PROP_CONSOLE); |
| if (console != null) |
| startConsole(osgi, new String[0], console); |
| context = osgi.getBundleContext(); |
| publishSplashScreen(endSplashHandler); |
| Bundle[] startBundles = loadBasicBundles(); |
| // set the framework start level to the ultimate value. This will actually start things |
| // running if they are persistently active. |
| setStartLevel(getStartLevel()); |
| // they should all be active by this time |
| ensureBundlesActive(startBundles); |
| if (debug || System.getProperty(PROP_DEV) != null) |
| // only spend time showing unresolved bundles in dev/debug mode |
| logUnresolvedBundles(context.getBundles()); |
| running = true; |
| } |
| |
| private static int getStartLevel() { |
| String level = System.getProperty(PROP_INITIAL_STARTLEVEL); |
| if (level != null) |
| try { |
| return Integer.parseInt(level); |
| } catch (NumberFormatException e) { |
| if (debug) |
| System.out.println("Start level = " + level + " parsed. Using hardcoded default: 6"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| return 6; // hard coded default value for legacy purposes |
| } |
| |
| /** |
| * Runs the applicaiton for which the platform was started. The platform |
| * must be running. |
| * <p> |
| * The given argument is passed to the application being run. If it is <code>null</code> |
| * then the command line arguments used in starting the platform, and not consumed |
| * by the platform code, are passed to the application as a <code>String[]</code>. |
| * </p> |
| * @param argument the argument passed to the application. May be <code>null</code> |
| * @return the result of running the application |
| * @throws Exception if anything goes wrong |
| */ |
| public static Object run(Object argument) throws Exception { |
| if (!running) |
| throw new IllegalStateException(EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_NOT_RUNNING")); //$NON-NLS-1$ |
| // if we are just initializing, do not run the application just return. |
| if (initialize) |
| return new Integer(0); |
| initializeApplicationTracker(); |
| ParameterizedRunnable application = (ParameterizedRunnable) applicationTracker.getService(); |
| applicationTracker.close(); |
| if (application == null) |
| throw new IllegalStateException(EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_ERROR_NO_APPLICATION")); //$NON-NLS-1$ |
| if (debug) { |
| String timeString = System.getProperty("eclipse.startTime"); //$NON-NLS-1$ |
| long time = timeString == null ? 0L : Long.parseLong(timeString); |
| System.out.println("Starting application: " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ |
| } |
| return application.run(argument); |
| } |
| |
| /** |
| * Shuts down the Platform. The state of the Platform is not automatically |
| * saved before shutting down. |
| * <p> |
| * On return, the Platform will no longer be running (but could be re-launched |
| * with another call to startup). If relaunching, care must be taken to reinitialize |
| * any System properties which the platform uses (e.g., osgi.instance.area) as |
| * some policies in the platform do not allow resetting of such properties on |
| * subsequent runs. |
| * </p><p> |
| * Any objects handed out by running Platform, |
| * including Platform runnables obtained via getRunnable, will be |
| * permanently invalid. The effects of attempting to invoke methods |
| * on invalid objects is undefined. |
| * </p> |
| * @throws Exception if anything goes wrong |
| */ |
| public static void shutdown() throws Exception { |
| if (!running) |
| return; |
| stopSystemBundle(); |
| } |
| |
| private static void ensureBundlesActive(Bundle[] bundles) { |
| for (int i = 0; i < bundles.length; i++) { |
| if (bundles[i].getState() != Bundle.ACTIVE) { |
| String message = EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_ACTIVE", bundles[i]); //$NON-NLS-1$ |
| throw new IllegalStateException(message); |
| } |
| } |
| } |
| |
| private static void logUnresolvedBundles(Bundle[] bundles) { |
| State state = adaptor.getState(); |
| FrameworkLog logService = adaptor.getFrameworkLog(); |
| StateHelper stateHelper = adaptor.getPlatformAdmin().getStateHelper(); |
| for (int i = 0; i < bundles.length; i++) |
| if (bundles[i].getState() == Bundle.INSTALLED) { |
| String generalMessage = EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_RESOLVED", bundles[i]); //$NON-NLS-1$ |
| BundleDescription description = state.getBundle(bundles[i].getBundleId()); |
| // for some reason, the state does not know about that bundle |
| if (description == null) |
| continue; |
| FrameworkLogEntry[] logChildren = null; |
| VersionConstraint[] unsatisfied = stateHelper.getUnsatisfiedConstraints(description); |
| if (unsatisfied.length > 0) { |
| // the bundle wasn't resolved due to some of its constraints were unsatisfiable |
| logChildren = new FrameworkLogEntry[unsatisfied.length]; |
| for (int j = 0; j < unsatisfied.length; j++) |
| logChildren[j] = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, EclipseAdaptorMsg.getResolutionFailureMessage(unsatisfied[j]), 0, null, null); |
| } else if (description.getSymbolicName() != null) { |
| BundleDescription[] homonyms = state.getBundles(description.getSymbolicName()); |
| for (int j = 0; j < homonyms.length; j++) |
| if (homonyms[j].isResolved()) { |
| logChildren = new FrameworkLogEntry[1]; |
| logChildren[0] = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, EclipseAdaptorMsg.formatter.getString("ECLIPSE_CONSOLE_OTHER_VERSION", homonyms[j].getLocation()), 0, null, null); //$NON-NLS-1$ |
| } |
| } |
| |
| logService.log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, generalMessage, 0, null, logChildren)); |
| } |
| } |
| |
| private static void publishSplashScreen(final Runnable endSplashHandler) { |
| // InternalPlatform now how to retrieve this later |
| Dictionary properties = new Hashtable(); |
| properties.put("name", "splashscreen"); //$NON-NLS-1$ //$NON-NLS-2$ |
| Runnable handler = new Runnable() { |
| public void run() { |
| StatsManager.doneBooting(); |
| endSplashHandler.run(); |
| } |
| }; |
| context.registerService(Runnable.class.getName(), handler, properties); |
| } |
| |
| private static URL searchForBundle(String name, String parent) throws MalformedURLException { |
| URL url = null; |
| File fileLocation = null; |
| boolean reference = false; |
| try { |
| URL child = new URL(name); |
| url = new URL(new File(parent).toURL(), name); |
| } catch (MalformedURLException e) { |
| // TODO this is legacy support for non-URL names. It should be removed eventually. |
| // if name was not a URL then construct one. |
| // Assume it should be a reference and htat it is relative. This support need not |
| // be robust as it is temporary.. |
| File child = new File(name); |
| fileLocation = child.isAbsolute() ? child : new File(parent, name); |
| url = new URL(REFERENCE_PROTOCOL, null, fileLocation.toURL().toExternalForm()); |
| reference = true; |
| } |
| // if the name was a URL then see if it is relative. If so, insert syspath. |
| if (!reference) { |
| URL baseURL = url; |
| // if it is a reference URL then strip off the reference: and set base to the file:... |
| if (url.getProtocol().equals(REFERENCE_PROTOCOL)) { |
| reference = true; |
| String baseSpec = url.getFile(); |
| if (baseSpec.startsWith(FILE_SCHEME)) { |
| File child = new File(baseSpec.substring(5)); |
| baseURL = child.isAbsolute() ? child.toURL() : new File(parent, child.getPath()).toURL(); |
| } else |
| baseURL = new URL(baseSpec); |
| } |
| |
| fileLocation = new File(baseURL.getFile()); |
| // if the location is relative, prefix it with the parent |
| if (!fileLocation.isAbsolute()) |
| fileLocation = new File(parent, fileLocation.toString()); |
| } |
| // If the result is a reference then search for the real result and |
| // reconstruct the answer. |
| if (reference) { |
| String result = searchFor(fileLocation.getName(), new File(fileLocation.getParent()).getAbsolutePath()); |
| if (result != null) |
| url = new URL(REFERENCE_PROTOCOL, null, FILE_SCHEME + result); |
| else |
| return null; |
| } |
| |
| // finally we have something worth trying |
| try { |
| URLConnection result = url.openConnection(); |
| result.connect(); |
| return url; |
| } catch (IOException e) { |
| // int i = location.lastIndexOf('_'); |
| // return i == -1? location : location.substring(0, i); |
| return null; |
| } |
| } |
| |
| /* |
| * Ensure all basic bundles are installed, resolved and scheduled to start. Returns an array containing |
| * all basic bundles that are marked to start. |
| */ |
| private static Bundle[] loadBasicBundles() throws IOException { |
| long startTime = System.currentTimeMillis(); |
| String[] installEntries = getArrayFromList(System.getProperty(PROP_BUNDLES), ","); //$NON-NLS-1$ |
| // get the initial bundle list from the installEntries |
| InitialBundle[] initialBundles = getInitialBundles(installEntries); |
| // get the list of currently installed initial bundles from the framework |
| Bundle[] curInitBundles = getCurrentInitialBundles(); |
| |
| // list of bundles to be refreshed |
| List toRefresh = new ArrayList(curInitBundles.length); |
| // uninstall any of the currently installed bundles that do not exist in the |
| // initial bundle list from installEntries. |
| uninstallBundles(curInitBundles, initialBundles, toRefresh); |
| |
| // install the initialBundles that are not already installed. |
| ArrayList startBundles = new ArrayList(installEntries.length); |
| installBundles(initialBundles, curInitBundles, startBundles, toRefresh); |
| |
| // If we installed/uninstalled something, force a refresh of all installed/uninstalled bundles |
| if (!toRefresh.isEmpty()) |
| refreshPackages((Bundle[]) toRefresh.toArray(new Bundle[toRefresh.size()])); |
| |
| // schedule all basic bundles to be started |
| Bundle[] startInitBundles = (Bundle[]) startBundles.toArray(new Bundle[startBundles.size()]); |
| startBundles(startInitBundles); |
| |
| if (debug) |
| System.out.println("Time to load bundles: " + (System.currentTimeMillis() - startTime)); //$NON-NLS-1$ |
| return startInitBundles; |
| } |
| |
| private static InitialBundle[] getInitialBundles(String[] installEntries) throws MalformedURLException { |
| ArrayList result = new ArrayList(installEntries.length); |
| int defaultStartLevel = Integer.parseInt(System.getProperty(PROP_BUNDLES_STARTLEVEL)); |
| String syspath = getSysPath(); |
| for (int i = 0; i < installEntries.length; i++) { |
| String name = installEntries[i]; |
| int level = defaultStartLevel; |
| boolean start = false; |
| int index = name.indexOf('@'); |
| if (index >= 0) { |
| String[] attributes = getArrayFromList(name.substring(index + 1, name.length()), ":"); //$NON-NLS-1$ |
| name = name.substring(0, index); |
| for (int j = 0; j < attributes.length; j++) { |
| String attribute = attributes[j]; |
| if (attribute.equals("start")) //$NON-NLS-1$ |
| start = true; |
| else |
| level = Integer.parseInt(attribute); |
| } |
| } |
| URL location = searchForBundle(name, syspath); |
| if (location == null) { |
| FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_BUNDLE_NOT_FOUND", installEntries[i]), 0, null, null); //$NON-NLS-1$ |
| log.log(entry); |
| // skip this entry |
| continue; |
| } |
| String locationString = INITIAL_LOCATION + location.toExternalForm(); |
| result.add(new InitialBundle(locationString, location, level, start)); |
| } |
| return (InitialBundle[]) result.toArray(new InitialBundle[result.size()]); |
| } |
| |
| private static void refreshPackages(Bundle[] bundles) { |
| 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 Semaphore semaphore = new Semaphore(0); |
| FrameworkListener listener = new FrameworkListener() { |
| public void frameworkEvent(FrameworkEvent event) { |
| if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) |
| semaphore.release(); |
| } |
| }; |
| context.addFrameworkListener(listener); |
| packageAdmin.refreshPackages(bundles); |
| semaphore.acquire(); |
| context.removeFrameworkListener(listener); |
| context.ungetService(packageAdminRef); |
| } |
| |
| /** |
| * Invokes the OSGi Console on another thread |
| * |
| * @param osgi The current OSGi instance for the console to attach to |
| * @param consoleArgs An String array containing commands from the command line |
| * for the console to execute |
| * @param consolePort the port on which to run the console. Empty string implies the default port. |
| */ |
| private static void startConsole(OSGi osgi, String[] consoleArgs, String consolePort) { |
| try { |
| String consoleClassName = System.getProperty(PROP_CONSOLE_CLASS, DEFAULT_CONSOLE_CLASS); |
| Class consoleClass = Class.forName(consoleClassName); |
| Class[] parameterTypes; |
| Object[] parameters; |
| if (consolePort.length() == 0) { |
| parameterTypes = new Class[] {OSGi.class, String[].class}; |
| parameters = new Object[] {osgi, consoleArgs}; |
| } else { |
| parameterTypes = new Class[] {OSGi.class, int.class, String[].class}; |
| parameters = new Object[] {osgi, new Integer(consolePort), consoleArgs}; |
| } |
| Constructor constructor = consoleClass.getConstructor(parameterTypes); |
| Object console = constructor.newInstance(parameters); |
| Thread t = new Thread(((Runnable) console), CONSOLE_NAME); |
| t.start(); |
| } catch (NumberFormatException nfe) { |
| // TODO log or something other than write on System.err |
| System.err.println(EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_INVALID_PORT", consolePort)); //$NON-NLS-1$ |
| } catch (Exception ex) { |
| System.out.println(EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_FAILED_FIND", CONSOLE_NAME)); //$NON-NLS-1$ |
| } |
| |
| } |
| |
| /** |
| * Creates and returns the adaptor |
| * |
| * @return a FrameworkAdaptor object |
| */ |
| private static FrameworkAdaptor createAdaptor() throws Exception { |
| String adaptorClassName = System.getProperty(PROP_ADAPTOR, DEFAULT_ADAPTOR_CLASS); |
| Class adaptorClass = Class.forName(adaptorClassName); |
| Class[] constructorArgs = new Class[] {String[].class}; |
| Constructor constructor = adaptorClass.getConstructor(constructorArgs); |
| return (FrameworkAdaptor) constructor.newInstance(new Object[] {new String[0]}); |
| } |
| |
| private static String[] processCommandLine(String[] args) throws Exception { |
| EnvironmentInfo.allArgs = args; |
| if (args.length == 0) { |
| EnvironmentInfo.frameworkArgs = args; |
| EnvironmentInfo.appArgs = args; |
| return args; |
| } |
| int[] configArgs = new int[args.length]; |
| configArgs[0] = -1; // need to initialize the first element to something that could not be an index. |
| int configArgIndex = 0; |
| for (int i = 0; i < args.length; i++) { |
| boolean found = false; |
| // check for args without parameters (i.e., a flag arg) |
| |
| // check if debug should be enabled for the entire platform |
| // If this is the last arg or there is a following arg (i.e., arg+1 has a leading -), |
| // simply enable debug. Otherwise, assume that that the following arg is |
| // actually the filename of an options file. This will be processed below. |
| if (args[i].equalsIgnoreCase(DEBUG) && ((i + 1 == args.length) || ((i + 1 < args.length) && (args[i + 1].startsWith("-"))))) { //$NON-NLS-1$ |
| System.getProperties().put(PROP_DEBUG, ""); //$NON-NLS-1$ |
| debug = true; |
| found = true; |
| } |
| |
| // check if development mode should be enabled for the entire platform |
| // If this is the last arg or there is a following arg (i.e., arg+1 has a leading -), |
| // simply enable development mode. Otherwise, assume that that the following arg is |
| // actually some additional development time class path entries. This will be processed below. |
| if (args[i].equalsIgnoreCase(DEV) && ((i + 1 == args.length) || ((i + 1 < args.length) && (args[i + 1].startsWith("-"))))) { //$NON-NLS-1$ |
| System.getProperties().put(PROP_DEV, ""); //$NON-NLS-1$ |
| found = true; |
| } |
| |
| // look for the initialization arg |
| if (args[i].equalsIgnoreCase(INITIALIZE)) { |
| initialize = true; |
| found = true; |
| } |
| |
| // look for the clean flag. |
| if (args[i].equalsIgnoreCase(CLEAN)) { |
| System.getProperties().put(PROP_CLEAN, "true"); //$NON-NLS-1$ |
| found = true; |
| } |
| |
| // look for the consoleLog flag |
| if (args[i].equalsIgnoreCase(CONSOLE_LOG)) { |
| System.getProperties().put(PROP_CONSOLE_LOG, "true"); //$NON-NLS-1$ |
| found = true; |
| } |
| |
| // look for the console with no port. |
| if (args[i].equalsIgnoreCase(CONSOLE) && ((i + 1 == args.length) || ((i + 1 < args.length) && (args[i + 1].startsWith("-"))))) { //$NON-NLS-1$ |
| System.getProperties().put(PROP_CONSOLE, ""); //$NON-NLS-1$ |
| found = true; |
| } |
| |
| if (found) { |
| configArgs[configArgIndex++] = i; |
| continue; |
| } |
| // check for args with parameters. If we are at the last argument or if the next one |
| // has a '-' as the first character, then we can't have an arg with a parm so continue. |
| if (i == args.length - 1 || args[i + 1].startsWith("-")) { //$NON-NLS-1$ |
| continue; |
| } |
| String arg = args[++i]; |
| |
| // look for the console and port. |
| if (args[i - 1].equalsIgnoreCase(CONSOLE)) { |
| System.getProperties().put(PROP_CONSOLE, arg); |
| found = true; |
| } |
| |
| // look for the configuration location . |
| if (args[i - 1].equalsIgnoreCase(CONFIGURATION)) { |
| System.getProperties().put(LocationManager.PROP_CONFIG_AREA, arg); |
| found = true; |
| } |
| |
| // look for the data location for this instance. |
| if (args[i - 1].equalsIgnoreCase(DATA)) { |
| System.getProperties().put(LocationManager.PROP_INSTANCE_AREA, arg); |
| found = true; |
| } |
| |
| // look for the user location for this instance. |
| if (args[i - 1].equalsIgnoreCase(USER)) { |
| System.getProperties().put(LocationManager.PROP_USER_AREA, arg); |
| found = true; |
| } |
| |
| // look for the development mode and class path entries. |
| if (args[i - 1].equalsIgnoreCase(DEV)) { |
| System.getProperties().put(PROP_DEV, arg); |
| found = true; |
| } |
| |
| // look for the debug mode and option file location. |
| if (args[i - 1].equalsIgnoreCase(DEBUG)) { |
| System.getProperties().put(PROP_DEBUG, arg); |
| debug = true; |
| found = true; |
| } |
| |
| // look for the window system. |
| if (args[i - 1].equalsIgnoreCase(WS)) { |
| System.getProperties().put(PROP_WS, arg); |
| found = true; |
| } |
| |
| // look for the operating system |
| if (args[i - 1].equalsIgnoreCase(OS)) { |
| System.getProperties().put(PROP_OS, arg); |
| found = true; |
| } |
| |
| // look for the system architecture |
| if (args[i - 1].equalsIgnoreCase(ARCH)) { |
| System.getProperties().put(PROP_ARCH, arg); |
| found = true; |
| } |
| |
| // look for the nationality/language |
| if (args[i - 1].equalsIgnoreCase(NL)) { |
| System.getProperties().put(PROP_NL, arg); |
| found = true; |
| } |
| // done checking for args. Remember where an arg was found |
| if (found) { |
| configArgs[configArgIndex++] = i - 1; |
| configArgs[configArgIndex++] = i; |
| } |
| } |
| |
| // remove all the arguments consumed by this argument parsing |
| if (configArgIndex == 0) { |
| EnvironmentInfo.frameworkArgs = new String[0]; |
| EnvironmentInfo.appArgs = args; |
| return args; |
| } |
| EnvironmentInfo.appArgs = new String[args.length - configArgIndex]; |
| EnvironmentInfo.frameworkArgs = new String[configArgIndex]; |
| configArgIndex = 0; |
| int j = 0; |
| int k = 0; |
| for (int i = 0; i < args.length; i++) { |
| if (i == configArgs[configArgIndex]) { |
| EnvironmentInfo.frameworkArgs[k++] = args[i]; |
| configArgIndex++; |
| } else |
| EnvironmentInfo.appArgs[j++] = args[i]; |
| } |
| return EnvironmentInfo.appArgs; |
| } |
| |
| /** |
| * Returns the result of converting a list of comma-separated tokens into an array |
| * |
| * @return the array of string tokens |
| * @param prop the initial comma-separated string |
| */ |
| private static String[] getArrayFromList(String prop, String separator) { |
| if (prop == null || prop.trim().equals("")) //$NON-NLS-1$ |
| return new String[0]; |
| Vector list = new Vector(); |
| StringTokenizer tokens = new StringTokenizer(prop, separator); //$NON-NLS-1$ |
| while (tokens.hasMoreTokens()) { |
| String token = tokens.nextToken().trim(); |
| if (!token.equals("")) //$NON-NLS-1$ |
| list.addElement(token); |
| } |
| return list.isEmpty() ? new String[0] : (String[]) list.toArray(new String[list.size()]); |
| } |
| |
| protected static String getSysPath() { |
| String result = System.getProperty(PROP_SYSPATH); |
| if (result != null) |
| return result; |
| |
| URL url = EclipseStarter.class.getProtectionDomain().getCodeSource().getLocation(); |
| result = url.getFile(); |
| if (result.endsWith("/")) //$NON-NLS-1$ |
| result = result.substring(0, result.length() - 1); |
| result = result.substring(0, result.lastIndexOf('/')); |
| result = result.substring(0, result.lastIndexOf('/')); |
| if (Character.isUpperCase(result.charAt(0))) { |
| char[] chars = result.toCharArray(); |
| chars[0] = Character.toLowerCase(chars[0]); |
| result = new String(chars); |
| } |
| return result; |
| } |
| |
| private static Bundle[] getCurrentInitialBundles() { |
| Bundle[] installed = context.getBundles(); |
| ArrayList initial = new ArrayList(); |
| for (int i = 0; i < installed.length; i++) { |
| Bundle bundle = installed[i]; |
| if (bundle.getLocation().startsWith(INITIAL_LOCATION)) |
| initial.add(bundle); |
| } |
| return (Bundle[]) initial.toArray(new Bundle[initial.size()]); |
| } |
| |
| private static Bundle getBundleByLocation(String location, Bundle[] bundles) { |
| for (int i = 0; i < bundles.length; i++) { |
| Bundle bundle = bundles[i]; |
| if (location.equalsIgnoreCase(bundle.getLocation())) |
| return bundle; |
| } |
| return null; |
| } |
| |
| private static void uninstallBundles(Bundle[] curInitBundles, InitialBundle[] newInitBundles, List toRefresh) { |
| for (int i = 0; i < curInitBundles.length; i++) { |
| boolean found = false; |
| for (int j = 0; j < newInitBundles.length; j++) { |
| if (curInitBundles[i].getLocation().equalsIgnoreCase(newInitBundles[j].locationString)) { |
| found = true; |
| break; |
| } |
| } |
| if (!found) |
| try { |
| curInitBundles[i].uninstall(); |
| toRefresh.add(curInitBundles[i]); |
| } catch (BundleException e) { |
| FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_FAILED_UNINSTALL", curInitBundles[i].getLocation()), 0, e, null); //$NON-NLS-1$ |
| log.log(entry); |
| } |
| } |
| } |
| |
| private static void installBundles(InitialBundle[] initialBundles, Bundle[] curInitBundles, ArrayList startBundles, List toRefresh) { |
| ServiceReference reference = context.getServiceReference(StartLevel.class.getName()); |
| StartLevel startService = null; |
| if (reference != null) |
| startService = (StartLevel) context.getService(reference); |
| for (int i = 0; i < initialBundles.length; i++) { |
| Bundle osgiBundle = getBundleByLocation(initialBundles[i].locationString, curInitBundles); |
| try { |
| // don't need to install if it is already installed |
| if (osgiBundle == null) { |
| InputStream in = initialBundles[i].location.openStream(); |
| osgiBundle = context.installBundle(initialBundles[i].locationString, in); |
| if (initialBundles[i].level >= 0 && startService != null) |
| startService.setBundleStartLevel(osgiBundle, initialBundles[i].level); |
| } |
| if (initialBundles[i].start) |
| startBundles.add(osgiBundle); |
| // include basic bundles in case they were not resolved before |
| if ((osgiBundle.getState() & Bundle.INSTALLED) != 0) |
| toRefresh.add(osgiBundle); |
| } catch (BundleException e) { |
| FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_FAILED_INSTALL", initialBundles[i].location), 0, e, null); //$NON-NLS-1$ |
| log.log(entry); |
| } catch (IOException e) { |
| FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_FAILED_INSTALL", initialBundles[i].location), 0, e, null); //$NON-NLS-1$ |
| log.log(entry); |
| } |
| } |
| context.ungetService(reference); |
| } |
| |
| private static void startBundles(Bundle[] bundles) { |
| for (int i = 0; i < bundles.length; i++) { |
| Bundle bundle = bundles[i]; |
| if (bundle.getState() == Bundle.INSTALLED) |
| throw new IllegalStateException(EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_RESOLVED", bundle.getLocation())); //$NON-NLS-1$ |
| try { |
| bundle.start(); |
| } catch (BundleException e) { |
| FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, EclipseAdaptorMsg.formatter.getString("ECLIPSE_STARTUP_FAILED_START", bundle.getLocation()), 0, e, null); //$NON-NLS-1$ |
| log.log(entry); |
| } |
| } |
| } |
| |
| private static void initializeApplicationTracker() { |
| Filter filter = null; |
| try { |
| String appClass = ParameterizedRunnable.class.getName(); |
| filter = context.createFilter("(&(objectClass=" + appClass + ")(eclipse.application=*))"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } catch (InvalidSyntaxException e) { |
| // ignore this. It should never happen as we have tested the above format. |
| } |
| applicationTracker = new ServiceTracker(context, filter, null); |
| applicationTracker.open(); |
| } |
| |
| private static void loadConfigurationInfo() { |
| Location configArea = LocationManager.getConfigurationLocation(); |
| if (configArea == null) |
| return; |
| |
| URL location = null; |
| try { |
| location = new URL(configArea.getURL().toExternalForm() + LocationManager.CONFIG_FILE); |
| } catch (MalformedURLException e) { |
| // its ok. Thie should never happen |
| } |
| mergeProperties(System.getProperties(), loadProperties(location)); |
| } |
| |
| private static void loadDefaultProperties() { |
| URL codeLocation = EclipseStarter.class.getProtectionDomain().getCodeSource().getLocation(); |
| if (codeLocation == null) |
| return; |
| String location = codeLocation.getFile(); |
| if (location.endsWith("/")) //$NON-NLS-1$ |
| location = location.substring(0, location.length() - 1); |
| int i = location.lastIndexOf('/'); |
| location = location.substring(0, i + 1) + LocationManager.ECLIPSE_PROPERTIES; |
| URL result = null; |
| try { |
| result = new File(location).toURL(); |
| } catch (MalformedURLException e) { |
| // its ok. Thie should never happen |
| } |
| mergeProperties(System.getProperties(), loadProperties(result)); |
| } |
| |
| private static Properties loadProperties(URL location) { |
| Properties result = new Properties(); |
| if (location == null) |
| return result; |
| try { |
| InputStream in = location.openStream(); |
| try { |
| result.load(in); |
| } finally { |
| in.close(); |
| } |
| } catch (IOException e) { |
| // its ok if there is no file. We'll just use the defaults for everything |
| // TODO but it might be nice to log something with gentle wording (i.e., it is not an error) |
| } |
| return result; |
| } |
| |
| private static void mergeProperties(Properties destination, Properties source) { |
| for (Enumeration e = source.keys(); e.hasMoreElements();) { |
| String key = (String) e.nextElement(); |
| String value = source.getProperty(key); |
| if (destination.getProperty(key) == null) |
| destination.put(key, value); |
| } |
| } |
| |
| private static void stopSystemBundle() throws BundleException { |
| if (context == null || !running) |
| return; |
| Bundle systemBundle = context.getBundle(0); |
| if (systemBundle.getState() == Bundle.ACTIVE) { |
| final Semaphore semaphore = new Semaphore(0); |
| FrameworkListener listener = new FrameworkListener() { |
| public void frameworkEvent(FrameworkEvent event) { |
| if (event.getType() == FrameworkEvent.STARTLEVEL_CHANGED) |
| semaphore.release(); |
| } |
| |
| }; |
| context.addFrameworkListener(listener); |
| systemBundle.stop(); |
| semaphore.acquire(); |
| context.removeFrameworkListener(listener); |
| } |
| context = null; |
| applicationTracker = null; |
| running = false; |
| } |
| |
| private static void setStartLevel(final int value) { |
| ServiceTracker tracker = new ServiceTracker(context, StartLevel.class.getName(), null); |
| tracker.open(); |
| final StartLevel startLevel = (StartLevel) tracker.getService(); |
| final Semaphore semaphore = new Semaphore(0); |
| FrameworkListener listener = new FrameworkListener() { |
| public void frameworkEvent(FrameworkEvent event) { |
| if (event.getType() == FrameworkEvent.STARTLEVEL_CHANGED && startLevel.getStartLevel() == value) |
| semaphore.release(); |
| } |
| }; |
| context.addFrameworkListener(listener); |
| startLevel.setStartLevel(value); |
| semaphore.acquire(); |
| context.removeFrameworkListener(listener); |
| tracker.close(); |
| } |
| |
| /** |
| * Searches for the given target directory starting in the "plugins" subdirectory |
| * of the given location. If one is found then this location is returned; |
| * otherwise an exception is thrown. |
| * |
| * @return the location where target directory was found |
| * @param start the location to begin searching |
| */ |
| private static String searchFor(final String target, String start) { |
| String[] candidates = new File(start).list(); |
| if (candidates == null) |
| return null; |
| String result = null; |
| Object maxVersion = null; |
| for (int i = 0; i < candidates.length; i++) { |
| File candidate = new File(start, candidates[i]); |
| if (!candidate.getName().equals(target) && !candidate.getName().startsWith(target + "_")) //$NON-NLS-1$ |
| continue; |
| String name = candidate.getName(); |
| String version = ""; //$NON-NLS-1$ // Note: directory with version suffix is always > than directory without version suffix |
| int index = name.indexOf('_'); |
| if (index != -1) |
| version = name.substring(index + 1); |
| Object currentVersion = getVersionElements(version); |
| if (maxVersion == null) { |
| result = candidate.getAbsolutePath(); |
| maxVersion = currentVersion; |
| } else { |
| if (compareVersion((Object[]) maxVersion, (Object[]) currentVersion) < 0) { |
| result = candidate.getAbsolutePath(); |
| maxVersion = currentVersion; |
| } |
| } |
| } |
| if (result == null) |
| return null; |
| return result.replace(File.separatorChar, '/') + "/"; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Do a quick parse of version identifier so its elements can be correctly compared. |
| * If we are unable to parse the full version, remaining elements are initialized |
| * with suitable defaults. |
| * @return an array of size 4; first three elements are of type Integer (representing |
| * major, minor and service) and the fourth element is of type String (representing |
| * qualifier). Note, that returning anything else will cause exceptions in the caller. |
| */ |
| private static Object[] getVersionElements(String version) { |
| Object[] result = {new Integer(0), new Integer(0), new Integer(0), ""}; //$NON-NLS-1$ |
| StringTokenizer t = new StringTokenizer(version, "."); //$NON-NLS-1$ |
| String token; |
| int i = 0; |
| while (t.hasMoreTokens() && i < 4) { |
| token = t.nextToken(); |
| if (i < 3) { |
| // major, minor or service ... numeric values |
| try { |
| result[i++] = new Integer(token); |
| } catch (Exception e) { |
| // invalid number format - use default numbers (0) for the rest |
| break; |
| } |
| } else { |
| // qualifier ... string value |
| result[i++] = token; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Compares version strings. |
| * @return result of comparison, as integer; |
| * <code><0</code> if left < right; |
| * <code>0</code> if left == right; |
| * <code>>0</code> if left > right; |
| */ |
| private static int compareVersion(Object[] left, Object[] right) { |
| int result = ((Integer) left[0]).compareTo((Integer) right[0]); // compare major |
| if (result != 0) |
| return result; |
| |
| result = ((Integer) left[1]).compareTo((Integer) right[1]); // compare minor |
| if (result != 0) |
| return result; |
| |
| result = ((Integer) left[2]).compareTo((Integer) right[2]); // compare service |
| if (result != 0) |
| return result; |
| |
| return ((String) left[3]).compareTo((String) right[3]); // compare qualifier |
| } |
| |
| private static String buildCommandLine(String arg, String value) { |
| StringBuffer result = new StringBuffer(300); |
| String entry = System.getProperty(PROP_VM); |
| if (entry == null) |
| return null; |
| result.append(entry); |
| result.append('\n'); |
| // append the vmargs and commands. Assume that these already end in \n |
| entry = System.getProperty(PROP_VMARGS); |
| if (entry != null) |
| result.append(entry); |
| entry = System.getProperty(PROP_COMMANDS); |
| if (entry != null) |
| result.append(entry); |
| String commandLine = result.toString(); |
| int i = commandLine.indexOf(arg + "\n"); //$NON-NLS-1$ |
| if (i == 0) |
| commandLine += arg + "\n" + value + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ |
| else { |
| i += arg.length() + 1; |
| String left = commandLine.substring(0, i); |
| int j = commandLine.indexOf('\n', i); |
| String right = commandLine.substring(j); |
| commandLine = left + value + right; |
| } |
| return commandLine; |
| } |
| |
| private static void finalizeProperties() { |
| // if check config is unknown and we are in dev mode, |
| if (System.getProperty(PROP_DEV) != null && System.getProperty(PROP_CHECK_CONFIG) == null) |
| System.getProperties().put(PROP_CHECK_CONFIG, "true"); //$NON-NLS-1$ |
| } |
| |
| private static class InitialBundle { |
| public final String locationString; |
| public final URL location; |
| public final int level; |
| public final boolean start; |
| |
| InitialBundle(String locationString, URL location, int level, boolean start) { |
| this.locationString = locationString; |
| this.location = location; |
| this.level = level; |
| this.start = start; |
| } |
| } |
| } |