- First of all, we take a look at class EclipseStarter
-
ElipseStarter
1 /******************************************************************************* 2 * Copyright (c) 2003, 2012 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 * Alex Blewitt (bug 172969) 11 *******************************************************************************/ 12 package org.eclipse.core.runtime.adaptor; 13 14 import java.io.*; 15 import java.lang.reflect.Constructor; 16 import java.lang.reflect.Method; 17 import java.net.*; 18 import java.security.CodeSource; 19 import java.security.ProtectionDomain; 20 import java.util.*; 21 import org.eclipse.core.runtime.internal.adaptor.*; 22 import org.eclipse.osgi.framework.adaptor.*; 23 import org.eclipse.osgi.framework.internal.core.*; 24 import org.eclipse.osgi.framework.internal.core.Constants; 25 import org.eclipse.osgi.framework.log.FrameworkLog; 26 import org.eclipse.osgi.framework.log.FrameworkLogEntry; 27 import org.eclipse.osgi.internal.baseadaptor.BaseStorageHook; 28 import org.eclipse.osgi.internal.profile.Profile; 29 import org.eclipse.osgi.service.datalocation.Location; 30 import org.eclipse.osgi.service.resolver.*; 31 import org.eclipse.osgi.service.runnable.ApplicationLauncher; 32 import org.eclipse.osgi.service.runnable.StartupMonitor; 33 import org.eclipse.osgi.util.ManifestElement; 34 import org.eclipse.osgi.util.NLS; 35 import org.osgi.framework.*; 36 import org.osgi.service.packageadmin.PackageAdmin; 37 import org.osgi.service.startlevel.StartLevel; 38 import org.osgi.util.tracker.ServiceTracker; 39 40 /** 41 * Special startup class for the Eclipse Platform. This class cannot be 42 * instantiated; all functionality is provided by static methods. 43 * <p> 44 * The Eclipse Platform makes heavy use of Java class loaders for loading 45 * plug-ins. Even the Eclipse Runtime itself and the OSGi framework need 46 * to be loaded by special class loaders. The upshot is that a 47 * client program (such as a Java main program, a servlet) cannot 48 * reference any part of Eclipse directly. Instead, a client must use this 49 * loader class to start the platform, invoking functionality defined 50 * in plug-ins, and shutting down the platform when done. 51 * </p> 52 * <p>Note that the fields on this class are not API. </p> 53 * @since 3.0 54 * @noextend This class is not intended to be subclassed by clients. 55 */ 56 public class EclipseStarter { 57 private static FrameworkAdaptor adaptor; 58 private static BundleContext context; 59 private static boolean initialize = false; 60 public static boolean debug = false; 61 private static boolean running = false; 62 private static Framework framework = null; 63 private static ServiceRegistration<?> defaultMonitorRegistration = null; 64 private static ServiceRegistration<?> appLauncherRegistration = null; 65 private static ServiceRegistration<?> splashStreamRegistration = null; 66 67 // command line arguments 68 private static final String CLEAN = "-clean"; //$NON-NLS-1$ 69 private static final String CONSOLE = "-console"; //$NON-NLS-1$ 70 private static final String CONSOLE_LOG = "-consoleLog"; //$NON-NLS-1$ 71 private static final String DEBUG = "-debug"; //$NON-NLS-1$ 72 private static final String INITIALIZE = "-initialize"; //$NON-NLS-1$ 73 private static final String DEV = "-dev"; //$NON-NLS-1$ 74 private static final String WS = "-ws"; //$NON-NLS-1$ 75 private static final String OS = "-os"; //$NON-NLS-1$ 76 private static final String ARCH = "-arch"; //$NON-NLS-1$ 77 private static final String NL = "-nl"; //$NON-NLS-1$ 78 private static final String NL_EXTENSIONS = "-nlExtensions"; //$NON-NLS-1$ 79 private static final String CONFIGURATION = "-configuration"; //$NON-NLS-1$ 80 private static final String USER = "-user"; //$NON-NLS-1$ 81 private static final String NOEXIT = "-noExit"; //$NON-NLS-1$ 82 private static final String LAUNCHER = "-launcher"; //$NON-NLS-1$ 83 84 // this is more of an Eclipse argument but this OSGi implementation stores its 85 // metadata alongside Eclipse's. 86 private static final String DATA = "-data"; //$NON-NLS-1$ 87 88 // System properties 89 public static final String PROP_BUNDLES = "osgi.bundles"; //$NON-NLS-1$ 90 public static final String PROP_BUNDLES_STARTLEVEL = "osgi.bundles.defaultStartLevel"; //$NON-NLS-1$ //The start level used to install the bundles 91 public static final String PROP_EXTENSIONS = "osgi.framework.extensions"; //$NON-NLS-1$ 92 public static final String PROP_INITIAL_STARTLEVEL = "osgi.startLevel"; //$NON-NLS-1$ //The start level when the fwl start 93 public static final String PROP_DEBUG = "osgi.debug"; //$NON-NLS-1$ 94 public static final String PROP_DEV = "osgi.dev"; //$NON-NLS-1$ 95 public static final String PROP_CLEAN = "osgi.clean"; //$NON-NLS-1$ 96 public static final String PROP_CONSOLE = "osgi.console"; //$NON-NLS-1$ 97 public static final String PROP_CONSOLE_CLASS = "osgi.consoleClass"; //$NON-NLS-1$ 98 public static final String PROP_CHECK_CONFIG = "osgi.checkConfiguration"; //$NON-NLS-1$ 99 public static final String PROP_OS = "osgi.os"; //$NON-NLS-1$ 100 public static final String PROP_WS = "osgi.ws"; //$NON-NLS-1$ 101 public static final String PROP_NL = "osgi.nl"; //$NON-NLS-1$ 102 private static final String PROP_NL_EXTENSIONS = "osgi.nl.extensions"; //$NON-NLS-1$ 103 public static final String PROP_ARCH = "osgi.arch"; //$NON-NLS-1$ 104 public static final String PROP_ADAPTOR = "osgi.adaptor"; //$NON-NLS-1$ 105 public static final String PROP_SYSPATH = "osgi.syspath"; //$NON-NLS-1$ 106 public static final String PROP_LOGFILE = "osgi.logfile"; //$NON-NLS-1$ 107 public static final String PROP_FRAMEWORK = "osgi.framework"; //$NON-NLS-1$ 108 public static final String PROP_INSTALL_AREA = "osgi.install.area"; //$NON-NLS-1$ 109 public static final String PROP_FRAMEWORK_SHAPE = "osgi.framework.shape"; //$NON-NLS-1$ //the shape of the fwk (jar, or folder) 110 public static final String PROP_NOSHUTDOWN = "osgi.noShutdown"; //$NON-NLS-1$ 111 private static final String PROP_FORCED_RESTART = "osgi.forcedRestart"; //$NON-NLS-1$ 112 113 public static final String PROP_EXITCODE = "eclipse.exitcode"; //$NON-NLS-1$ 114 public static final String PROP_EXITDATA = "eclipse.exitdata"; //$NON-NLS-1$ 115 public static final String PROP_CONSOLE_LOG = "eclipse.consoleLog"; //$NON-NLS-1$ 116 public static final String PROP_IGNOREAPP = "eclipse.ignoreApp"; //$NON-NLS-1$ 117 public static final String PROP_REFRESH_BUNDLES = "eclipse.refreshBundles"; //$NON-NLS-1$ 118 private static final String PROP_ALLOW_APPRELAUNCH = "eclipse.allowAppRelaunch"; //$NON-NLS-1$ 119 private static final String PROP_APPLICATION_LAUNCHDEFAULT = "eclipse.application.launchDefault"; //$NON-NLS-1$ 120 121 private static final String FILE_SCHEME = "file:"; //$NON-NLS-1$ 122 private static final String REFERENCE_SCHEME = "reference:"; //$NON-NLS-1$ 123 private static final String REFERENCE_PROTOCOL = "reference"; //$NON-NLS-1$ 124 private static final String INITIAL_LOCATION = "initial@"; //$NON-NLS-1$ 125 /** string containing the classname of the adaptor to be used in this framework instance */ 126 protected static final String DEFAULT_ADAPTOR_CLASS = "org.eclipse.osgi.baseadaptor.BaseAdaptor"; //$NON-NLS-1$ 127 128 private static final int DEFAULT_INITIAL_STARTLEVEL = 6; // default value for legacy purposes 129 private static final String DEFAULT_BUNDLES_STARTLEVEL = "4"; //$NON-NLS-1$ 130 131 private static FrameworkLog log; 132 // directory of serch candidates keyed by directory abs path -> directory listing (bug 122024) 133 private static Map<String, String[]> searchCandidates = new HashMap<String, String[]>(4); 134 private static EclipseAppLauncher appLauncher; 135 private static List<Runnable> shutdownHandlers; 136 137 private static ConsoleManager consoleMgr = null; 138 139 /** 140 * This is the main to start osgi. 141 * It only works when the framework is being jared as a single jar 142 */ 143 public static void main(String[] args) throws Exception { 144 if (FrameworkProperties.getProperty("eclipse.startTime") == null) //$NON-NLS-1$ 145 FrameworkProperties.setProperty("eclipse.startTime", Long.toString(System.currentTimeMillis())); //$NON-NLS-1$ 146 if (FrameworkProperties.getProperty(PROP_NOSHUTDOWN) == null) 147 FrameworkProperties.setProperty(PROP_NOSHUTDOWN, "true"); //$NON-NLS-1$ 148 // set the compatibility boot delegation flag to false to get "standard" OSGi behavior WRT boot delegation (bug 178477) 149 if (FrameworkProperties.getProperty(Constants.OSGI_COMPATIBILITY_BOOTDELEGATION) == null) 150 FrameworkProperties.setProperty(Constants.OSGI_COMPATIBILITY_BOOTDELEGATION, "false"); //$NON-NLS-1$ 151 Object result = run(args, null); 152 if (result instanceof Integer && !Boolean.valueOf(FrameworkProperties.getProperty(PROP_NOSHUTDOWN)).booleanValue()) 153 System.exit(((Integer) result).intValue()); 154 } 155 156 /** 157 * Launches the platform and runs a single application. The application is either identified 158 * in the given arguments (e.g., -application <app id>) or in the <code>eclipse.application</code> 159 * System property. This convenience method starts 160 * up the platform, runs the indicated application, and then shuts down the 161 * platform. The platform must not be running already. 162 * 163 * @param args the command line-style arguments used to configure the platform 164 * @param endSplashHandler the block of code to run to tear down the splash 165 * screen or <code>null</code> if no tear down is required 166 * @return the result of running the application 167 * @throws Exception if anything goes wrong 168 */ 169 public static Object run(String[] args, Runnable endSplashHandler) throws Exception { 170 if (Profile.PROFILE && Profile.STARTUP) 171 Profile.logEnter("EclipseStarter.run()", null); //$NON-NLS-1$ 172 if (running) 173 throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_ALREADY_RUNNING); 174 boolean startupFailed = true; 175 try { 176 startup(args, endSplashHandler); 177 startupFailed = false; 178 if (Boolean.valueOf(FrameworkProperties.getProperty(PROP_IGNOREAPP)).booleanValue() || isForcedRestart()) 179 return null; 180 return run(null); 181 } catch (Throwable e) { 182 // ensure the splash screen is down 183 if (endSplashHandler != null) 184 endSplashHandler.run(); 185 // may use startupFailed to understand where the error happened 186 FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, startupFailed ? EclipseAdaptorMsg.ECLIPSE_STARTUP_STARTUP_ERROR : EclipseAdaptorMsg.ECLIPSE_STARTUP_APP_ERROR, 1, e, null); 187 if (log != null) 188 log.log(logEntry); 189 else 190 // TODO desperate measure - ideally, we should write this to disk (a la Main.log) 191 e.printStackTrace(); 192 } finally { 193 try { 194 // The application typically sets the exit code however the framework can request that 195 // it be re-started. We need to check for this and potentially override the exit code. 196 if (isForcedRestart()) 197 FrameworkProperties.setProperty(PROP_EXITCODE, "23"); //$NON-NLS-1$ 198 if (!Boolean.valueOf(FrameworkProperties.getProperty(PROP_NOSHUTDOWN)).booleanValue()) 199 shutdown(); 200 } catch (Throwable e) { 201 FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, EclipseAdaptorMsg.ECLIPSE_STARTUP_SHUTDOWN_ERROR, 1, e, null); 202 if (log != null) 203 log.log(logEntry); 204 else 205 // TODO desperate measure - ideally, we should write this to disk (a la Main.log) 206 e.printStackTrace(); 207 } 208 if (Profile.PROFILE && Profile.STARTUP) 209 Profile.logExit("EclipseStarter.run()"); //$NON-NLS-1$ 210 if (Profile.PROFILE) { 211 String report = Profile.getProfileLog(); 212 // avoiding writing to the console if there is nothing to print 213 if (report != null && report.length() > 0) 214 System.out.println(report); 215 } 216 } 217 // we only get here if an error happened 218 if (FrameworkProperties.getProperty(PROP_EXITCODE) == null) { 219 FrameworkProperties.setProperty(PROP_EXITCODE, "13"); //$NON-NLS-1$ 220 FrameworkProperties.setProperty(PROP_EXITDATA, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_CHECK_LOG, log == null ? null : log.getFile().getPath())); 221 } 222 return null; 223 } 224 225 /** 226 * Returns true if the platform is already running, false otherwise. 227 * @return whether or not the platform is already running 228 */ 229 public static boolean isRunning() { 230 return running; 231 } 232 233 /** 234 * Starts the platform and sets it up to run a single application. The application is either identified 235 * in the given arguments (e.g., -application <app id>) or in the <code>eclipse.application</code> 236 * System property. The platform must not be running already. 237 * <p> 238 * The given runnable (if not <code>null</code>) is used to tear down the splash screen if required. 239 * </p> 240 * @param args the arguments passed to the application 241 * @return BundleContext the context of the system bundle 242 * @throws Exception if anything goes wrong 243 */ 244 public static BundleContext startup(String[] args, Runnable endSplashHandler) throws Exception { 245 if (Profile.PROFILE && Profile.STARTUP) 246 Profile.logEnter("EclipseStarter.startup()", null); //$NON-NLS-1$ 247 if (running) 248 throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_ALREADY_RUNNING); 249 FrameworkProperties.initializeProperties(); 250 processCommandLine(args); 251 LocationManager.initializeLocations(); 252 loadConfigurationInfo(); 253 finalizeProperties(); 254 if (Profile.PROFILE) 255 Profile.initProps(); // catch any Profile properties set in eclipse.properties... 256 if (Profile.PROFILE && Profile.STARTUP) 257 Profile.logTime("EclipseStarter.startup()", "props inited"); //$NON-NLS-1$ //$NON-NLS-2$ 258 adaptor = createAdaptor(); 259 log = adaptor.getFrameworkLog(); 260 if (Profile.PROFILE && Profile.STARTUP) 261 Profile.logTime("EclipseStarter.startup()", "adapter created"); //$NON-NLS-1$ //$NON-NLS-2$ 262 framework = new Framework(adaptor); 263 if (Profile.PROFILE && Profile.STARTUP) 264 Profile.logTime("EclipseStarter.startup()", "OSGi created"); //$NON-NLS-1$ //$NON-NLS-2$ 265 context = framework.getBundle(0).getBundleContext(); 266 registerFrameworkShutdownHandlers(); 267 publishSplashScreen(endSplashHandler); 268 if (Profile.PROFILE && Profile.STARTUP) 269 Profile.logTime("EclipseStarter.startup()", "osgi launched"); //$NON-NLS-1$ //$NON-NLS-2$ 270 consoleMgr = ConsoleManager.startConsole(framework); 271 if (Profile.PROFILE && Profile.STARTUP) { 272 Profile.logTime("EclipseStarter.startup()", "console started"); //$NON-NLS-1$ //$NON-NLS-2$ 273 } 274 framework.launch(); 275 // save the cached timestamp before loading basic bundles; this is needed so we can do a proper timestamp check when logging resolver errors 276 long stateStamp = adaptor.getState().getTimeStamp(); 277 Bundle[] startBundles = loadBasicBundles(); 278 279 if (startBundles == null || ("true".equals(FrameworkProperties.getProperty(PROP_REFRESH_BUNDLES)) && refreshPackages(getCurrentBundles(false)))) { //$NON-NLS-1$ 280 waitForShutdown(); 281 return context; // cannot continue; loadBasicBundles caused refreshPackages to shutdown the framework 282 } 283 284 if (Profile.PROFILE && Profile.STARTUP) 285 Profile.logTime("EclipseStarter.startup()", "loading basic bundles"); //$NON-NLS-1$ //$NON-NLS-2$ 286 287 // set the framework start level to the ultimate value. This will actually start things 288 // running if they are persistently active. 289 setStartLevel(getStartLevel()); 290 if (Profile.PROFILE && Profile.STARTUP) 291 Profile.logTime("EclipseStarter.startup()", "StartLevel set"); //$NON-NLS-1$ //$NON-NLS-2$ 292 // they should all be active by this time 293 ensureBundlesActive(startBundles); 294 295 // in the case where the built-in console is disabled we should try to start the console bundle 296 try { 297 consoleMgr.checkForConsoleBundle(); 298 } catch (BundleException e) { 299 FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, e.getMessage(), 0, e, null); 300 log.log(entry); 301 } 302 if (debug || FrameworkProperties.getProperty(PROP_DEV) != null) 303 // only spend time showing unresolved bundles in dev/debug mode and the state has changed 304 if (stateStamp != adaptor.getState().getTimeStamp()) 305 logUnresolvedBundles(context.getBundles()); 306 running = true; 307 if (Profile.PROFILE && Profile.STARTUP) 308 Profile.logExit("EclipseStarter.startup()"); //$NON-NLS-1$ 309 return context; 310 } 311 312 private static int getStartLevel() { 313 String level = FrameworkProperties.getProperty(PROP_INITIAL_STARTLEVEL); 314 if (level != null) 315 try { 316 return Integer.parseInt(level); 317 } catch (NumberFormatException e) { 318 if (debug) 319 System.out.println("Start level = " + level + " parsed. Using hardcoded default: 6"); //$NON-NLS-1$ //$NON-NLS-2$ 320 } 321 return DEFAULT_INITIAL_STARTLEVEL; 322 } 323 324 /** 325 * Runs the application for which the platform was started. The platform 326 * must be running. 327 * <p> 328 * The given argument is passed to the application being run. If it is <code>null</code> 329 * then the command line arguments used in starting the platform, and not consumed 330 * by the platform code, are passed to the application as a <code>String[]</code>. 331 * </p> 332 * @param argument the argument passed to the application. May be <code>null</code> 333 * @return the result of running the application 334 * @throws Exception if anything goes wrong 335 */ 336 public static Object run(Object argument) throws Exception { 337 if (Profile.PROFILE && Profile.STARTUP) 338 Profile.logEnter("EclipseStarter.run(Object)()", null); //$NON-NLS-1$ 339 if (!running) 340 throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_NOT_RUNNING); 341 // if we are just initializing, do not run the application just return. 342 if (initialize) 343 return new Integer(0); 344 try { 345 if (appLauncher == null) { 346 boolean launchDefault = Boolean.valueOf(FrameworkProperties.getProperty(PROP_APPLICATION_LAUNCHDEFAULT, "true")).booleanValue(); //$NON-NLS-1$ 347 // create the ApplicationLauncher and register it as a service 348 appLauncher = new EclipseAppLauncher(context, Boolean.valueOf(FrameworkProperties.getProperty(PROP_ALLOW_APPRELAUNCH)).booleanValue(), launchDefault, log); 349 appLauncherRegistration = context.registerService(ApplicationLauncher.class.getName(), appLauncher, null); 350 // must start the launcher AFTER service restration because this method 351 // blocks and runs the application on the current thread. This method 352 // will return only after the application has stopped. 353 return appLauncher.start(argument); 354 } 355 return appLauncher.reStart(argument); 356 } catch (Exception e) { 357 if (log != null && context != null) // context can be null if OSGi failed to launch (bug 151413) 358 logUnresolvedBundles(context.getBundles()); 359 throw e; 360 } 361 } 362 363 /** 364 * Shuts down the Platform. The state of the Platform is not automatically 365 * saved before shutting down. 366 * <p> 367 * On return, the Platform will no longer be running (but could be re-launched 368 * with another call to startup). If relaunching, care must be taken to reinitialize 369 * any System properties which the platform uses (e.g., osgi.instance.area) as 370 * some policies in the platform do not allow resetting of such properties on 371 * subsequent runs. 372 * </p><p> 373 * Any objects handed out by running Platform, 374 * including Platform runnables obtained via getRunnable, will be 375 * permanently invalid. The effects of attempting to invoke methods 376 * on invalid objects is undefined. 377 * </p> 378 * @throws Exception if anything goes wrong 379 */ 380 public static void shutdown() throws Exception { 381 if (!running || framework == null) 382 return; 383 if (appLauncherRegistration != null) 384 appLauncherRegistration.unregister(); 385 if (splashStreamRegistration != null) 386 splashStreamRegistration.unregister(); 387 if (defaultMonitorRegistration != null) 388 defaultMonitorRegistration.unregister(); 389 if (appLauncher != null) 390 appLauncher.shutdown(); 391 appLauncherRegistration = null; 392 appLauncher = null; 393 splashStreamRegistration = null; 394 defaultMonitorRegistration = null; 395 if (consoleMgr != null) { 396 consoleMgr.stopConsole(); 397 consoleMgr = null; 398 } 399 framework.close(); 400 framework = null; 401 context = null; 402 running = false; 403 } 404 405 private static void ensureBundlesActive(Bundle[] bundles) { 406 ServiceTracker<StartLevel, StartLevel> tracker = null; 407 try { 408 for (int i = 0; i < bundles.length; i++) { 409 if (bundles[i].getState() != Bundle.ACTIVE) { 410 if (bundles[i].getState() == Bundle.INSTALLED) { 411 // Log that the bundle is not resolved 412 log.log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_RESOLVED, bundles[i].getLocation()), 0, null, null)); 413 continue; 414 } 415 // check that the startlevel allows the bundle to be active (111550) 416 if (tracker == null) { 417 tracker = new ServiceTracker<StartLevel, StartLevel>(context, StartLevel.class.getName(), null); 418 tracker.open(); 419 } 420 StartLevel sl = tracker.getService(); 421 if (sl != null && (sl.getBundleStartLevel(bundles[i]) <= sl.getStartLevel())) { 422 log.log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_ACTIVE, bundles[i]), 0, null, null)); 423 } 424 } 425 } 426 } finally { 427 if (tracker != null) 428 tracker.close(); 429 } 430 } 431 432 private static void logUnresolvedBundles(Bundle[] bundles) { 433 State state = adaptor.getState(); 434 FrameworkLog logService = adaptor.getFrameworkLog(); 435 StateHelper stateHelper = adaptor.getPlatformAdmin().getStateHelper(); 436 437 // first lets look for missing leaf constraints (bug 114120) 438 VersionConstraint[] leafConstraints = stateHelper.getUnsatisfiedLeaves(state.getBundles()); 439 // hash the missing leaf constraints by the declaring bundles 440 Map<BundleDescription, List<VersionConstraint>> missing = new HashMap<BundleDescription, List<VersionConstraint>>(); 441 for (int i = 0; i < leafConstraints.length; i++) { 442 // only include non-optional and non-dynamic constraint leafs 443 if (leafConstraints[i] instanceof BundleSpecification && ((BundleSpecification) leafConstraints[i]).isOptional()) 444 continue; 445 if (leafConstraints[i] instanceof ImportPackageSpecification) { 446 if (ImportPackageSpecification.RESOLUTION_OPTIONAL.equals(((ImportPackageSpecification) leafConstraints[i]).getDirective(Constants.RESOLUTION_DIRECTIVE))) 447 continue; 448 if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(((ImportPackageSpecification) leafConstraints[i]).getDirective(Constants.RESOLUTION_DIRECTIVE))) 449 continue; 450 } 451 BundleDescription bundle = leafConstraints[i].getBundle(); 452 List<VersionConstraint> constraints = missing.get(bundle); 453 if (constraints == null) { 454 constraints = new ArrayList<VersionConstraint>(); 455 missing.put(bundle, constraints); 456 } 457 constraints.add(leafConstraints[i]); 458 } 459 460 // found some bundles with missing leaf constraints; log them first 461 if (missing.size() > 0) { 462 FrameworkLogEntry[] rootChildren = new FrameworkLogEntry[missing.size()]; 463 int rootIndex = 0; 464 for (Iterator<BundleDescription> iter = missing.keySet().iterator(); iter.hasNext(); rootIndex++) { 465 BundleDescription description = iter.next(); 466 String symbolicName = description.getSymbolicName() == null ? FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME : description.getSymbolicName(); 467 String generalMessage = NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_RESOLVED, description.getLocation()); 468 List<VersionConstraint> constraints = missing.get(description); 469 FrameworkLogEntry[] logChildren = new FrameworkLogEntry[constraints.size()]; 470 for (int i = 0; i < logChildren.length; i++) 471 logChildren[i] = new FrameworkLogEntry(symbolicName, FrameworkLogEntry.WARNING, 0, MessageHelper.getResolutionFailureMessage(constraints.get(i)), 0, null, null); 472 rootChildren[rootIndex] = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.WARNING, 0, generalMessage, 0, null, logChildren); 473 } 474 logService.log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.WARNING, 0, EclipseAdaptorMsg.ECLIPSE_STARTUP_ROOTS_NOT_RESOLVED, 0, null, rootChildren)); 475 } 476 477 // There may be some bundles unresolved for other reasons, causing the system to be unresolved 478 // log all unresolved constraints now 479 List<FrameworkLogEntry> allChildren = new ArrayList<FrameworkLogEntry>(); 480 for (int i = 0; i < bundles.length; i++) 481 if (bundles[i].getState() == Bundle.INSTALLED) { 482 String symbolicName = bundles[i].getSymbolicName() == null ? FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME : bundles[i].getSymbolicName(); 483 String generalMessage = NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_RESOLVED, bundles[i]); 484 BundleDescription description = state.getBundle(bundles[i].getBundleId()); 485 // for some reason, the state does not know about that bundle 486 if (description == null) 487 continue; 488 FrameworkLogEntry[] logChildren = null; 489 VersionConstraint[] unsatisfied = stateHelper.getUnsatisfiedConstraints(description); 490 if (unsatisfied.length > 0) { 491 // the bundle wasn't resolved due to some of its constraints were unsatisfiable 492 logChildren = new FrameworkLogEntry[unsatisfied.length]; 493 for (int j = 0; j < unsatisfied.length; j++) 494 logChildren[j] = new FrameworkLogEntry(symbolicName, FrameworkLogEntry.WARNING, 0, MessageHelper.getResolutionFailureMessage(unsatisfied[j]), 0, null, null); 495 } else { 496 ResolverError[] resolverErrors = state.getResolverErrors(description); 497 if (resolverErrors.length > 0) { 498 logChildren = new FrameworkLogEntry[resolverErrors.length]; 499 for (int j = 0; j < resolverErrors.length; j++) 500 logChildren[j] = new FrameworkLogEntry(symbolicName, FrameworkLogEntry.WARNING, 0, resolverErrors[j].toString(), 0, null, null); 501 } 502 } 503 504 allChildren.add(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.WARNING, 0, generalMessage, 0, null, logChildren)); 505 } 506 if (allChildren.size() > 0) 507 logService.log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.WARNING, 0, EclipseAdaptorMsg.ECLIPSE_STARTUP_ALL_NOT_RESOLVED, 0, null, allChildren.toArray(new FrameworkLogEntry[allChildren.size()]))); 508 } 509 510 private static void publishSplashScreen(final Runnable endSplashHandler) { 511 if (endSplashHandler == null) 512 return; 513 // register the output stream to the launcher if it exists 514 try { 515 Method method = endSplashHandler.getClass().getMethod("getOutputStream", new Class[0]); //$NON-NLS-1$ 516 Object outputStream = method.invoke(endSplashHandler, new Object[0]); 517 if (outputStream instanceof OutputStream) { 518 Dictionary<String, Object> osProperties = new Hashtable<String, Object>(); 519 osProperties.put("name", "splashstream"); //$NON-NLS-1$//$NON-NLS-2$ 520 splashStreamRegistration = context.registerService(OutputStream.class.getName(), outputStream, osProperties); 521 } 522 } catch (Exception ex) { 523 // ignore 524 } 525 // keep this splash handler as the default startup monitor 526 try { 527 Dictionary<String, Object> monitorProps = new Hashtable<String, Object>(); 528 monitorProps.put(Constants.SERVICE_RANKING, new Integer(Integer.MIN_VALUE)); 529 defaultMonitorRegistration = context.registerService(StartupMonitor.class.getName(), new DefaultStartupMonitor(endSplashHandler), monitorProps); 530 } catch (IllegalStateException e) { 531 //splash handler did not provide the necessary methods, ignore it 532 } 533 } 534 535 @SuppressWarnings("deprecation") 536 private static URL searchForBundle(String name, String parent) throws MalformedURLException { 537 URL url = null; 538 File fileLocation = null; 539 boolean reference = false; 540 try { 541 new URL(name); // quick check to see if the name is a valid URL 542 url = new URL(new File(parent).toURL(), name); 543 } catch (MalformedURLException e) { 544 // TODO this is legacy support for non-URL names. It should be removed eventually. 545 // if name was not a URL then construct one. 546 // Assume it should be a reference and that it is relative. This support need not 547 // be robust as it is temporary.. 548 File child = new File(name); 549 fileLocation = child.isAbsolute() ? child : new File(parent, name); 550 url = new URL(REFERENCE_PROTOCOL, null, fileLocation.toURL().toExternalForm()); 551 reference = true; 552 } 553 // if the name was a URL then see if it is relative. If so, insert syspath. 554 if (!reference) { 555 URL baseURL = url; 556 // if it is a reference URL then strip off the reference: and set base to the file:... 557 if (url.getProtocol().equals(REFERENCE_PROTOCOL)) { 558 reference = true; 559 String baseSpec = url.getFile(); 560 if (baseSpec.startsWith(FILE_SCHEME)) { 561 File child = new File(baseSpec.substring(5)); 562 baseURL = child.isAbsolute() ? child.toURL() : new File(parent, child.getPath()).toURL(); 563 } else 564 baseURL = new URL(baseSpec); 565 } 566 567 fileLocation = new File(baseURL.getFile()); 568 // if the location is relative, prefix it with the parent 569 if (!fileLocation.isAbsolute()) 570 fileLocation = new File(parent, fileLocation.toString()); 571 } 572 // If the result is a reference then search for the real result and 573 // reconstruct the answer. 574 if (reference) { 575 String result = searchFor(fileLocation.getName(), new File(fileLocation.getParent()).getAbsolutePath()); 576 if (result != null) 577 url = new URL(REFERENCE_PROTOCOL, null, FILE_SCHEME + result); 578 else 579 return null; 580 } 581 582 // finally we have something worth trying 583 try { 584 URLConnection result = url.openConnection(); 585 result.connect(); 586 return url; 587 } catch (IOException e) { 588 // int i = location.lastIndexOf('_'); 589 // return i == -1? location : location.substring(0, i); 590 return null; 591 } 592 } 593 594 /* 595 * Ensure all basic bundles are installed, resolved and scheduled to start. Returns an array containing 596 * all basic bundles that are marked to start. 597 * Returns null if the framework has been shutdown as a result of refreshPackages 598 */ 599 private static Bundle[] loadBasicBundles() { 600 long startTime = System.currentTimeMillis(); 601 String osgiBundles = FrameworkProperties.getProperty(PROP_BUNDLES); 602 String osgiExtensions = FrameworkProperties.getProperty(PROP_EXTENSIONS); 603 if (osgiExtensions != null && osgiExtensions.length() > 0) { 604 osgiBundles = osgiExtensions + ',' + osgiBundles; 605 FrameworkProperties.setProperty(PROP_BUNDLES, osgiBundles); 606 } 607 String[] installEntries = getArrayFromList(osgiBundles, ","); //$NON-NLS-1$ 608 // get the initial bundle list from the installEntries 609 InitialBundle[] initialBundles = getInitialBundles(installEntries); 610 // get the list of currently installed initial bundles from the framework 611 Bundle[] curInitBundles = getCurrentBundles(true); 612 613 // list of bundles to be refreshed 614 List<Bundle> toRefresh = new ArrayList<Bundle>(curInitBundles.length); 615 // uninstall any of the currently installed bundles that do not exist in the 616 // initial bundle list from installEntries. 617 uninstallBundles(curInitBundles, initialBundles, toRefresh); 618 619 // install the initialBundles that are not already installed. 620 List<Bundle> startBundles = new ArrayList<Bundle>(installEntries.length); 621 List<Bundle> lazyActivationBundles = new ArrayList<Bundle>(installEntries.length); 622 installBundles(initialBundles, curInitBundles, startBundles, lazyActivationBundles, toRefresh); 623 624 // If we installed/uninstalled something, force a refresh of all installed/uninstalled bundles 625 if (!toRefresh.isEmpty() && refreshPackages(toRefresh.toArray(new Bundle[toRefresh.size()]))) 626 return null; // cannot continue; refreshPackages shutdown the framework 627 628 // schedule all basic bundles to be started 629 Bundle[] startInitBundles = startBundles.toArray(new Bundle[startBundles.size()]); 630 Bundle[] lazyInitBundles = lazyActivationBundles.toArray(new Bundle[lazyActivationBundles.size()]); 631 startBundles(startInitBundles, lazyInitBundles); 632 633 if (debug) 634 System.out.println("Time to load bundles: " + (System.currentTimeMillis() - startTime)); //$NON-NLS-1$ 635 return startInitBundles; 636 } 637 638 private static InitialBundle[] getInitialBundles(String[] installEntries) { 639 searchCandidates.clear(); 640 List<InitialBundle> result = new ArrayList<InitialBundle>(installEntries.length); 641 int defaultStartLevel = Integer.parseInt(FrameworkProperties.getProperty(PROP_BUNDLES_STARTLEVEL, DEFAULT_BUNDLES_STARTLEVEL)); 642 String syspath = getSysPath(); 643 // should canonicalize the syspath. 644 try { 645 syspath = new File(syspath).getCanonicalPath(); 646 } catch (IOException ioe) { 647 // do nothing 648 } 649 for (int i = 0; i < installEntries.length; i++) { 650 String name = installEntries[i]; 651 int level = defaultStartLevel; 652 boolean start = false; 653 int index = name.lastIndexOf('@'); 654 if (index >= 0) { 655 String[] attributes = getArrayFromList(name.substring(index + 1, name.length()), ":"); //$NON-NLS-1$ 656 for (int j = 0; j < attributes.length; j++) { 657 String attribute = attributes[j]; 658 if (attribute.equals("start")) //$NON-NLS-1$ 659 start = true; 660 else { 661 try { 662 level = Integer.parseInt(attribute); 663 } catch (NumberFormatException e) { // bug 188089 664 index = name.length(); 665 continue; 666 } 667 } 668 } 669 name = name.substring(0, index); 670 } 671 try { 672 URL location = searchForBundle(name, syspath); 673 if (location == null) { 674 FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_BUNDLE_NOT_FOUND, installEntries[i]), 0, null, null); 675 log.log(entry); 676 // skip this entry 677 continue; 678 } 679 location = makeRelative(LocationManager.getInstallLocation().getURL(), location); 680 String locationString = INITIAL_LOCATION + location.toExternalForm(); 681 result.add(new InitialBundle(locationString, location, level, start)); 682 } catch (IOException e) { 683 log.log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, e.getMessage(), 0, e, null)); 684 } 685 } 686 return result.toArray(new InitialBundle[result.size()]); 687 } 688 689 // returns true if the refreshPackages operation caused the framework to shutdown 690 private static boolean refreshPackages(Bundle[] bundles) { 691 ServiceReference<?> packageAdminRef = context.getServiceReference(PackageAdmin.class.getName()); 692 PackageAdmin packageAdmin = null; 693 if (packageAdminRef != null) 694 packageAdmin = (PackageAdmin) context.getService(packageAdminRef); 695 if (packageAdmin == null) 696 return false; 697 // TODO this is such a hack it is silly. There are still cases for race conditions etc 698 // but this should allow for some progress... 699 final Semaphore semaphore = new Semaphore(0); 700 StartupEventListener listener = new StartupEventListener(semaphore, FrameworkEvent.PACKAGES_REFRESHED); 701 context.addFrameworkListener(listener); 702 context.addBundleListener(listener); 703 packageAdmin.refreshPackages(bundles); 704 context.ungetService(packageAdminRef); 705 updateSplash(semaphore, listener); 706 if (isForcedRestart()) 707 return true; 708 return false; 709 } 710 711 private static void waitForShutdown() { 712 if (!isForcedRestart()) 713 return; 714 // wait for the system bundle to stop 715 Bundle systemBundle = framework.getBundle(0); 716 int i = 0; 717 while (i < 5000 && (systemBundle.getState() & (Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING)) != 0) { 718 i += 200; 719 try { 720 Thread.sleep(200); 721 } catch (InterruptedException e) { 722 break; 723 } 724 } 725 } 726 727 /** 728 * Creates and returns the adaptor 729 * 730 * @return a FrameworkAdaptor object 731 */ 732 private static FrameworkAdaptor createAdaptor() throws Exception { 733 String adaptorClassName = FrameworkProperties.getProperty(PROP_ADAPTOR, DEFAULT_ADAPTOR_CLASS); 734 Class<?> adaptorClass = Class.forName(adaptorClassName); 735 Class<?>[] constructorArgs = new Class[] {String[].class}; 736 Constructor<?> constructor = adaptorClass.getConstructor(constructorArgs); 737 return (FrameworkAdaptor) constructor.newInstance(new Object[] {new String[0]}); 738 } 739 740 private static String[] processCommandLine(String[] args) throws Exception { 741 EclipseEnvironmentInfo.setAllArgs(args); 742 if (args.length == 0) { 743 EclipseEnvironmentInfo.setFrameworkArgs(args); 744 EclipseEnvironmentInfo.setAllArgs(args); 745 return args; 746 } 747 int[] configArgs = new int[args.length]; 748 configArgs[0] = -1; // need to initialize the first element to something that could not be an index. 749 int configArgIndex = 0; 750 for (int i = 0; i < args.length; i++) { 751 boolean found = false; 752 // check for args without parameters (i.e., a flag arg) 753 754 // check if debug should be enabled for the entire platform 755 // If this is the last arg or there is a following arg (i.e., arg+1 has a leading -), 756 // simply enable debug. Otherwise, assume that that the following arg is 757 // actually the filename of an options file. This will be processed below. 758 if (args[i].equalsIgnoreCase(DEBUG) && ((i + 1 == args.length) || ((i + 1 < args.length) && (args[i + 1].startsWith("-"))))) { //$NON-NLS-1$ 759 FrameworkProperties.setProperty(PROP_DEBUG, ""); //$NON-NLS-1$ 760 debug = true; 761 found = true; 762 } 763 764 // check if development mode should be enabled for the entire platform 765 // If this is the last arg or there is a following arg (i.e., arg+1 has a leading -), 766 // simply enable development mode. Otherwise, assume that that the following arg is 767 // actually some additional development time class path entries. This will be processed below. 768 if (args[i].equalsIgnoreCase(DEV) && ((i + 1 == args.length) || ((i + 1 < args.length) && (args[i + 1].startsWith("-"))))) { //$NON-NLS-1$ 769 FrameworkProperties.setProperty(PROP_DEV, ""); //$NON-NLS-1$ 770 found = true; 771 } 772 773 // look for the initialization arg 774 if (args[i].equalsIgnoreCase(INITIALIZE)) { 775 initialize = true; 776 found = true; 777 } 778 779 // look for the clean flag. 780 if (args[i].equalsIgnoreCase(CLEAN)) { 781 FrameworkProperties.setProperty(PROP_CLEAN, "true"); //$NON-NLS-1$ 782 found = true; 783 } 784 785 // look for the consoleLog flag 786 if (args[i].equalsIgnoreCase(CONSOLE_LOG)) { 787 FrameworkProperties.setProperty(PROP_CONSOLE_LOG, "true"); //$NON-NLS-1$ 788 found = true; 789 } 790 791 // look for the console with no port. 792 if (args[i].equalsIgnoreCase(CONSOLE) && ((i + 1 == args.length) || ((i + 1 < args.length) && (args[i + 1].startsWith("-"))))) { //$NON-NLS-1$ 793 FrameworkProperties.setProperty(PROP_CONSOLE, ""); //$NON-NLS-1$ 794 found = true; 795 } 796 797 if (args[i].equalsIgnoreCase(NOEXIT)) { 798 FrameworkProperties.setProperty(PROP_NOSHUTDOWN, "true"); //$NON-NLS-1$ 799 found = true; 800 } 801 802 if (found) { 803 configArgs[configArgIndex++] = i; 804 continue; 805 } 806 // check for args with parameters. If we are at the last argument or if the next one 807 // has a '-' as the first character, then we can't have an arg with a parm so continue. 808 if (i == args.length - 1 || args[i + 1].startsWith("-")) { //$NON-NLS-1$ 809 continue; 810 } 811 String arg = args[++i]; 812 813 // look for the console and port. 814 if (args[i - 1].equalsIgnoreCase(CONSOLE)) { 815 FrameworkProperties.setProperty(PROP_CONSOLE, arg); 816 found = true; 817 } 818 819 // look for the configuration location . 820 if (args[i - 1].equalsIgnoreCase(CONFIGURATION)) { 821 FrameworkProperties.setProperty(LocationManager.PROP_CONFIG_AREA, arg); 822 found = true; 823 } 824 825 // look for the data location for this instance. 826 if (args[i - 1].equalsIgnoreCase(DATA)) { 827 FrameworkProperties.setProperty(LocationManager.PROP_INSTANCE_AREA, arg); 828 found = true; 829 } 830 831 // look for the user location for this instance. 832 if (args[i - 1].equalsIgnoreCase(USER)) { 833 FrameworkProperties.setProperty(LocationManager.PROP_USER_AREA, arg); 834 found = true; 835 } 836 837 // look for the launcher location 838 if (args[i - 1].equalsIgnoreCase(LAUNCHER)) { 839 FrameworkProperties.setProperty(LocationManager.PROP_LAUNCHER, arg); 840 found = true; 841 } 842 // look for the development mode and class path entries. 843 if (args[i - 1].equalsIgnoreCase(DEV)) { 844 FrameworkProperties.setProperty(PROP_DEV, arg); 845 found = true; 846 } 847 848 // look for the debug mode and option file location. 849 if (args[i - 1].equalsIgnoreCase(DEBUG)) { 850 FrameworkProperties.setProperty(PROP_DEBUG, arg); 851 debug = true; 852 found = true; 853 } 854 855 // look for the window system. 856 if (args[i - 1].equalsIgnoreCase(WS)) { 857 FrameworkProperties.setProperty(PROP_WS, arg); 858 found = true; 859 } 860 861 // look for the operating system 862 if (args[i - 1].equalsIgnoreCase(OS)) { 863 FrameworkProperties.setProperty(PROP_OS, arg); 864 found = true; 865 } 866 867 // look for the system architecture 868 if (args[i - 1].equalsIgnoreCase(ARCH)) { 869 FrameworkProperties.setProperty(PROP_ARCH, arg); 870 found = true; 871 } 872 873 // look for the nationality/language 874 if (args[i - 1].equalsIgnoreCase(NL)) { 875 FrameworkProperties.setProperty(PROP_NL, arg); 876 found = true; 877 } 878 879 // look for the locale extensions 880 if (args[i - 1].equalsIgnoreCase(NL_EXTENSIONS)) { 881 FrameworkProperties.setProperty(PROP_NL_EXTENSIONS, arg); 882 found = true; 883 } 884 885 // done checking for args. Remember where an arg was found 886 if (found) { 887 configArgs[configArgIndex++] = i - 1; 888 configArgs[configArgIndex++] = i; 889 } 890 } 891 892 // remove all the arguments consumed by this argument parsing 893 if (configArgIndex == 0) { 894 EclipseEnvironmentInfo.setFrameworkArgs(new String[0]); 895 EclipseEnvironmentInfo.setAppArgs(args); 896 return args; 897 } 898 String[] appArgs = new String[args.length - configArgIndex]; 899 String[] frameworkArgs = new String[configArgIndex]; 900 configArgIndex = 0; 901 int j = 0; 902 int k = 0; 903 for (int i = 0; i < args.length; i++) { 904 if (i == configArgs[configArgIndex]) { 905 frameworkArgs[k++] = args[i]; 906 configArgIndex++; 907 } else 908 appArgs[j++] = args[i]; 909 } 910 EclipseEnvironmentInfo.setFrameworkArgs(frameworkArgs); 911 EclipseEnvironmentInfo.setAppArgs(appArgs); 912 return appArgs; 913 } 914 915 /** 916 * Returns the result of converting a list of comma-separated tokens into an array 917 * 918 * @return the array of string tokens 919 * @param prop the initial comma-separated string 920 */ 921 private static String[] getArrayFromList(String prop, String separator) { 922 return ManifestElement.getArrayFromList(prop, separator); 923 } 924 925 protected static String getSysPath() { 926 String result = FrameworkProperties.getProperty(PROP_SYSPATH); 927 if (result != null) 928 return result; 929 result = getSysPathFromURL(FrameworkProperties.getProperty(PROP_FRAMEWORK)); 930 if (result == null) 931 result = getSysPathFromCodeSource(); 932 if (result == null) 933 throw new IllegalStateException("Can not find the system path."); //$NON-NLS-1$ 934 if (Character.isUpperCase(result.charAt(0))) { 935 char[] chars = result.toCharArray(); 936 chars[0] = Character.toLowerCase(chars[0]); 937 result = new String(chars); 938 } 939 FrameworkProperties.setProperty(PROP_SYSPATH, result); 940 return result; 941 } 942 943 private static String getSysPathFromURL(String urlSpec) { 944 if (urlSpec == null) 945 return null; 946 URL url = LocationHelper.buildURL(urlSpec, false); 947 if (url == null) 948 return null; 949 File fwkFile = new File(url.getFile()); 950 fwkFile = new File(fwkFile.getAbsolutePath()); 951 fwkFile = new File(fwkFile.getParent()); 952 return fwkFile.getAbsolutePath(); 953 } 954 955 private static String getSysPathFromCodeSource() { 956 ProtectionDomain pd = EclipseStarter.class.getProtectionDomain(); 957 if (pd == null) 958 return null; 959 CodeSource cs = pd.getCodeSource(); 960 if (cs == null) 961 return null; 962 URL url = cs.getLocation(); 963 if (url == null) 964 return null; 965 String result = url.getFile(); 966 if (result.endsWith(".jar")) { //$NON-NLS-1$ 967 result = result.substring(0, result.lastIndexOf('/')); 968 if ("folder".equals(FrameworkProperties.getProperty(PROP_FRAMEWORK_SHAPE))) //$NON-NLS-1$ 969 result = result.substring(0, result.lastIndexOf('/')); 970 } else { 971 if (result.endsWith("/")) //$NON-NLS-1$ 972 result = result.substring(0, result.length() - 1); 973 result = result.substring(0, result.lastIndexOf('/')); 974 result = result.substring(0, result.lastIndexOf('/')); 975 } 976 return result; 977 } 978 979 private static Bundle[] getCurrentBundles(boolean includeInitial) { 980 Bundle[] installed = context.getBundles(); 981 List<Bundle> initial = new ArrayList<Bundle>(); 982 for (int i = 0; i < installed.length; i++) { 983 Bundle bundle = installed[i]; 984 if (bundle.getLocation().startsWith(INITIAL_LOCATION)) { 985 if (includeInitial) 986 initial.add(bundle); 987 } else if (!includeInitial && bundle.getBundleId() != 0) 988 initial.add(bundle); 989 } 990 return initial.toArray(new Bundle[initial.size()]); 991 } 992 993 private static Bundle getBundleByLocation(String location, Bundle[] bundles) { 994 for (int i = 0; i < bundles.length; i++) { 995 Bundle bundle = bundles[i]; 996 if (location.equalsIgnoreCase(bundle.getLocation())) 997 return bundle; 998 } 999 return null; 1000 } 1001 1002 private static void uninstallBundles(Bundle[] curInitBundles, InitialBundle[] newInitBundles, List<Bundle> toRefresh) { 1003 for (int i = 0; i < curInitBundles.length; i++) { 1004 boolean found = false; 1005 for (int j = 0; j < newInitBundles.length; j++) { 1006 if (curInitBundles[i].getLocation().equalsIgnoreCase(newInitBundles[j].locationString)) { 1007 found = true; 1008 break; 1009 } 1010 } 1011 if (!found) 1012 try { 1013 curInitBundles[i].uninstall(); 1014 toRefresh.add(curInitBundles[i]); 1015 } catch (BundleException e) { 1016 FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FAILED_UNINSTALL, curInitBundles[i].getLocation()), 0, e, null); 1017 log.log(entry); 1018 } 1019 } 1020 } 1021 1022 private static void installBundles(InitialBundle[] initialBundles, Bundle[] curInitBundles, List<Bundle> startBundles, List<Bundle> lazyActivationBundles, List<Bundle> toRefresh) { 1023 ServiceReference<?> reference = context.getServiceReference(StartLevel.class.getName()); 1024 StartLevel startService = null; 1025 if (reference != null) 1026 startService = (StartLevel) context.getService(reference); 1027 try { 1028 for (int i = 0; i < initialBundles.length; i++) { 1029 Bundle osgiBundle = getBundleByLocation(initialBundles[i].locationString, curInitBundles); 1030 try { 1031 // don't need to install if it is already installed 1032 if (osgiBundle == null) { 1033 InputStream in = initialBundles[i].location.openStream(); 1034 try { 1035 osgiBundle = context.installBundle(initialBundles[i].locationString, in); 1036 } catch (BundleException e) { 1037 StatusException status = e instanceof StatusException ? (StatusException) e : null; 1038 if (status != null && status.getStatusCode() == StatusException.CODE_OK && status.getStatus() instanceof Bundle) { 1039 osgiBundle = (Bundle) status.getStatus(); 1040 } else 1041 throw e; 1042 } 1043 // only check for lazy activation header if this is a newly installed bundle and is not marked for persistent start 1044 if (!initialBundles[i].start && hasLazyActivationPolicy(osgiBundle)) 1045 lazyActivationBundles.add(osgiBundle); 1046 } 1047 // always set the startlevel incase it has changed (bug 111549) 1048 // this is a no-op if the level is the same as previous launch. 1049 if ((osgiBundle.getState() & Bundle.UNINSTALLED) == 0 && initialBundles[i].level >= 0 && startService != null) 1050 startService.setBundleStartLevel(osgiBundle, initialBundles[i].level); 1051 // if this bundle is supposed to be started then add it to the start list 1052 if (initialBundles[i].start) 1053 startBundles.add(osgiBundle); 1054 // include basic bundles in case they were not resolved before 1055 if ((osgiBundle.getState() & Bundle.INSTALLED) != 0) 1056 toRefresh.add(osgiBundle); 1057 } catch (BundleException e) { 1058 FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FAILED_INSTALL, initialBundles[i].location), 0, e, null); 1059 log.log(entry); 1060 } catch (IOException e) { 1061 FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FAILED_INSTALL, initialBundles[i].location), 0, e, null); 1062 log.log(entry); 1063 } 1064 } 1065 } finally { 1066 if (reference != null) 1067 context.ungetService(reference); 1068 } 1069 } 1070 1071 @SuppressWarnings("deprecation") 1072 private static boolean hasLazyActivationPolicy(Bundle target) { 1073 // check the bundle manifest to see if it defines a lazy activation policy 1074 Dictionary<String, String> headers = target.getHeaders(""); //$NON-NLS-1$ 1075 // first check to see if this is a fragment bundle 1076 String fragmentHost = headers.get(Constants.FRAGMENT_HOST); 1077 if (fragmentHost != null) 1078 return false; // do not activate fragment bundles 1079 // look for the OSGi defined Bundle-ActivationPolicy header 1080 String activationPolicy = headers.get(Constants.BUNDLE_ACTIVATIONPOLICY); 1081 try { 1082 if (activationPolicy != null) { 1083 ManifestElement[] elements = ManifestElement.parseHeader(Constants.BUNDLE_ACTIVATIONPOLICY, activationPolicy); 1084 if (elements != null && elements.length > 0) { 1085 // if the value is "lazy" then it has a lazy activation poliyc 1086 if (Constants.ACTIVATION_LAZY.equals(elements[0].getValue())) 1087 return true; 1088 } 1089 } else { 1090 // check for Eclipse specific lazy start headers "Eclipse-LazyStart" and "Eclipse-AutoStart" 1091 String eclipseLazyStart = headers.get(Constants.ECLIPSE_LAZYSTART); 1092 if (eclipseLazyStart == null) 1093 eclipseLazyStart = headers.get(Constants.ECLIPSE_AUTOSTART); 1094 ManifestElement[] elements = ManifestElement.parseHeader(Constants.ECLIPSE_LAZYSTART, eclipseLazyStart); 1095 if (elements != null && elements.length > 0) { 1096 // if the value is true then it is lazy activated 1097 if ("true".equals(elements[0].getValue())) //$NON-NLS-1$ 1098 return true; 1099 // otherwise it is only lazy activated if it defines an exceptions directive. 1100 else if (elements[0].getDirective("exceptions") != null) //$NON-NLS-1$ 1101 return true; 1102 } 1103 } 1104 } catch (BundleException be) { 1105 // ignore this 1106 } 1107 return false; 1108 } 1109 1110 private static void startBundles(Bundle[] startBundles, Bundle[] lazyBundles) { 1111 for (int i = 0; i < startBundles.length; i++) 1112 startBundle(startBundles[i], 0); 1113 for (int i = 0; i < lazyBundles.length; i++) 1114 startBundle(lazyBundles[i], Bundle.START_ACTIVATION_POLICY); 1115 } 1116 1117 private static void startBundle(Bundle bundle, int options) { 1118 try { 1119 bundle.start(options); 1120 } catch (BundleException e) { 1121 if ((bundle.getState() & Bundle.RESOLVED) != 0) { 1122 // only log errors if the bundle is resolved 1123 FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FAILED_START, bundle.getLocation()), 0, e, null); 1124 log.log(entry); 1125 } 1126 } 1127 } 1128 1129 private static void loadConfigurationInfo() { 1130 Location configArea = LocationManager.getConfigurationLocation(); 1131 if (configArea == null) 1132 return; 1133 1134 URL location = null; 1135 try { 1136 location = new URL(configArea.getURL().toExternalForm() + LocationManager.CONFIG_FILE); 1137 } catch (MalformedURLException e) { 1138 // its ok. This should never happen 1139 } 1140 mergeProperties(FrameworkProperties.getProperties(), loadProperties(location)); 1141 } 1142 1143 private static Properties loadProperties(URL location) { 1144 Properties result = new Properties(); 1145 if (location == null) 1146 return result; 1147 try { 1148 InputStream in = location.openStream(); 1149 try { 1150 result.load(in); 1151 } finally { 1152 in.close(); 1153 } 1154 } catch (IOException e) { 1155 // its ok if there is no file. We'll just use the defaults for everything 1156 // TODO but it might be nice to log something with gentle wording (i.e., it is not an error) 1157 } 1158 return substituteVars(result); 1159 } 1160 1161 private static Properties substituteVars(Properties result) { 1162 if (result == null) { 1163 //nothing todo. 1164 return null; 1165 } 1166 for (Enumeration<Object> eKeys = result.keys(); eKeys.hasMoreElements();) { 1167 Object key = eKeys.nextElement(); 1168 if (key instanceof String) { 1169 String value = result.getProperty((String) key); 1170 if (value != null) 1171 result.put(key, BaseStorageHook.substituteVars(value)); 1172 } 1173 } 1174 return result; 1175 } 1176 1177 /** 1178 * Returns a URL which is equivalent to the given URL relative to the 1179 * specified base URL. Works only for file: URLs 1180 * @throws MalformedURLException 1181 */ 1182 private static URL makeRelative(URL base, URL location) throws MalformedURLException { 1183 if (base == null) 1184 return location; 1185 if (!"file".equals(base.getProtocol())) //$NON-NLS-1$ 1186 return location; 1187 if (!location.getProtocol().equals(REFERENCE_PROTOCOL)) 1188 return location; // we can only make reference urls relative 1189 URL nonReferenceLocation = new URL(location.getPath()); 1190 // if some URL component does not match, return the original location 1191 if (!base.getProtocol().equals(nonReferenceLocation.getProtocol())) 1192 return location; 1193 File locationPath = new File(nonReferenceLocation.getPath()); 1194 // if location is not absolute, return original location 1195 if (!locationPath.isAbsolute()) 1196 return location; 1197 File relativePath = makeRelative(new File(base.getPath()), locationPath); 1198 String urlPath = relativePath.getPath(); 1199 if (File.separatorChar != '/') 1200 urlPath = urlPath.replace(File.separatorChar, '/'); 1201 if (nonReferenceLocation.getPath().endsWith("/")) //$NON-NLS-1$ 1202 // restore original trailing slash 1203 urlPath += '/'; 1204 // couldn't use File to create URL here because it prepends the path with user.dir 1205 URL relativeURL = new URL(base.getProtocol(), base.getHost(), base.getPort(), urlPath); 1206 // now make it back to a reference URL 1207 relativeURL = new URL(REFERENCE_SCHEME + relativeURL.toExternalForm()); 1208 return relativeURL; 1209 } 1210 1211 private static File makeRelative(File base, File location) { 1212 if (!location.isAbsolute()) 1213 return location; 1214 File relative = new File(new FilePath(base).makeRelative(new FilePath(location))); 1215 return relative; 1216 } 1217 1218 private static void mergeProperties(Properties destination, Properties source) { 1219 for (Enumeration<?> e = source.keys(); e.hasMoreElements();) { 1220 String key = (String) e.nextElement(); 1221 String value = source.getProperty(key); 1222 if (destination.getProperty(key) == null) 1223 destination.setProperty(key, value); 1224 } 1225 } 1226 1227 private static void setStartLevel(final int value) { 1228 ServiceReference<?> reference = context.getServiceReference(StartLevel.class.getName()); 1229 final StartLevel startLevel = reference != null ? (StartLevel) context.getService(reference) : null; 1230 if (startLevel == null) 1231 return; 1232 final Semaphore semaphore = new Semaphore(0); 1233 StartupEventListener listener = new StartupEventListener(semaphore, FrameworkEvent.STARTLEVEL_CHANGED); 1234 context.addFrameworkListener(listener); 1235 context.addBundleListener(listener); 1236 startLevel.setStartLevel(value); 1237 context.ungetService(reference); 1238 updateSplash(semaphore, listener); 1239 } 1240 1241 static class StartupEventListener implements SynchronousBundleListener, FrameworkListener { 1242 private final Semaphore semaphore; 1243 private final int frameworkEventType; 1244 1245 public StartupEventListener(Semaphore semaphore, int frameworkEventType) { 1246 this.semaphore = semaphore; 1247 this.frameworkEventType = frameworkEventType; 1248 } 1249 1250 public void bundleChanged(BundleEvent event) { 1251 if (event.getBundle().getBundleId() == 0 && event.getType() == BundleEvent.STOPPING) 1252 semaphore.release(); 1253 } 1254 1255 public void frameworkEvent(FrameworkEvent event) { 1256 if (event.getType() == frameworkEventType) 1257 semaphore.release(); 1258 } 1259 1260 } 1261 1262 private static void updateSplash(Semaphore semaphore, StartupEventListener listener) { 1263 ServiceTracker<StartupMonitor, StartupMonitor> monitorTracker = new ServiceTracker<StartupMonitor, StartupMonitor>(context, StartupMonitor.class.getName(), null); 1264 monitorTracker.open(); 1265 try { 1266 while (true) { 1267 StartupMonitor monitor = monitorTracker.getService(); 1268 if (monitor != null) { 1269 try { 1270 monitor.update(); 1271 } catch (Throwable e) { 1272 // ignore exceptions thrown by the monitor 1273 } 1274 } 1275 // can we acquire the semaphore yet? 1276 if (semaphore.acquire(50)) 1277 break; //done 1278 //else still working, spin another update 1279 } 1280 } finally { 1281 if (listener != null) { 1282 context.removeFrameworkListener(listener); 1283 context.removeBundleListener(listener); 1284 } 1285 monitorTracker.close(); 1286 } 1287 } 1288 1289 /** 1290 * Searches for the given target directory immediately under 1291 * the given start location. If one is found then this location is returned; 1292 * otherwise an exception is thrown. 1293 * 1294 * @return the location where target directory was found 1295 * @param start the location to begin searching 1296 */ 1297 private static String searchFor(final String target, String start) { 1298 String[] candidates = searchCandidates.get(start); 1299 if (candidates == null) { 1300 File startFile = new File(start); 1301 // Pre-check if file exists, if not, and it contains escape characters, 1302 // try decoding the path 1303 if (!startFile.exists() && start.indexOf('%') >= 0) { 1304 String decodePath = FrameworkProperties.decode(start); 1305 File f = new File(decodePath); 1306 if (f.exists()) 1307 startFile = f; 1308 } 1309 candidates = startFile.list(); 1310 if (candidates != null) 1311 searchCandidates.put(start, candidates); 1312 } 1313 if (candidates == null) 1314 return null; 1315 String result = null; 1316 Object[] maxVersion = null; 1317 boolean resultIsFile = false; 1318 for (int i = 0; i < candidates.length; i++) { 1319 String candidateName = candidates[i]; 1320 if (!candidateName.startsWith(target)) 1321 continue; 1322 boolean simpleJar = false; 1323 final char versionSep = candidateName.length() > target.length() ? candidateName.charAt(target.length()) : 0; 1324 if (candidateName.length() > target.length() && versionSep != '_' && versionSep != '-') { 1325 // make sure this is not just a jar with no (_|-)version tacked on the end 1326 if (candidateName.length() == 4 + target.length() && candidateName.endsWith(".jar")) //$NON-NLS-1$ 1327 simpleJar = true; 1328 else 1329 // name does not match the target properly with an (_|-) version at the end 1330 continue; 1331 } 1332 // Note: directory with version suffix is always > than directory without version suffix 1333 String version = candidateName.length() > target.length() + 1 && (versionSep == '_' || versionSep == '-') ? candidateName.substring(target.length() + 1) : ""; //$NON-NLS-1$ 1334 Object[] currentVersion = getVersionElements(version); 1335 if (currentVersion != null && compareVersion(maxVersion, currentVersion) < 0) { 1336 File candidate = new File(start, candidateName); 1337 boolean candidateIsFile = candidate.isFile(); 1338 // if simple jar; make sure it is really a file before accepting it 1339 if (!simpleJar || candidateIsFile) { 1340 result = candidate.getAbsolutePath(); 1341 resultIsFile = candidateIsFile; 1342 maxVersion = currentVersion; 1343 } 1344 } 1345 } 1346 if (result == null) 1347 return null; 1348 return result.replace(File.separatorChar, '/') + (resultIsFile ? "" : "/"); //$NON-NLS-1$ //$NON-NLS-2$ 1349 } 1350 1351 /** 1352 * Do a quick parse of version identifier so its elements can be correctly compared. 1353 * If we are unable to parse the full version, remaining elements are initialized 1354 * with suitable defaults. 1355 * @return an array of size 4; first three elements are of type Integer (representing 1356 * major, minor and service) and the fourth element is of type String (representing 1357 * qualifier). A value of null is returned if there are no valid Integers. Note, that 1358 * returning anything else will cause exceptions in the caller. 1359 */ 1360 private static Object[] getVersionElements(String version) { 1361 Object[] result = {new Integer(-1), new Integer(-1), new Integer(-1), ""}; //$NON-NLS-1$ 1362 StringTokenizer t = new StringTokenizer(version, "."); //$NON-NLS-1$ 1363 String token; 1364 for (int i = 0; t.hasMoreTokens() && i < 4; i++) { 1365 token = t.nextToken(); 1366 if (i < 3) { 1367 // major, minor or service ... numeric values 1368 try { 1369 result[i] = new Integer(token); 1370 } catch (Exception e) { 1371 if (i == 0) 1372 return null; // return null if no valid numbers are present 1373 // invalid number format - use default numbers (-1) for the rest 1374 break; 1375 } 1376 } else { 1377 // qualifier ... string value 1378 result[i] = token; 1379 } 1380 } 1381 return result; 1382 } 1383 1384 /** 1385 * Compares version strings. 1386 * @return result of comparison, as integer; 1387 * <code><0</code> if left < right; 1388 * <code>0</code> if left == right; 1389 * <code>>0</code> if left > right; 1390 */ 1391 private static int compareVersion(Object[] left, Object[] right) { 1392 if (left == null) 1393 return -1; 1394 int result = ((Integer) left[0]).compareTo((Integer) right[0]); // compare major 1395 if (result != 0) 1396 return result; 1397 1398 result = ((Integer) left[1]).compareTo((Integer) right[1]); // compare minor 1399 if (result != 0) 1400 return result; 1401 1402 result = ((Integer) left[2]).compareTo((Integer) right[2]); // compare service 1403 if (result != 0) 1404 return result; 1405 1406 return ((String) left[3]).compareTo((String) right[3]); // compare qualifier 1407 } 1408 1409 private static void finalizeProperties() { 1410 // if check config is unknown and we are in dev mode, 1411 if (FrameworkProperties.getProperty(PROP_DEV) != null && FrameworkProperties.getProperty(PROP_CHECK_CONFIG) == null) 1412 FrameworkProperties.setProperty(PROP_CHECK_CONFIG, "true"); //$NON-NLS-1$ 1413 } 1414 1415 private static class InitialBundle { 1416 public final String locationString; 1417 public final URL location; 1418 public final int level; 1419 public final boolean start; 1420 1421 InitialBundle(String locationString, URL location, int level, boolean start) { 1422 this.locationString = locationString; 1423 this.location = location; 1424 this.level = level; 1425 this.start = start; 1426 } 1427 } 1428 1429 /** 1430 * Sets the initial properties for the platform. 1431 * This method must be called before calling the {@link #run(String[], Runnable)} or 1432 * {@link #startup(String[], Runnable)} methods for the properties to be used in 1433 * a launched instance of the platform. 1434 * <p> 1435 * If the specified properties contains a null value then the key for that value 1436 * will be cleared from the properties of the platform. 1437 * </p> 1438 * @param initialProperties the initial properties to set for the platform. 1439 * @since 3.2 1440 */ 1441 public static void setInitialProperties(Map<String, String> initialProperties) { 1442 if (initialProperties == null || initialProperties.isEmpty()) 1443 return; 1444 for (Map.Entry<String, String> entry : initialProperties.entrySet()) { 1445 if (entry.getValue() != null) 1446 FrameworkProperties.setProperty(entry.getKey(), entry.getValue()); 1447 else 1448 FrameworkProperties.clearProperty(entry.getKey()); 1449 } 1450 } 1451 1452 /** 1453 * Returns the context of the system bundle. A value of 1454 * <code>null</code> is returned if the platform is not running. 1455 * @return the context of the system bundle 1456 * @throws java.lang.SecurityException If the caller does not have the 1457 * appropriate <code>AdminPermission[system.bundle,CONTEXT]</code>, and 1458 * the Java Runtime Environment supports permissions. 1459 */ 1460 public static BundleContext getSystemBundleContext() { 1461 if (context == null || !running) 1462 return null; 1463 return context.getBundle().getBundleContext(); 1464 } 1465 1466 private static boolean isForcedRestart() { 1467 return Boolean.valueOf(FrameworkProperties.getProperty(PROP_FORCED_RESTART)).booleanValue(); 1468 } 1469 1470 /* 1471 * NOTE: This is an internal/experimental method used by launchers that need to react when the framework 1472 * is shutdown internally. 1473 * 1474 * Adds a framework shutdown handler. <p> 1475 * A handler implements the {@link Runnable} interface. When the framework is shutdown 1476 * the {@link Runnable#run()} method is called for each registered handler. Handlers should 1477 * make no assumptions on the thread it is being called from. If a handler object is 1478 * registered multiple times it will be called once for each registration. 1479 * <p> 1480 * At the time a handler is called the framework is shutdown. Handlers must not depend on 1481 * a running framework to execute or attempt to load additional classes from bundles 1482 * installed in the framework. 1483 * @param handler the framework shutdown handler 1484 * @throws IllegalStateException if the platform is already running 1485 */ 1486 static void internalAddFrameworkShutdownHandler(Runnable handler) { 1487 if (running) 1488 throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_ALREADY_RUNNING); 1489 1490 if (shutdownHandlers == null) 1491 shutdownHandlers = new ArrayList<Runnable>(); 1492 1493 shutdownHandlers.add(handler); 1494 } 1495 1496 /* 1497 * NOTE: This is an internal/experimental method used by launchers that need to react when the framework 1498 * is shutdown internally. 1499 * 1500 * Removes a framework shutdown handler. <p> 1501 * @param handler the framework shutdown handler 1502 * @throws IllegalStateException if the platform is already running 1503 */ 1504 static void internalRemoveFrameworkShutdownHandler(Runnable handler) { 1505 if (running) 1506 throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_ALREADY_RUNNING); 1507 1508 if (shutdownHandlers != null) 1509 shutdownHandlers.remove(handler); 1510 } 1511 1512 private static void registerFrameworkShutdownHandlers() { 1513 if (shutdownHandlers == null) 1514 return; 1515 1516 final Bundle systemBundle = context.getBundle(); 1517 for (Iterator<Runnable> it = shutdownHandlers.iterator(); it.hasNext();) { 1518 final Runnable handler = it.next(); 1519 BundleListener listener = new BundleListener() { 1520 public void bundleChanged(BundleEvent event) { 1521 if (event.getBundle() == systemBundle && event.getType() == BundleEvent.STOPPED) { 1522 handler.run(); 1523 } 1524 } 1525 }; 1526 context.addBundleListener(listener); 1527 } 1528 } 1529 }
相关文章: