Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2012-06-13 14:04:56 +0000
committerThomas Watson2012-07-16 21:03:11 +0000
commit7e980cfb94c1eb742bf1c9986055e70373f76392 (patch)
treef652dd6795661a52dc837fe11cc2db363cc35903 /bundles/org.eclipse.osgi/container/src/org/eclipse/core
parentf843e83246bb55c8e035fb0fa3e5515516070cd2 (diff)
downloadrt.equinox.framework-7e980cfb94c1eb742bf1c9986055e70373f76392.tar.gz
rt.equinox.framework-7e980cfb94c1eb742bf1c9986055e70373f76392.tar.xz
rt.equinox.framework-7e980cfb94c1eb742bf1c9986055e70373f76392.zip
Consolidate the source folders
Diffstat (limited to 'bundles/org.eclipse.osgi/container/src/org/eclipse/core')
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/EclipseStarter.java1529
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/LocationManager.java415
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/package.html17
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/BundleLocalizationImpl.java35
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/CachedManifest.java156
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ClasspathManifest.java87
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ContextFinder.java175
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/DefaultStartupMonitor.java68
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseAdaptorHook.java231
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseAppLauncher.java159
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseClassLoadingHook.java243
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseEnvironmentInfo.java243
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseErrorHandler.java113
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLazyStarter.java281
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogFactory.java107
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogHook.java147
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogWriter.java726
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseStorageHook.java522
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/IModel.java92
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/IPluginInfo.java52
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/MessageHelper.java66
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/PluginConverterImpl.java761
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/PluginParser.java710
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/Semaphore.java72
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/URLConverterImpl.java55
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/BundleStats.java132
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ClassStats.java121
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ClassloaderStats.java242
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ResourceBundleStats.java123
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/StatsManager.java237
30 files changed, 7917 insertions, 0 deletions
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/EclipseStarter.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/EclipseStarter.java
new file mode 100644
index 000000000..5783d5a9e
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/EclipseStarter.java
@@ -0,0 +1,1529 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Alex Blewitt (bug 172969)
+ *******************************************************************************/
+package org.eclipse.core.runtime.adaptor;
+
+import java.io.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.*;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
+import java.util.*;
+import org.eclipse.core.runtime.internal.adaptor.*;
+import org.eclipse.osgi.framework.adaptor.*;
+import org.eclipse.osgi.framework.internal.core.*;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.internal.baseadaptor.BaseStorageHook;
+import org.eclipse.osgi.internal.profile.Profile;
+import org.eclipse.osgi.service.datalocation.Location;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.service.runnable.ApplicationLauncher;
+import org.eclipse.osgi.service.runnable.StartupMonitor;
+import org.eclipse.osgi.util.ManifestElement;
+import org.eclipse.osgi.util.NLS;
+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 Eclipse 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
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class EclipseStarter {
+ private static FrameworkAdaptor adaptor;
+ private static BundleContext context;
+ private static boolean initialize = false;
+ public static boolean debug = false;
+ private static boolean running = false;
+ private static Framework framework = null;
+ private static ServiceRegistration<?> defaultMonitorRegistration = null;
+ private static ServiceRegistration<?> appLauncherRegistration = null;
+ private static ServiceRegistration<?> splashStreamRegistration = null;
+
+ // 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 NL_EXTENSIONS = "-nlExtensions"; //$NON-NLS-1$
+ private static final String CONFIGURATION = "-configuration"; //$NON-NLS-1$
+ private static final String USER = "-user"; //$NON-NLS-1$
+ private static final String NOEXIT = "-noExit"; //$NON-NLS-1$
+ private static final String LAUNCHER = "-launcher"; //$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$ //The start level used to install the bundles
+ public static final String PROP_EXTENSIONS = "osgi.framework.extensions"; //$NON-NLS-1$
+ public static final String PROP_INITIAL_STARTLEVEL = "osgi.startLevel"; //$NON-NLS-1$ //The start level when the fwl start
+ 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$
+ private static final String PROP_NL_EXTENSIONS = "osgi.nl.extensions"; //$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_FRAMEWORK = "osgi.framework"; //$NON-NLS-1$
+ public static final String PROP_INSTALL_AREA = "osgi.install.area"; //$NON-NLS-1$
+ public static final String PROP_FRAMEWORK_SHAPE = "osgi.framework.shape"; //$NON-NLS-1$ //the shape of the fwk (jar, or folder)
+ public static final String PROP_NOSHUTDOWN = "osgi.noShutdown"; //$NON-NLS-1$
+ private static final String PROP_FORCED_RESTART = "osgi.forcedRestart"; //$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$
+ public static final String PROP_IGNOREAPP = "eclipse.ignoreApp"; //$NON-NLS-1$
+ public static final String PROP_REFRESH_BUNDLES = "eclipse.refreshBundles"; //$NON-NLS-1$
+ private static final String PROP_ALLOW_APPRELAUNCH = "eclipse.allowAppRelaunch"; //$NON-NLS-1$
+ private static final String PROP_APPLICATION_LAUNCHDEFAULT = "eclipse.application.launchDefault"; //$NON-NLS-1$
+
+ private static final String FILE_SCHEME = "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.osgi.baseadaptor.BaseAdaptor"; //$NON-NLS-1$
+
+ private static final int DEFAULT_INITIAL_STARTLEVEL = 6; // default value for legacy purposes
+ private static final String DEFAULT_BUNDLES_STARTLEVEL = "4"; //$NON-NLS-1$
+
+ private static FrameworkLog log;
+ // directory of serch candidates keyed by directory abs path -> directory listing (bug 122024)
+ private static Map<String, String[]> searchCandidates = new HashMap<String, String[]>(4);
+ private static EclipseAppLauncher appLauncher;
+ private static List<Runnable> shutdownHandlers;
+
+ private static ConsoleManager consoleMgr = null;
+
+ /**
+ * This is the main to start osgi.
+ * It only works when the framework is being jared as a single jar
+ */
+ public static void main(String[] args) throws Exception {
+ if (FrameworkProperties.getProperty("eclipse.startTime") == null) //$NON-NLS-1$
+ FrameworkProperties.setProperty("eclipse.startTime", Long.toString(System.currentTimeMillis())); //$NON-NLS-1$
+ if (FrameworkProperties.getProperty(PROP_NOSHUTDOWN) == null)
+ FrameworkProperties.setProperty(PROP_NOSHUTDOWN, "true"); //$NON-NLS-1$
+ // set the compatibility boot delegation flag to false to get "standard" OSGi behavior WRT boot delegation (bug 178477)
+ if (FrameworkProperties.getProperty(Constants.OSGI_COMPATIBILITY_BOOTDELEGATION) == null)
+ FrameworkProperties.setProperty(Constants.OSGI_COMPATIBILITY_BOOTDELEGATION, "false"); //$NON-NLS-1$
+ Object result = run(args, null);
+ if (result instanceof Integer && !Boolean.valueOf(FrameworkProperties.getProperty(PROP_NOSHUTDOWN)).booleanValue())
+ System.exit(((Integer) result).intValue());
+ }
+
+ /**
+ * Launches the platform and runs a single application. The application is either identified
+ * in the given arguments (e.g., -application &lt;app id&gt;) 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 (Profile.PROFILE && Profile.STARTUP)
+ Profile.logEnter("EclipseStarter.run()", null); //$NON-NLS-1$
+ if (running)
+ throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_ALREADY_RUNNING);
+ boolean startupFailed = true;
+ try {
+ startup(args, endSplashHandler);
+ startupFailed = false;
+ if (Boolean.valueOf(FrameworkProperties.getProperty(PROP_IGNOREAPP)).booleanValue() || isForcedRestart())
+ return null;
+ 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, FrameworkLogEntry.ERROR, 0, startupFailed ? EclipseAdaptorMsg.ECLIPSE_STARTUP_STARTUP_ERROR : EclipseAdaptorMsg.ECLIPSE_STARTUP_APP_ERROR, 1, e, null);
+ if (log != null)
+ log.log(logEntry);
+ else
+ // TODO desperate measure - ideally, we should write this to disk (a la Main.log)
+ e.printStackTrace();
+ } finally {
+ try {
+ // The application typically sets the exit code however the framework can request that
+ // it be re-started. We need to check for this and potentially override the exit code.
+ if (isForcedRestart())
+ FrameworkProperties.setProperty(PROP_EXITCODE, "23"); //$NON-NLS-1$
+ if (!Boolean.valueOf(FrameworkProperties.getProperty(PROP_NOSHUTDOWN)).booleanValue())
+ shutdown();
+ } catch (Throwable e) {
+ FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, EclipseAdaptorMsg.ECLIPSE_STARTUP_SHUTDOWN_ERROR, 1, e, null);
+ if (log != null)
+ log.log(logEntry);
+ else
+ // TODO desperate measure - ideally, we should write this to disk (a la Main.log)
+ e.printStackTrace();
+ }
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logExit("EclipseStarter.run()"); //$NON-NLS-1$
+ if (Profile.PROFILE) {
+ String report = Profile.getProfileLog();
+ // avoiding writing to the console if there is nothing to print
+ if (report != null && report.length() > 0)
+ System.out.println(report);
+ }
+ }
+ // we only get here if an error happened
+ if (FrameworkProperties.getProperty(PROP_EXITCODE) == null) {
+ FrameworkProperties.setProperty(PROP_EXITCODE, "13"); //$NON-NLS-1$
+ FrameworkProperties.setProperty(PROP_EXITDATA, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_CHECK_LOG, log == null ? null : log.getFile().getPath()));
+ }
+ 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;
+ }
+
+ /**
+ * Starts the platform and sets it up to run a single application. The application is either identified
+ * in the given arguments (e.g., -application &lt;app id&gt;) 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
+ * @return BundleContext the context of the system bundle
+ * @throws Exception if anything goes wrong
+ */
+ public static BundleContext startup(String[] args, Runnable endSplashHandler) throws Exception {
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logEnter("EclipseStarter.startup()", null); //$NON-NLS-1$
+ if (running)
+ throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_ALREADY_RUNNING);
+ FrameworkProperties.initializeProperties();
+ processCommandLine(args);
+ LocationManager.initializeLocations();
+ loadConfigurationInfo();
+ finalizeProperties();
+ if (Profile.PROFILE)
+ Profile.initProps(); // catch any Profile properties set in eclipse.properties...
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logTime("EclipseStarter.startup()", "props inited"); //$NON-NLS-1$ //$NON-NLS-2$
+ adaptor = createAdaptor();
+ log = adaptor.getFrameworkLog();
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logTime("EclipseStarter.startup()", "adapter created"); //$NON-NLS-1$ //$NON-NLS-2$
+ framework = new Framework(adaptor);
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logTime("EclipseStarter.startup()", "OSGi created"); //$NON-NLS-1$ //$NON-NLS-2$
+ context = framework.getBundle(0).getBundleContext();
+ registerFrameworkShutdownHandlers();
+ publishSplashScreen(endSplashHandler);
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logTime("EclipseStarter.startup()", "osgi launched"); //$NON-NLS-1$ //$NON-NLS-2$
+ consoleMgr = ConsoleManager.startConsole(framework);
+ if (Profile.PROFILE && Profile.STARTUP) {
+ Profile.logTime("EclipseStarter.startup()", "console started"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ framework.launch();
+ // save the cached timestamp before loading basic bundles; this is needed so we can do a proper timestamp check when logging resolver errors
+ long stateStamp = adaptor.getState().getTimeStamp();
+ Bundle[] startBundles = loadBasicBundles();
+
+ if (startBundles == null || ("true".equals(FrameworkProperties.getProperty(PROP_REFRESH_BUNDLES)) && refreshPackages(getCurrentBundles(false)))) { //$NON-NLS-1$
+ waitForShutdown();
+ return context; // cannot continue; loadBasicBundles caused refreshPackages to shutdown the framework
+ }
+
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logTime("EclipseStarter.startup()", "loading basic bundles"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // set the framework start level to the ultimate value. This will actually start things
+ // running if they are persistently active.
+ setStartLevel(getStartLevel());
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logTime("EclipseStarter.startup()", "StartLevel set"); //$NON-NLS-1$ //$NON-NLS-2$
+ // they should all be active by this time
+ ensureBundlesActive(startBundles);
+
+ // in the case where the built-in console is disabled we should try to start the console bundle
+ try {
+ consoleMgr.checkForConsoleBundle();
+ } catch (BundleException e) {
+ FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, e.getMessage(), 0, e, null);
+ log.log(entry);
+ }
+ if (debug || FrameworkProperties.getProperty(PROP_DEV) != null)
+ // only spend time showing unresolved bundles in dev/debug mode and the state has changed
+ if (stateStamp != adaptor.getState().getTimeStamp())
+ logUnresolvedBundles(context.getBundles());
+ running = true;
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logExit("EclipseStarter.startup()"); //$NON-NLS-1$
+ return context;
+ }
+
+ private static int getStartLevel() {
+ String level = FrameworkProperties.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 DEFAULT_INITIAL_STARTLEVEL;
+ }
+
+ /**
+ * Runs the application 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 (Profile.PROFILE && Profile.STARTUP)
+ Profile.logEnter("EclipseStarter.run(Object)()", null); //$NON-NLS-1$
+ if (!running)
+ throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_NOT_RUNNING);
+ // if we are just initializing, do not run the application just return.
+ if (initialize)
+ return new Integer(0);
+ try {
+ if (appLauncher == null) {
+ boolean launchDefault = Boolean.valueOf(FrameworkProperties.getProperty(PROP_APPLICATION_LAUNCHDEFAULT, "true")).booleanValue(); //$NON-NLS-1$
+ // create the ApplicationLauncher and register it as a service
+ appLauncher = new EclipseAppLauncher(context, Boolean.valueOf(FrameworkProperties.getProperty(PROP_ALLOW_APPRELAUNCH)).booleanValue(), launchDefault, log);
+ appLauncherRegistration = context.registerService(ApplicationLauncher.class.getName(), appLauncher, null);
+ // must start the launcher AFTER service restration because this method
+ // blocks and runs the application on the current thread. This method
+ // will return only after the application has stopped.
+ return appLauncher.start(argument);
+ }
+ return appLauncher.reStart(argument);
+ } catch (Exception e) {
+ if (log != null && context != null) // context can be null if OSGi failed to launch (bug 151413)
+ logUnresolvedBundles(context.getBundles());
+ throw e;
+ }
+ }
+
+ /**
+ * 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 || framework == null)
+ return;
+ if (appLauncherRegistration != null)
+ appLauncherRegistration.unregister();
+ if (splashStreamRegistration != null)
+ splashStreamRegistration.unregister();
+ if (defaultMonitorRegistration != null)
+ defaultMonitorRegistration.unregister();
+ if (appLauncher != null)
+ appLauncher.shutdown();
+ appLauncherRegistration = null;
+ appLauncher = null;
+ splashStreamRegistration = null;
+ defaultMonitorRegistration = null;
+ if (consoleMgr != null) {
+ consoleMgr.stopConsole();
+ consoleMgr = null;
+ }
+ framework.close();
+ framework = null;
+ context = null;
+ running = false;
+ }
+
+ private static void ensureBundlesActive(Bundle[] bundles) {
+ ServiceTracker<StartLevel, StartLevel> tracker = null;
+ try {
+ for (int i = 0; i < bundles.length; i++) {
+ if (bundles[i].getState() != Bundle.ACTIVE) {
+ if (bundles[i].getState() == Bundle.INSTALLED) {
+ // Log that the bundle is not resolved
+ 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));
+ continue;
+ }
+ // check that the startlevel allows the bundle to be active (111550)
+ if (tracker == null) {
+ tracker = new ServiceTracker<StartLevel, StartLevel>(context, StartLevel.class.getName(), null);
+ tracker.open();
+ }
+ StartLevel sl = tracker.getService();
+ if (sl != null && (sl.getBundleStartLevel(bundles[i]) <= sl.getStartLevel())) {
+ log.log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_ACTIVE, bundles[i]), 0, null, null));
+ }
+ }
+ }
+ } finally {
+ if (tracker != null)
+ tracker.close();
+ }
+ }
+
+ private static void logUnresolvedBundles(Bundle[] bundles) {
+ State state = adaptor.getState();
+ FrameworkLog logService = adaptor.getFrameworkLog();
+ StateHelper stateHelper = adaptor.getPlatformAdmin().getStateHelper();
+
+ // first lets look for missing leaf constraints (bug 114120)
+ VersionConstraint[] leafConstraints = stateHelper.getUnsatisfiedLeaves(state.getBundles());
+ // hash the missing leaf constraints by the declaring bundles
+ Map<BundleDescription, List<VersionConstraint>> missing = new HashMap<BundleDescription, List<VersionConstraint>>();
+ for (int i = 0; i < leafConstraints.length; i++) {
+ // only include non-optional and non-dynamic constraint leafs
+ if (leafConstraints[i] instanceof BundleSpecification && ((BundleSpecification) leafConstraints[i]).isOptional())
+ continue;
+ if (leafConstraints[i] instanceof ImportPackageSpecification) {
+ if (ImportPackageSpecification.RESOLUTION_OPTIONAL.equals(((ImportPackageSpecification) leafConstraints[i]).getDirective(Constants.RESOLUTION_DIRECTIVE)))
+ continue;
+ if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(((ImportPackageSpecification) leafConstraints[i]).getDirective(Constants.RESOLUTION_DIRECTIVE)))
+ continue;
+ }
+ BundleDescription bundle = leafConstraints[i].getBundle();
+ List<VersionConstraint> constraints = missing.get(bundle);
+ if (constraints == null) {
+ constraints = new ArrayList<VersionConstraint>();
+ missing.put(bundle, constraints);
+ }
+ constraints.add(leafConstraints[i]);
+ }
+
+ // found some bundles with missing leaf constraints; log them first
+ if (missing.size() > 0) {
+ FrameworkLogEntry[] rootChildren = new FrameworkLogEntry[missing.size()];
+ int rootIndex = 0;
+ for (Iterator<BundleDescription> iter = missing.keySet().iterator(); iter.hasNext(); rootIndex++) {
+ BundleDescription description = iter.next();
+ String symbolicName = description.getSymbolicName() == null ? FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME : description.getSymbolicName();
+ String generalMessage = NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_RESOLVED, description.getLocation());
+ List<VersionConstraint> constraints = missing.get(description);
+ FrameworkLogEntry[] logChildren = new FrameworkLogEntry[constraints.size()];
+ for (int i = 0; i < logChildren.length; i++)
+ logChildren[i] = new FrameworkLogEntry(symbolicName, FrameworkLogEntry.WARNING, 0, MessageHelper.getResolutionFailureMessage(constraints.get(i)), 0, null, null);
+ rootChildren[rootIndex] = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.WARNING, 0, generalMessage, 0, null, logChildren);
+ }
+ logService.log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.WARNING, 0, EclipseAdaptorMsg.ECLIPSE_STARTUP_ROOTS_NOT_RESOLVED, 0, null, rootChildren));
+ }
+
+ // There may be some bundles unresolved for other reasons, causing the system to be unresolved
+ // log all unresolved constraints now
+ List<FrameworkLogEntry> allChildren = new ArrayList<FrameworkLogEntry>();
+ for (int i = 0; i < bundles.length; i++)
+ if (bundles[i].getState() == Bundle.INSTALLED) {
+ String symbolicName = bundles[i].getSymbolicName() == null ? FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME : bundles[i].getSymbolicName();
+ String generalMessage = NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_BUNDLE_NOT_RESOLVED, bundles[i]);
+ 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(symbolicName, FrameworkLogEntry.WARNING, 0, MessageHelper.getResolutionFailureMessage(unsatisfied[j]), 0, null, null);
+ } else {
+ ResolverError[] resolverErrors = state.getResolverErrors(description);
+ if (resolverErrors.length > 0) {
+ logChildren = new FrameworkLogEntry[resolverErrors.length];
+ for (int j = 0; j < resolverErrors.length; j++)
+ logChildren[j] = new FrameworkLogEntry(symbolicName, FrameworkLogEntry.WARNING, 0, resolverErrors[j].toString(), 0, null, null);
+ }
+ }
+
+ allChildren.add(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.WARNING, 0, generalMessage, 0, null, logChildren));
+ }
+ if (allChildren.size() > 0)
+ logService.log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.WARNING, 0, EclipseAdaptorMsg.ECLIPSE_STARTUP_ALL_NOT_RESOLVED, 0, null, allChildren.toArray(new FrameworkLogEntry[allChildren.size()])));
+ }
+
+ private static void publishSplashScreen(final Runnable endSplashHandler) {
+ if (endSplashHandler == null)
+ return;
+ // register the output stream to the launcher if it exists
+ try {
+ Method method = endSplashHandler.getClass().getMethod("getOutputStream", new Class[0]); //$NON-NLS-1$
+ Object outputStream = method.invoke(endSplashHandler, new Object[0]);
+ if (outputStream instanceof OutputStream) {
+ Dictionary<String, Object> osProperties = new Hashtable<String, Object>();
+ osProperties.put("name", "splashstream"); //$NON-NLS-1$//$NON-NLS-2$
+ splashStreamRegistration = context.registerService(OutputStream.class.getName(), outputStream, osProperties);
+ }
+ } catch (Exception ex) {
+ // ignore
+ }
+ // keep this splash handler as the default startup monitor
+ try {
+ Dictionary<String, Object> monitorProps = new Hashtable<String, Object>();
+ monitorProps.put(Constants.SERVICE_RANKING, new Integer(Integer.MIN_VALUE));
+ defaultMonitorRegistration = context.registerService(StartupMonitor.class.getName(), new DefaultStartupMonitor(endSplashHandler), monitorProps);
+ } catch (IllegalStateException e) {
+ //splash handler did not provide the necessary methods, ignore it
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private static URL searchForBundle(String name, String parent) throws MalformedURLException {
+ URL url = null;
+ File fileLocation = null;
+ boolean reference = false;
+ try {
+ new URL(name); // quick check to see if the name is a valid URL
+ 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 that 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.
+ * Returns null if the framework has been shutdown as a result of refreshPackages
+ */
+ private static Bundle[] loadBasicBundles() {
+ long startTime = System.currentTimeMillis();
+ String osgiBundles = FrameworkProperties.getProperty(PROP_BUNDLES);
+ String osgiExtensions = FrameworkProperties.getProperty(PROP_EXTENSIONS);
+ if (osgiExtensions != null && osgiExtensions.length() > 0) {
+ osgiBundles = osgiExtensions + ',' + osgiBundles;
+ FrameworkProperties.setProperty(PROP_BUNDLES, osgiBundles);
+ }
+ String[] installEntries = getArrayFromList(osgiBundles, ","); //$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 = getCurrentBundles(true);
+
+ // list of bundles to be refreshed
+ List<Bundle> toRefresh = new ArrayList<Bundle>(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.
+ List<Bundle> startBundles = new ArrayList<Bundle>(installEntries.length);
+ List<Bundle> lazyActivationBundles = new ArrayList<Bundle>(installEntries.length);
+ installBundles(initialBundles, curInitBundles, startBundles, lazyActivationBundles, toRefresh);
+
+ // If we installed/uninstalled something, force a refresh of all installed/uninstalled bundles
+ if (!toRefresh.isEmpty() && refreshPackages(toRefresh.toArray(new Bundle[toRefresh.size()])))
+ return null; // cannot continue; refreshPackages shutdown the framework
+
+ // schedule all basic bundles to be started
+ Bundle[] startInitBundles = startBundles.toArray(new Bundle[startBundles.size()]);
+ Bundle[] lazyInitBundles = lazyActivationBundles.toArray(new Bundle[lazyActivationBundles.size()]);
+ startBundles(startInitBundles, lazyInitBundles);
+
+ if (debug)
+ System.out.println("Time to load bundles: " + (System.currentTimeMillis() - startTime)); //$NON-NLS-1$
+ return startInitBundles;
+ }
+
+ private static InitialBundle[] getInitialBundles(String[] installEntries) {
+ searchCandidates.clear();
+ List<InitialBundle> result = new ArrayList<InitialBundle>(installEntries.length);
+ int defaultStartLevel = Integer.parseInt(FrameworkProperties.getProperty(PROP_BUNDLES_STARTLEVEL, DEFAULT_BUNDLES_STARTLEVEL));
+ String syspath = getSysPath();
+ // should canonicalize the syspath.
+ try {
+ syspath = new File(syspath).getCanonicalPath();
+ } catch (IOException ioe) {
+ // do nothing
+ }
+ for (int i = 0; i < installEntries.length; i++) {
+ String name = installEntries[i];
+ int level = defaultStartLevel;
+ boolean start = false;
+ int index = name.lastIndexOf('@');
+ if (index >= 0) {
+ String[] attributes = getArrayFromList(name.substring(index + 1, name.length()), ":"); //$NON-NLS-1$
+ for (int j = 0; j < attributes.length; j++) {
+ String attribute = attributes[j];
+ if (attribute.equals("start")) //$NON-NLS-1$
+ start = true;
+ else {
+ try {
+ level = Integer.parseInt(attribute);
+ } catch (NumberFormatException e) { // bug 188089
+ index = name.length();
+ continue;
+ }
+ }
+ }
+ name = name.substring(0, index);
+ }
+ try {
+ URL location = searchForBundle(name, syspath);
+ if (location == null) {
+ FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_BUNDLE_NOT_FOUND, installEntries[i]), 0, null, null);
+ log.log(entry);
+ // skip this entry
+ continue;
+ }
+ location = makeRelative(LocationManager.getInstallLocation().getURL(), location);
+ String locationString = INITIAL_LOCATION + location.toExternalForm();
+ result.add(new InitialBundle(locationString, location, level, start));
+ } catch (IOException e) {
+ log.log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, e.getMessage(), 0, e, null));
+ }
+ }
+ return result.toArray(new InitialBundle[result.size()]);
+ }
+
+ // returns true if the refreshPackages operation caused the framework to shutdown
+ private static boolean 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 false;
+ // 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);
+ StartupEventListener listener = new StartupEventListener(semaphore, FrameworkEvent.PACKAGES_REFRESHED);
+ context.addFrameworkListener(listener);
+ context.addBundleListener(listener);
+ packageAdmin.refreshPackages(bundles);
+ context.ungetService(packageAdminRef);
+ updateSplash(semaphore, listener);
+ if (isForcedRestart())
+ return true;
+ return false;
+ }
+
+ private static void waitForShutdown() {
+ if (!isForcedRestart())
+ return;
+ // wait for the system bundle to stop
+ Bundle systemBundle = framework.getBundle(0);
+ int i = 0;
+ while (i < 5000 && (systemBundle.getState() & (Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING)) != 0) {
+ i += 200;
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Creates and returns the adaptor
+ *
+ * @return a FrameworkAdaptor object
+ */
+ private static FrameworkAdaptor createAdaptor() throws Exception {
+ String adaptorClassName = FrameworkProperties.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 {
+ EclipseEnvironmentInfo.setAllArgs(args);
+ if (args.length == 0) {
+ EclipseEnvironmentInfo.setFrameworkArgs(args);
+ EclipseEnvironmentInfo.setAllArgs(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$
+ FrameworkProperties.setProperty(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$
+ FrameworkProperties.setProperty(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)) {
+ FrameworkProperties.setProperty(PROP_CLEAN, "true"); //$NON-NLS-1$
+ found = true;
+ }
+
+ // look for the consoleLog flag
+ if (args[i].equalsIgnoreCase(CONSOLE_LOG)) {
+ FrameworkProperties.setProperty(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$
+ FrameworkProperties.setProperty(PROP_CONSOLE, ""); //$NON-NLS-1$
+ found = true;
+ }
+
+ if (args[i].equalsIgnoreCase(NOEXIT)) {
+ FrameworkProperties.setProperty(PROP_NOSHUTDOWN, "true"); //$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)) {
+ FrameworkProperties.setProperty(PROP_CONSOLE, arg);
+ found = true;
+ }
+
+ // look for the configuration location .
+ if (args[i - 1].equalsIgnoreCase(CONFIGURATION)) {
+ FrameworkProperties.setProperty(LocationManager.PROP_CONFIG_AREA, arg);
+ found = true;
+ }
+
+ // look for the data location for this instance.
+ if (args[i - 1].equalsIgnoreCase(DATA)) {
+ FrameworkProperties.setProperty(LocationManager.PROP_INSTANCE_AREA, arg);
+ found = true;
+ }
+
+ // look for the user location for this instance.
+ if (args[i - 1].equalsIgnoreCase(USER)) {
+ FrameworkProperties.setProperty(LocationManager.PROP_USER_AREA, arg);
+ found = true;
+ }
+
+ // look for the launcher location
+ if (args[i - 1].equalsIgnoreCase(LAUNCHER)) {
+ FrameworkProperties.setProperty(LocationManager.PROP_LAUNCHER, arg);
+ found = true;
+ }
+ // look for the development mode and class path entries.
+ if (args[i - 1].equalsIgnoreCase(DEV)) {
+ FrameworkProperties.setProperty(PROP_DEV, arg);
+ found = true;
+ }
+
+ // look for the debug mode and option file location.
+ if (args[i - 1].equalsIgnoreCase(DEBUG)) {
+ FrameworkProperties.setProperty(PROP_DEBUG, arg);
+ debug = true;
+ found = true;
+ }
+
+ // look for the window system.
+ if (args[i - 1].equalsIgnoreCase(WS)) {
+ FrameworkProperties.setProperty(PROP_WS, arg);
+ found = true;
+ }
+
+ // look for the operating system
+ if (args[i - 1].equalsIgnoreCase(OS)) {
+ FrameworkProperties.setProperty(PROP_OS, arg);
+ found = true;
+ }
+
+ // look for the system architecture
+ if (args[i - 1].equalsIgnoreCase(ARCH)) {
+ FrameworkProperties.setProperty(PROP_ARCH, arg);
+ found = true;
+ }
+
+ // look for the nationality/language
+ if (args[i - 1].equalsIgnoreCase(NL)) {
+ FrameworkProperties.setProperty(PROP_NL, arg);
+ found = true;
+ }
+
+ // look for the locale extensions
+ if (args[i - 1].equalsIgnoreCase(NL_EXTENSIONS)) {
+ FrameworkProperties.setProperty(PROP_NL_EXTENSIONS, 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) {
+ EclipseEnvironmentInfo.setFrameworkArgs(new String[0]);
+ EclipseEnvironmentInfo.setAppArgs(args);
+ return args;
+ }
+ String[] appArgs = new String[args.length - configArgIndex];
+ String[] frameworkArgs = new String[configArgIndex];
+ configArgIndex = 0;
+ int j = 0;
+ int k = 0;
+ for (int i = 0; i < args.length; i++) {
+ if (i == configArgs[configArgIndex]) {
+ frameworkArgs[k++] = args[i];
+ configArgIndex++;
+ } else
+ appArgs[j++] = args[i];
+ }
+ EclipseEnvironmentInfo.setFrameworkArgs(frameworkArgs);
+ EclipseEnvironmentInfo.setAppArgs(appArgs);
+ return 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) {
+ return ManifestElement.getArrayFromList(prop, separator);
+ }
+
+ protected static String getSysPath() {
+ String result = FrameworkProperties.getProperty(PROP_SYSPATH);
+ if (result != null)
+ return result;
+ result = getSysPathFromURL(FrameworkProperties.getProperty(PROP_FRAMEWORK));
+ if (result == null)
+ result = getSysPathFromCodeSource();
+ if (result == null)
+ throw new IllegalStateException("Can not find the system path."); //$NON-NLS-1$
+ if (Character.isUpperCase(result.charAt(0))) {
+ char[] chars = result.toCharArray();
+ chars[0] = Character.toLowerCase(chars[0]);
+ result = new String(chars);
+ }
+ FrameworkProperties.setProperty(PROP_SYSPATH, result);
+ return result;
+ }
+
+ private static String getSysPathFromURL(String urlSpec) {
+ if (urlSpec == null)
+ return null;
+ URL url = LocationHelper.buildURL(urlSpec, false);
+ if (url == null)
+ return null;
+ File fwkFile = new File(url.getFile());
+ fwkFile = new File(fwkFile.getAbsolutePath());
+ fwkFile = new File(fwkFile.getParent());
+ return fwkFile.getAbsolutePath();
+ }
+
+ private static String getSysPathFromCodeSource() {
+ ProtectionDomain pd = EclipseStarter.class.getProtectionDomain();
+ if (pd == null)
+ return null;
+ CodeSource cs = pd.getCodeSource();
+ if (cs == null)
+ return null;
+ URL url = cs.getLocation();
+ if (url == null)
+ return null;
+ String result = url.getFile();
+ if (result.endsWith(".jar")) { //$NON-NLS-1$
+ result = result.substring(0, result.lastIndexOf('/'));
+ if ("folder".equals(FrameworkProperties.getProperty(PROP_FRAMEWORK_SHAPE))) //$NON-NLS-1$
+ result = result.substring(0, result.lastIndexOf('/'));
+ } else {
+ 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('/'));
+ }
+ return result;
+ }
+
+ private static Bundle[] getCurrentBundles(boolean includeInitial) {
+ Bundle[] installed = context.getBundles();
+ List<Bundle> initial = new ArrayList<Bundle>();
+ for (int i = 0; i < installed.length; i++) {
+ Bundle bundle = installed[i];
+ if (bundle.getLocation().startsWith(INITIAL_LOCATION)) {
+ if (includeInitial)
+ initial.add(bundle);
+ } else if (!includeInitial && bundle.getBundleId() != 0)
+ initial.add(bundle);
+ }
+ return 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<Bundle> 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, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FAILED_UNINSTALL, curInitBundles[i].getLocation()), 0, e, null);
+ log.log(entry);
+ }
+ }
+ }
+
+ private static void installBundles(InitialBundle[] initialBundles, Bundle[] curInitBundles, List<Bundle> startBundles, List<Bundle> lazyActivationBundles, List<Bundle> toRefresh) {
+ ServiceReference<?> reference = context.getServiceReference(StartLevel.class.getName());
+ StartLevel startService = null;
+ if (reference != null)
+ startService = (StartLevel) context.getService(reference);
+ try {
+ 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();
+ try {
+ osgiBundle = context.installBundle(initialBundles[i].locationString, in);
+ } catch (BundleException e) {
+ StatusException status = e instanceof StatusException ? (StatusException) e : null;
+ if (status != null && status.getStatusCode() == StatusException.CODE_OK && status.getStatus() instanceof Bundle) {
+ osgiBundle = (Bundle) status.getStatus();
+ } else
+ throw e;
+ }
+ // only check for lazy activation header if this is a newly installed bundle and is not marked for persistent start
+ if (!initialBundles[i].start && hasLazyActivationPolicy(osgiBundle))
+ lazyActivationBundles.add(osgiBundle);
+ }
+ // always set the startlevel incase it has changed (bug 111549)
+ // this is a no-op if the level is the same as previous launch.
+ if ((osgiBundle.getState() & Bundle.UNINSTALLED) == 0 && initialBundles[i].level >= 0 && startService != null)
+ startService.setBundleStartLevel(osgiBundle, initialBundles[i].level);
+ // if this bundle is supposed to be started then add it to the start list
+ 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, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FAILED_INSTALL, initialBundles[i].location), 0, e, null);
+ log.log(entry);
+ } catch (IOException e) {
+ FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FAILED_INSTALL, initialBundles[i].location), 0, e, null);
+ log.log(entry);
+ }
+ }
+ } finally {
+ if (reference != null)
+ context.ungetService(reference);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private static boolean hasLazyActivationPolicy(Bundle target) {
+ // check the bundle manifest to see if it defines a lazy activation policy
+ Dictionary<String, String> headers = target.getHeaders(""); //$NON-NLS-1$
+ // first check to see if this is a fragment bundle
+ String fragmentHost = headers.get(Constants.FRAGMENT_HOST);
+ if (fragmentHost != null)
+ return false; // do not activate fragment bundles
+ // look for the OSGi defined Bundle-ActivationPolicy header
+ String activationPolicy = headers.get(Constants.BUNDLE_ACTIVATIONPOLICY);
+ try {
+ if (activationPolicy != null) {
+ ManifestElement[] elements = ManifestElement.parseHeader(Constants.BUNDLE_ACTIVATIONPOLICY, activationPolicy);
+ if (elements != null && elements.length > 0) {
+ // if the value is "lazy" then it has a lazy activation poliyc
+ if (Constants.ACTIVATION_LAZY.equals(elements[0].getValue()))
+ return true;
+ }
+ } else {
+ // check for Eclipse specific lazy start headers "Eclipse-LazyStart" and "Eclipse-AutoStart"
+ String eclipseLazyStart = headers.get(Constants.ECLIPSE_LAZYSTART);
+ if (eclipseLazyStart == null)
+ eclipseLazyStart = headers.get(Constants.ECLIPSE_AUTOSTART);
+ ManifestElement[] elements = ManifestElement.parseHeader(Constants.ECLIPSE_LAZYSTART, eclipseLazyStart);
+ if (elements != null && elements.length > 0) {
+ // if the value is true then it is lazy activated
+ if ("true".equals(elements[0].getValue())) //$NON-NLS-1$
+ return true;
+ // otherwise it is only lazy activated if it defines an exceptions directive.
+ else if (elements[0].getDirective("exceptions") != null) //$NON-NLS-1$
+ return true;
+ }
+ }
+ } catch (BundleException be) {
+ // ignore this
+ }
+ return false;
+ }
+
+ private static void startBundles(Bundle[] startBundles, Bundle[] lazyBundles) {
+ for (int i = 0; i < startBundles.length; i++)
+ startBundle(startBundles[i], 0);
+ for (int i = 0; i < lazyBundles.length; i++)
+ startBundle(lazyBundles[i], Bundle.START_ACTIVATION_POLICY);
+ }
+
+ private static void startBundle(Bundle bundle, int options) {
+ try {
+ bundle.start(options);
+ } catch (BundleException e) {
+ if ((bundle.getState() & Bundle.RESOLVED) != 0) {
+ // only log errors if the bundle is resolved
+ FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FAILED_START, bundle.getLocation()), 0, e, null);
+ log.log(entry);
+ }
+ }
+ }
+
+ 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. This should never happen
+ }
+ mergeProperties(FrameworkProperties.getProperties(), loadProperties(location));
+ }
+
+ 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 substituteVars(result);
+ }
+
+ private static Properties substituteVars(Properties result) {
+ if (result == null) {
+ //nothing todo.
+ return null;
+ }
+ for (Enumeration<Object> eKeys = result.keys(); eKeys.hasMoreElements();) {
+ Object key = eKeys.nextElement();
+ if (key instanceof String) {
+ String value = result.getProperty((String) key);
+ if (value != null)
+ result.put(key, BaseStorageHook.substituteVars(value));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns a URL which is equivalent to the given URL relative to the
+ * specified base URL. Works only for file: URLs
+ * @throws MalformedURLException
+ */
+ private static URL makeRelative(URL base, URL location) throws MalformedURLException {
+ if (base == null)
+ return location;
+ if (!"file".equals(base.getProtocol())) //$NON-NLS-1$
+ return location;
+ if (!location.getProtocol().equals(REFERENCE_PROTOCOL))
+ return location; // we can only make reference urls relative
+ URL nonReferenceLocation = new URL(location.getPath());
+ // if some URL component does not match, return the original location
+ if (!base.getProtocol().equals(nonReferenceLocation.getProtocol()))
+ return location;
+ File locationPath = new File(nonReferenceLocation.getPath());
+ // if location is not absolute, return original location
+ if (!locationPath.isAbsolute())
+ return location;
+ File relativePath = makeRelative(new File(base.getPath()), locationPath);
+ String urlPath = relativePath.getPath();
+ if (File.separatorChar != '/')
+ urlPath = urlPath.replace(File.separatorChar, '/');
+ if (nonReferenceLocation.getPath().endsWith("/")) //$NON-NLS-1$
+ // restore original trailing slash
+ urlPath += '/';
+ // couldn't use File to create URL here because it prepends the path with user.dir
+ URL relativeURL = new URL(base.getProtocol(), base.getHost(), base.getPort(), urlPath);
+ // now make it back to a reference URL
+ relativeURL = new URL(REFERENCE_SCHEME + relativeURL.toExternalForm());
+ return relativeURL;
+ }
+
+ private static File makeRelative(File base, File location) {
+ if (!location.isAbsolute())
+ return location;
+ File relative = new File(new FilePath(base).makeRelative(new FilePath(location)));
+ return relative;
+ }
+
+ 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.setProperty(key, value);
+ }
+ }
+
+ private static void setStartLevel(final int value) {
+ ServiceReference<?> reference = context.getServiceReference(StartLevel.class.getName());
+ final StartLevel startLevel = reference != null ? (StartLevel) context.getService(reference) : null;
+ if (startLevel == null)
+ return;
+ final Semaphore semaphore = new Semaphore(0);
+ StartupEventListener listener = new StartupEventListener(semaphore, FrameworkEvent.STARTLEVEL_CHANGED);
+ context.addFrameworkListener(listener);
+ context.addBundleListener(listener);
+ startLevel.setStartLevel(value);
+ context.ungetService(reference);
+ updateSplash(semaphore, listener);
+ }
+
+ static class StartupEventListener implements SynchronousBundleListener, FrameworkListener {
+ private final Semaphore semaphore;
+ private final int frameworkEventType;
+
+ public StartupEventListener(Semaphore semaphore, int frameworkEventType) {
+ this.semaphore = semaphore;
+ this.frameworkEventType = frameworkEventType;
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ if (event.getBundle().getBundleId() == 0 && event.getType() == BundleEvent.STOPPING)
+ semaphore.release();
+ }
+
+ public void frameworkEvent(FrameworkEvent event) {
+ if (event.getType() == frameworkEventType)
+ semaphore.release();
+ }
+
+ }
+
+ private static void updateSplash(Semaphore semaphore, StartupEventListener listener) {
+ ServiceTracker<StartupMonitor, StartupMonitor> monitorTracker = new ServiceTracker<StartupMonitor, StartupMonitor>(context, StartupMonitor.class.getName(), null);
+ monitorTracker.open();
+ try {
+ while (true) {
+ StartupMonitor monitor = monitorTracker.getService();
+ if (monitor != null) {
+ try {
+ monitor.update();
+ } catch (Throwable e) {
+ // ignore exceptions thrown by the monitor
+ }
+ }
+ // can we acquire the semaphore yet?
+ if (semaphore.acquire(50))
+ break; //done
+ //else still working, spin another update
+ }
+ } finally {
+ if (listener != null) {
+ context.removeFrameworkListener(listener);
+ context.removeBundleListener(listener);
+ }
+ monitorTracker.close();
+ }
+ }
+
+ /**
+ * Searches for the given target directory immediately under
+ * the given start 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 = searchCandidates.get(start);
+ if (candidates == null) {
+ File startFile = new File(start);
+ // Pre-check if file exists, if not, and it contains escape characters,
+ // try decoding the path
+ if (!startFile.exists() && start.indexOf('%') >= 0) {
+ String decodePath = FrameworkProperties.decode(start);
+ File f = new File(decodePath);
+ if (f.exists())
+ startFile = f;
+ }
+ candidates = startFile.list();
+ if (candidates != null)
+ searchCandidates.put(start, candidates);
+ }
+ if (candidates == null)
+ return null;
+ String result = null;
+ Object[] maxVersion = null;
+ boolean resultIsFile = false;
+ for (int i = 0; i < candidates.length; i++) {
+ String candidateName = candidates[i];
+ if (!candidateName.startsWith(target))
+ continue;
+ boolean simpleJar = false;
+ final char versionSep = candidateName.length() > target.length() ? candidateName.charAt(target.length()) : 0;
+ if (candidateName.length() > target.length() && versionSep != '_' && versionSep != '-') {
+ // make sure this is not just a jar with no (_|-)version tacked on the end
+ if (candidateName.length() == 4 + target.length() && candidateName.endsWith(".jar")) //$NON-NLS-1$
+ simpleJar = true;
+ else
+ // name does not match the target properly with an (_|-) version at the end
+ continue;
+ }
+ // Note: directory with version suffix is always > than directory without version suffix
+ String version = candidateName.length() > target.length() + 1 && (versionSep == '_' || versionSep == '-') ? candidateName.substring(target.length() + 1) : ""; //$NON-NLS-1$
+ Object[] currentVersion = getVersionElements(version);
+ if (currentVersion != null && compareVersion(maxVersion, currentVersion) < 0) {
+ File candidate = new File(start, candidateName);
+ boolean candidateIsFile = candidate.isFile();
+ // if simple jar; make sure it is really a file before accepting it
+ if (!simpleJar || candidateIsFile) {
+ result = candidate.getAbsolutePath();
+ resultIsFile = candidateIsFile;
+ maxVersion = currentVersion;
+ }
+ }
+ }
+ if (result == null)
+ return null;
+ return result.replace(File.separatorChar, '/') + (resultIsFile ? "" : "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * 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). A value of null is returned if there are no valid Integers. Note, that
+ * returning anything else will cause exceptions in the caller.
+ */
+ private static Object[] getVersionElements(String version) {
+ Object[] result = {new Integer(-1), new Integer(-1), new Integer(-1), ""}; //$NON-NLS-1$
+ StringTokenizer t = new StringTokenizer(version, "."); //$NON-NLS-1$
+ String token;
+ for (int i = 0; t.hasMoreTokens() && i < 4; i++) {
+ token = t.nextToken();
+ if (i < 3) {
+ // major, minor or service ... numeric values
+ try {
+ result[i] = new Integer(token);
+ } catch (Exception e) {
+ if (i == 0)
+ return null; // return null if no valid numbers are present
+ // invalid number format - use default numbers (-1) 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) {
+ if (left == null)
+ return -1;
+ 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 void finalizeProperties() {
+ // if check config is unknown and we are in dev mode,
+ if (FrameworkProperties.getProperty(PROP_DEV) != null && FrameworkProperties.getProperty(PROP_CHECK_CONFIG) == null)
+ FrameworkProperties.setProperty(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;
+ }
+ }
+
+ /**
+ * Sets the initial properties for the platform.
+ * This method must be called before calling the {@link #run(String[], Runnable)} or
+ * {@link #startup(String[], Runnable)} methods for the properties to be used in
+ * a launched instance of the platform.
+ * <p>
+ * If the specified properties contains a null value then the key for that value
+ * will be cleared from the properties of the platform.
+ * </p>
+ * @param initialProperties the initial properties to set for the platform.
+ * @since 3.2
+ */
+ public static void setInitialProperties(Map<String, String> initialProperties) {
+ if (initialProperties == null || initialProperties.isEmpty())
+ return;
+ for (Map.Entry<String, String> entry : initialProperties.entrySet()) {
+ if (entry.getValue() != null)
+ FrameworkProperties.setProperty(entry.getKey(), entry.getValue());
+ else
+ FrameworkProperties.clearProperty(entry.getKey());
+ }
+ }
+
+ /**
+ * Returns the context of the system bundle. A value of
+ * <code>null</code> is returned if the platform is not running.
+ * @return the context of the system bundle
+ * @throws java.lang.SecurityException If the caller does not have the
+ * appropriate <code>AdminPermission[system.bundle,CONTEXT]</code>, and
+ * the Java Runtime Environment supports permissions.
+ */
+ public static BundleContext getSystemBundleContext() {
+ if (context == null || !running)
+ return null;
+ return context.getBundle().getBundleContext();
+ }
+
+ private static boolean isForcedRestart() {
+ return Boolean.valueOf(FrameworkProperties.getProperty(PROP_FORCED_RESTART)).booleanValue();
+ }
+
+ /*
+ * NOTE: This is an internal/experimental method used by launchers that need to react when the framework
+ * is shutdown internally.
+ *
+ * Adds a framework shutdown handler. <p>
+ * A handler implements the {@link Runnable} interface. When the framework is shutdown
+ * the {@link Runnable#run()} method is called for each registered handler. Handlers should
+ * make no assumptions on the thread it is being called from. If a handler object is
+ * registered multiple times it will be called once for each registration.
+ * <p>
+ * At the time a handler is called the framework is shutdown. Handlers must not depend on
+ * a running framework to execute or attempt to load additional classes from bundles
+ * installed in the framework.
+ * @param handler the framework shutdown handler
+ * @throws IllegalStateException if the platform is already running
+ */
+ static void internalAddFrameworkShutdownHandler(Runnable handler) {
+ if (running)
+ throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_ALREADY_RUNNING);
+
+ if (shutdownHandlers == null)
+ shutdownHandlers = new ArrayList<Runnable>();
+
+ shutdownHandlers.add(handler);
+ }
+
+ /*
+ * NOTE: This is an internal/experimental method used by launchers that need to react when the framework
+ * is shutdown internally.
+ *
+ * Removes a framework shutdown handler. <p>
+ * @param handler the framework shutdown handler
+ * @throws IllegalStateException if the platform is already running
+ */
+ static void internalRemoveFrameworkShutdownHandler(Runnable handler) {
+ if (running)
+ throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_ALREADY_RUNNING);
+
+ if (shutdownHandlers != null)
+ shutdownHandlers.remove(handler);
+ }
+
+ private static void registerFrameworkShutdownHandlers() {
+ if (shutdownHandlers == null)
+ return;
+
+ final Bundle systemBundle = context.getBundle();
+ for (Iterator<Runnable> it = shutdownHandlers.iterator(); it.hasNext();) {
+ final Runnable handler = it.next();
+ BundleListener listener = new BundleListener() {
+ public void bundleChanged(BundleEvent event) {
+ if (event.getBundle() == systemBundle && event.getType() == BundleEvent.STOPPED) {
+ handler.run();
+ }
+ }
+ };
+ context.addBundleListener(listener);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/LocationManager.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/LocationManager.java
new file mode 100644
index 000000000..2e06c32fc
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/LocationManager.java
@@ -0,0 +1,415 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.adaptor;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Properties;
+import org.eclipse.core.runtime.internal.adaptor.*;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.internal.baseadaptor.AdaptorUtil;
+import org.eclipse.osgi.service.datalocation.Location;
+
+/**
+ * This class is used to manage the various Locations for Eclipse.
+ * <p>
+ * Clients may not extend this class.
+ * </p>
+ * @since 3.1
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class LocationManager {
+ private static Location installLocation = null;
+ private static Location configurationLocation = null;
+ private static Location userLocation = null;
+ private static Location instanceLocation = null;
+ private static Location eclipseHomeLocation = null;
+
+ public static final String READ_ONLY_AREA_SUFFIX = ".readOnly"; //$NON-NLS-1$
+ public static final String PROP_INSTALL_AREA = "osgi.install.area"; //$NON-NLS-1$
+ public static final String PROP_CONFIG_AREA = "osgi.configuration.area"; //$NON-NLS-1$
+ public static final String PROP_CONFIG_AREA_DEFAULT = "osgi.configuration.area.default"; //$NON-NLS-1$
+ public static final String PROP_SHARED_CONFIG_AREA = "osgi.sharedConfiguration.area"; //$NON-NLS-1$
+ public static final String PROP_INSTANCE_AREA = "osgi.instance.area"; //$NON-NLS-1$
+ public static final String PROP_INSTANCE_AREA_DEFAULT = "osgi.instance.area.default"; //$NON-NLS-1$
+ public static final String PROP_USER_AREA = "osgi.user.area"; //$NON-NLS-1$
+ public static final String PROP_USER_AREA_DEFAULT = "osgi.user.area.default"; //$NON-NLS-1$
+ public static final String PROP_MANIFEST_CACHE = "osgi.manifest.cache"; //$NON-NLS-1$
+ public static final String PROP_USER_HOME = "user.home"; //$NON-NLS-1$
+ public static final String PROP_USER_DIR = "user.dir"; //$NON-NLS-1$
+ public static final String PROP_HOME_LOCATION_AREA = "eclipse.home.location"; //$NON-NLS-1$
+ static final String PROP_LAUNCHER = "eclipse.launcher"; //$NON-NLS-1$
+
+ // configuration area file/dir names
+ public static final String BUNDLES_DIR = "bundles"; //$NON-NLS-1$
+ public static final String STATE_FILE = ".state"; //$NON-NLS-1$
+ public static final String LAZY_FILE = ".lazy"; //$NON-NLS-1$
+ public static final String BUNDLE_DATA_FILE = ".bundledata"; //$NON-NLS-1$
+ public static final String MANIFESTS_DIR = "manifests"; //$NON-NLS-1$
+ public static final String CONFIG_FILE = "config.ini"; //$NON-NLS-1$
+ public static final String ECLIPSE_PROPERTIES = "eclipse.properties"; //$NON-NLS-1$
+
+ // Constants for configuration location discovery
+ private static final String ECLIPSE = "eclipse"; //$NON-NLS-1$
+ private static final String PRODUCT_SITE_MARKER = ".eclipseproduct"; //$NON-NLS-1$
+ private static final String PRODUCT_SITE_ID = "id"; //$NON-NLS-1$
+ private static final String PRODUCT_SITE_VERSION = "version"; //$NON-NLS-1$
+
+ private static final String CONFIG_DIR = "configuration"; //$NON-NLS-1$
+
+ // Data mode constants for user, configuration and data locations.
+ private static final String NONE = "@none"; //$NON-NLS-1$
+ private static final String NO_DEFAULT = "@noDefault"; //$NON-NLS-1$
+ private static final String USER_HOME = "@user.home"; //$NON-NLS-1$
+ private static final String USER_DIR = "@user.dir"; //$NON-NLS-1$
+ // Placeholder for hashcode of installation directory
+ private static final String INSTALL_HASH_PLACEHOLDER = "@install.hash"; //$NON-NLS-1$
+
+ private static final String INSTANCE_DATA_AREA_PREFIX = ".metadata/.plugins/"; //$NON-NLS-1$
+
+ /**
+ * Builds a URL with the given specification
+ * @param spec the URL specification
+ * @param trailingSlash flag to indicate a trailing slash on the spec
+ * @return a URL
+ */
+ public static URL buildURL(String spec, boolean trailingSlash) {
+ return LocationHelper.buildURL(spec, trailingSlash);
+ }
+
+ private static void mungeConfigurationLocation() {
+ // if the config property was set, munge it for backwards compatibility.
+ String location = FrameworkProperties.getProperty(PROP_CONFIG_AREA);
+ if (location != null) {
+ if (location.endsWith(".cfg")) { //$NON-NLS-1$
+ int index = location.lastIndexOf('/');
+ if (index < 0)
+ index = location.lastIndexOf('\\');
+ location = location.substring(0, index + 1);
+ FrameworkProperties.setProperty(PROP_CONFIG_AREA, location);
+ }
+ }
+ }
+
+ /**
+ * Initializes the Location objects for the LocationManager.
+ */
+ public static void initializeLocations() {
+ // set the osgi storage area if it exists
+ String osgiStorage = FrameworkProperties.getProperty(Constants.FRAMEWORK_STORAGE);
+ if (osgiStorage != null)
+ FrameworkProperties.setProperty(PROP_CONFIG_AREA, osgiStorage);
+ // do install location initialization first since others may depend on it
+ // assumes that the property is already set
+ installLocation = buildLocation(PROP_INSTALL_AREA, null, "", true, false, null); //$NON-NLS-1$
+
+ // TODO not sure what the data area prefix should be here for the user area
+ Location temp = buildLocation(PROP_USER_AREA_DEFAULT, null, "", false, false, null); //$NON-NLS-1$
+ URL defaultLocation = temp == null ? null : temp.getURL();
+ if (defaultLocation == null)
+ defaultLocation = buildURL(new File(FrameworkProperties.getProperty(PROP_USER_HOME), "user").getAbsolutePath(), true); //$NON-NLS-1$
+ userLocation = buildLocation(PROP_USER_AREA, defaultLocation, "", false, false, null); //$NON-NLS-1$
+
+ temp = buildLocation(PROP_INSTANCE_AREA_DEFAULT, null, "", false, false, INSTANCE_DATA_AREA_PREFIX); //$NON-NLS-1$
+ defaultLocation = temp == null ? null : temp.getURL();
+ if (defaultLocation == null)
+ defaultLocation = buildURL(new File(FrameworkProperties.getProperty(PROP_USER_DIR), "workspace").getAbsolutePath(), true); //$NON-NLS-1$
+ instanceLocation = buildLocation(PROP_INSTANCE_AREA, defaultLocation, "", false, false, INSTANCE_DATA_AREA_PREFIX); //$NON-NLS-1$
+
+ mungeConfigurationLocation();
+ // compute a default but it is very unlikely to be used since main will have computed everything
+ temp = buildLocation(PROP_CONFIG_AREA_DEFAULT, null, "", false, false, null); //$NON-NLS-1$
+ defaultLocation = temp == null ? null : temp.getURL();
+ if (defaultLocation == null && FrameworkProperties.getProperty(PROP_CONFIG_AREA) == null)
+ // only compute the default if the configuration area property is not set
+ defaultLocation = buildURL(computeDefaultConfigurationLocation(), true);
+ configurationLocation = buildLocation(PROP_CONFIG_AREA, defaultLocation, "", false, false, null); //$NON-NLS-1$
+ // get the parent location based on the system property. This will have been set on the
+ // way in either by the caller/user or by main. There will be no parent location if we are not
+ // cascaded.
+ URL parentLocation = computeSharedConfigurationLocation();
+ if (parentLocation != null && !parentLocation.equals(configurationLocation.getURL())) {
+ Location parent = new BasicLocation(null, parentLocation, true, null);
+ ((BasicLocation) configurationLocation).setParent(parent);
+ }
+ initializeDerivedConfigurationLocations();
+
+ if (FrameworkProperties.getProperty(PROP_HOME_LOCATION_AREA) == null) {
+ String eclipseLauncher = FrameworkProperties.getProperty(PROP_LAUNCHER);
+ String eclipseHomeLocationPath = getEclipseHomeLocation(eclipseLauncher);
+ if (eclipseHomeLocationPath != null)
+ FrameworkProperties.setProperty(PROP_HOME_LOCATION_AREA, eclipseHomeLocationPath);
+ }
+ // if eclipse.home.location is not set then default to osgi.install.area
+ if (FrameworkProperties.getProperty(PROP_HOME_LOCATION_AREA) == null && FrameworkProperties.getProperty(PROP_INSTALL_AREA) != null)
+ FrameworkProperties.setProperty(PROP_HOME_LOCATION_AREA, FrameworkProperties.getProperty(PROP_INSTALL_AREA));
+ eclipseHomeLocation = buildLocation(PROP_HOME_LOCATION_AREA, null, "", true, true, null); //$NON-NLS-1$
+ }
+
+ private static String getEclipseHomeLocation(String launcher) {
+ if (launcher == null)
+ return null;
+ File launcherFile = new File(launcher);
+ if (launcherFile.getParent() == null)
+ return null;
+ File launcherDir = new File(launcherFile.getParent());
+ // check for mac os; the os check is copied from EclipseEnvironmentInfo.
+ String macosx = org.eclipse.osgi.service.environment.Constants.OS_MACOSX;
+ if (macosx.equals(EclipseEnvironmentInfo.getDefault().getOS()))
+ launcherDir = getMacOSEclipsoeHomeLocation(launcherDir);
+ return (launcherDir.exists() && launcherDir.isDirectory()) ? launcherDir.getAbsolutePath() : null;
+ }
+
+ private static File getMacOSEclipsoeHomeLocation(File launcherDir) {
+ // TODO for now we go up three directories from the launcher dir as long as the parent dir is named MacOS; is this always the case?
+ // TODO not sure if case is important
+ if (!launcherDir.getName().equalsIgnoreCase("macos")) //$NON-NLS-1$
+ return launcherDir; // don't do the up three stuff if not in macos directory
+ String launcherParent = launcherDir.getParent();
+ if (launcherParent != null)
+ launcherParent = new File(launcherParent).getParent();
+ if (launcherParent != null)
+ launcherParent = new File(launcherParent).getParent();
+ return launcherParent == null ? null : new File(launcherParent);
+ }
+
+ @SuppressWarnings("deprecation")
+ private static Location buildLocation(String property, URL defaultLocation, String userDefaultAppendage, boolean readOnlyDefault, boolean computeReadOnly, String dataAreaPrefix) {
+ String location = FrameworkProperties.clearProperty(property);
+ // the user/product may specify a non-default readOnly setting
+ String userReadOnlySetting = FrameworkProperties.getProperty(property + READ_ONLY_AREA_SUFFIX);
+ boolean readOnly = (userReadOnlySetting == null ? readOnlyDefault : Boolean.valueOf(userReadOnlySetting).booleanValue());
+ // if the instance location is not set, predict where the workspace will be and
+ // put the instance area inside the workspace meta area.
+ if (location == null)
+ return new BasicLocation(property, defaultLocation, userReadOnlySetting != null || !computeReadOnly ? readOnly : !canWrite(defaultLocation), dataAreaPrefix);
+ String trimmedLocation = location.trim();
+ if (trimmedLocation.equalsIgnoreCase(NONE))
+ return null;
+ if (trimmedLocation.equalsIgnoreCase(NO_DEFAULT))
+ return new BasicLocation(property, null, readOnly, dataAreaPrefix);
+ if (trimmedLocation.startsWith(USER_HOME)) {
+ String base = substituteVar(location, USER_HOME, PROP_USER_HOME);
+ location = new File(base, userDefaultAppendage).getAbsolutePath();
+ } else if (trimmedLocation.startsWith(USER_DIR)) {
+ String base = substituteVar(location, USER_DIR, PROP_USER_DIR);
+ location = new File(base, userDefaultAppendage).getAbsolutePath();
+ }
+ int idx = location.indexOf(INSTALL_HASH_PLACEHOLDER);
+ if (idx == 0) {
+ throw new RuntimeException("The location cannot start with '" + INSTALL_HASH_PLACEHOLDER + "': " + location); //$NON-NLS-1$ //$NON-NLS-2$
+ } else if (idx > 0) {
+ location = location.substring(0, idx) + getInstallDirHash() + location.substring(idx + INSTALL_HASH_PLACEHOLDER.length());
+ }
+ URL url = buildURL(location, true);
+ BasicLocation result = null;
+ if (url != null) {
+ result = new BasicLocation(property, null, userReadOnlySetting != null || !computeReadOnly ? readOnly : !canWrite(url), dataAreaPrefix);
+ result.setURL(url, false);
+ }
+ return result;
+ }
+
+ private static String substituteVar(String source, String var, String prop) {
+ String value = FrameworkProperties.getProperty(prop, ""); //$NON-NLS-1$
+ return value + source.substring(var.length());
+ }
+
+ private static void initializeDerivedConfigurationLocations() {
+ if (FrameworkProperties.getProperty(PROP_MANIFEST_CACHE) == null)
+ FrameworkProperties.setProperty(PROP_MANIFEST_CACHE, getConfigurationFile(MANIFESTS_DIR).getAbsolutePath());
+ }
+
+ private static URL computeInstallConfigurationLocation() {
+ String property = FrameworkProperties.getProperty(PROP_INSTALL_AREA);
+ if (property != null)
+ return LocationHelper.buildURL(property, true);
+ return null;
+ }
+
+ private static URL computeSharedConfigurationLocation() {
+ String property = FrameworkProperties.getProperty(PROP_SHARED_CONFIG_AREA);
+ if (property == null)
+ return null;
+ try {
+ URL sharedConfigurationURL = LocationHelper.buildURL(property, true);
+ if (sharedConfigurationURL == null)
+ return null;
+ if (sharedConfigurationURL.getPath().startsWith("/")) //$NON-NLS-1$
+ // absolute
+ return sharedConfigurationURL;
+ URL installURL = installLocation.getURL();
+ if (!sharedConfigurationURL.getProtocol().equals(installURL.getProtocol()))
+ // different protocol
+ return sharedConfigurationURL;
+ sharedConfigurationURL = new URL(installURL, sharedConfigurationURL.getPath());
+ FrameworkProperties.setProperty(PROP_SHARED_CONFIG_AREA, sharedConfigurationURL.toExternalForm());
+ } catch (MalformedURLException e) {
+ // do nothing here since it is basically impossible to get a bogus url
+ }
+ return null;
+ }
+
+ private static String computeDefaultConfigurationLocation() {
+ // 1) We store the config state relative to the 'eclipse' directory if possible
+ // 2) If this directory is read-only
+ // we store the state in <user.home>/.eclipse/<application-id>_<version> where <user.home>
+ // is unique for each local user, and <application-id> is the one
+ // defined in .eclipseproduct marker file. If .eclipseproduct does not
+ // exist, use "eclipse" as the application-id.
+
+ URL installURL = computeInstallConfigurationLocation();
+ if (installURL != null && "file".equals(installURL.getProtocol())) { //$NON-NLS-1$
+ File installDir = new File(installURL.getFile());
+ File defaultConfigDir = new File(installDir, CONFIG_DIR);
+ if (!defaultConfigDir.exists())
+ defaultConfigDir.mkdirs();
+ if (defaultConfigDir.exists() && AdaptorUtil.canWrite(defaultConfigDir))
+ return defaultConfigDir.getAbsolutePath();
+ }
+ // We can't write in the eclipse install dir so try for some place in the user's home dir
+ return computeDefaultUserAreaLocation(CONFIG_DIR);
+ }
+
+ private static boolean canWrite(URL location) {
+ if (location != null && "file".equals(location.getProtocol())) { //$NON-NLS-1$
+ File locationDir = new File(location.getFile());
+ if (!locationDir.exists())
+ locationDir.mkdirs();
+ if (locationDir.exists() && AdaptorUtil.canWrite(locationDir))
+ return true;
+ }
+ return false;
+ }
+
+ private static String computeDefaultUserAreaLocation(String pathAppendage) {
+ // we store the state in <user.home>/.eclipse/<application-id>_<version> where <user.home>
+ // is unique for each local user, and <application-id> is the one
+ // defined in .eclipseproduct marker file. If .eclipseproduct does not
+ // exist, use "eclipse" as the application-id.
+ String installProperty = FrameworkProperties.getProperty(PROP_INSTALL_AREA);
+ URL installURL = buildURL(installProperty, true);
+ if (installURL == null)
+ return null;
+ File installDir = new File(installURL.getFile());
+ String installDirHash = getInstallDirHash();
+
+ String appName = "." + ECLIPSE; //$NON-NLS-1$
+ File eclipseProduct = new File(installDir, PRODUCT_SITE_MARKER);
+ if (eclipseProduct.exists()) {
+ Properties props = new Properties();
+ try {
+ props.load(new FileInputStream(eclipseProduct));
+ String appId = props.getProperty(PRODUCT_SITE_ID);
+ if (appId == null || appId.trim().length() == 0)
+ appId = ECLIPSE;
+ String appVersion = props.getProperty(PRODUCT_SITE_VERSION);
+ if (appVersion == null || appVersion.trim().length() == 0)
+ appVersion = ""; //$NON-NLS-1$
+ appName += File.separator + appId + "_" + appVersion + "_" + installDirHash; //$NON-NLS-1$ //$NON-NLS-2$
+ } catch (IOException e) {
+ // Do nothing if we get an exception. We will default to a standard location
+ // in the user's home dir.
+ // add the hash to help prevent collisions
+ appName += File.separator + installDirHash;
+ }
+ } else {
+ // add the hash to help prevent collisions
+ appName += File.separator + installDirHash;
+ }
+ String userHome = FrameworkProperties.getProperty(PROP_USER_HOME);
+ return new File(userHome, appName + "/" + pathAppendage).getAbsolutePath(); //$NON-NLS-1$
+ }
+
+ /**
+ * Return hash code identifying an absolute installation path
+ * @return hash code as String
+ */
+ private static String getInstallDirHash() {
+ // compute an install dir hash to prevent configuration area collisions with other eclipse installs
+ String installProperty = FrameworkProperties.getProperty(PROP_INSTALL_AREA);
+ URL installURL = buildURL(installProperty, true);
+ if (installURL == null)
+ return ""; //$NON-NLS-1$
+ File installDir = new File(installURL.getFile());
+ int hashCode;
+ try {
+ hashCode = installDir.getCanonicalPath().hashCode();
+ } catch (IOException ioe) {
+ // fall back to absolute path
+ hashCode = installDir.getAbsolutePath().hashCode();
+ }
+ if (hashCode < 0)
+ hashCode = -(hashCode);
+ String installDirHash = String.valueOf(hashCode);
+ return installDirHash;
+ }
+
+ /**
+ * Returns the user Location object
+ * @return the user Location object
+ */
+ public static Location getUserLocation() {
+ return userLocation;
+ }
+
+ /**
+ * Returns the configuration Location object
+ * @return the configuration Location object
+ */
+ public static Location getConfigurationLocation() {
+ return configurationLocation;
+ }
+
+ /**
+ * Returns the install Location object
+ * @return the install Location object
+ */
+ public static Location getInstallLocation() {
+ return installLocation;
+ }
+
+ /**
+ * Returns the instance Location object
+ * @return the instance Location object
+ */
+ public static Location getInstanceLocation() {
+ return instanceLocation;
+ }
+
+ public static Location getEclipseHomeLocation() {
+ return eclipseHomeLocation;
+ }
+
+ /**
+ * Returns the File object under the configuration location used for the OSGi configuration
+ * @return the OSGi configuration directory
+ */
+ public static File getOSGiConfigurationDir() {
+ // TODO assumes the URL is a file: url
+ return new File(configurationLocation.getURL().getFile(), FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME);
+ }
+
+ /**
+ * Returns a file from the configuration area that can be used by the framework
+ * @param filename the filename
+ * @return a file from the configuration area
+ */
+ public static File getConfigurationFile(String filename) {
+ File dir = getOSGiConfigurationDir();
+ if (!dir.exists())
+ dir.mkdirs();
+ return new File(dir, filename);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/package.html b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/package.html
new file mode 100644
index 000000000..65b481997
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/adaptor/package.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides API to start the platform.
+<h2>
+Package Specification</h2>
+This package specifies API to start the platform.
+<p>
+Clients may use the <tt>EclipseStarter</tt> loader class to start the platform. The
+<tt>EclipseStarter</tt> class is the only defined API in this package.
+</p>
+</body>
+</html>
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/BundleLocalizationImpl.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/BundleLocalizationImpl.java
new file mode 100644
index 000000000..e2eb1d829
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/BundleLocalizationImpl.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.util.ResourceBundle;
+import org.eclipse.osgi.service.localization.BundleLocalization;
+import org.osgi.framework.Bundle;
+
+/**
+ * The implementation of the service that gets ResourceBundle objects from a given
+ * bundle with a given locale.
+ *
+ * <p>Internal class.</p>
+ */
+
+public class BundleLocalizationImpl implements BundleLocalization {
+ /**
+ * The getLocalization method gets a ResourceBundle object for the given
+ * locale and bundle.
+ *
+ * @return A <code>ResourceBundle</code> object for the given bundle and locale.
+ * If null is passed for the locale parameter, the default locale is used.
+ */
+ public ResourceBundle getLocalization(Bundle bundle, String locale) {
+ return ((org.eclipse.osgi.framework.internal.core.AbstractBundle) (bundle)).getResourceBundle(locale);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/CachedManifest.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/CachedManifest.java
new file mode 100644
index 000000000..550c2e24b
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/CachedManifest.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.framework.util.Headers;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Version;
+
+/**
+ * Internal class.
+ */
+public class CachedManifest extends Dictionary<String, String> {
+ static final String SERVICE_COMPONENT = "Service-Component"; //$NON-NLS-1$
+ static boolean DEBUG = false;
+ private Dictionary<String, String> manifest = null;
+ private EclipseStorageHook storageHook;
+
+ public CachedManifest(EclipseStorageHook storageHook) {
+ this.storageHook = storageHook;
+ }
+
+ public Dictionary<String, String> getManifest() {
+ if (manifest == null)
+ try {
+ if (DEBUG)
+ System.out.println("Reading manifest for: " + storageHook.getBaseData()); //$NON-NLS-1$
+ manifest = storageHook.createCachedManifest(true);
+ } catch (BundleException e) {
+ final String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CACHEDMANIFEST_UNEXPECTED_EXCEPTION, storageHook.getBaseData().getLocation());
+ FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, e, null);
+ storageHook.getAdaptor().getFrameworkLog().log(entry);
+ }
+ if (manifest == null) {
+ Headers<String, String> empty = new Headers<String, String>(0);
+ empty.setReadOnly();
+ manifest = empty;
+ return empty;
+ }
+ return manifest;
+ }
+
+ public int size() {
+ return getManifest().size();
+ }
+
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ public Enumeration<String> elements() {
+ return getManifest().elements();
+ }
+
+ public Enumeration<String> keys() {
+ return getManifest().keys();
+ }
+
+ @SuppressWarnings("deprecation")
+ public String get(Object key) {
+ if (manifest != null)
+ return manifest.get(key);
+ String keyString = (String) key;
+ if (Constants.BUNDLE_VERSION.equalsIgnoreCase(keyString)) {
+ Version result = storageHook.getBaseData().getVersion();
+ return result == null ? null : result.toString();
+ }
+ if (Constants.PLUGIN_CLASS.equalsIgnoreCase(keyString))
+ return storageHook.getPluginClass();
+ if (Constants.BUNDLE_SYMBOLICNAME.equalsIgnoreCase(keyString)) {
+ if ((storageHook.getBaseData().getType() & BundleData.TYPE_SINGLETON) == 0)
+ return storageHook.getBaseData().getSymbolicName();
+ return storageHook.getBaseData().getSymbolicName() + ';' + Constants.SINGLETON_DIRECTIVE + ":=true"; //$NON-NLS-1$
+ }
+ if (Constants.BUDDY_LOADER.equalsIgnoreCase(keyString))
+ return storageHook.getBuddyList();
+ if (Constants.REGISTERED_POLICY.equalsIgnoreCase(keyString))
+ return storageHook.getRegisteredBuddyList();
+ if (Constants.BUNDLE_ACTIVATOR.equalsIgnoreCase(keyString))
+ return storageHook.getBaseData().getActivator();
+ if (Constants.BUNDLE_ACTIVATIONPOLICY.equals(keyString)) {
+ if (!storageHook.isAutoStartable())
+ return null;
+ String[] excludes = storageHook.getLazyStartExcludes();
+ String[] includes = storageHook.getLazyStartIncludes();
+ if (excludes == null && includes == null)
+ return Constants.ACTIVATION_LAZY;
+ StringBuffer result = new StringBuffer(Constants.ACTIVATION_LAZY);
+ if (excludes != null) {
+ result.append(';').append(Constants.EXCLUDE_DIRECTIVE).append(":=\""); //$NON-NLS-1$
+ for (int i = 0; i < excludes.length; i++) {
+ if (i > 0)
+ result.append(',');
+ result.append(excludes[i]);
+ }
+ result.append("\""); //$NON-NLS-1$
+ }
+ if (includes != null) {
+ result.append(';').append(Constants.INCLUDE_DIRECTIVE).append(":=\""); //$NON-NLS-1$
+ for (int i = 0; i < includes.length; i++) {
+ if (i > 0)
+ result.append(',');
+ result.append(includes[i]);
+ }
+ result.append("\""); //$NON-NLS-1$
+ }
+ }
+ if (Constants.ECLIPSE_LAZYSTART.equals(keyString) || Constants.ECLIPSE_AUTOSTART.equals(keyString)) {
+ if (!storageHook.isAutoStartable())
+ return null;
+ if (storageHook.getLazyStartExcludes() == null)
+ return Boolean.TRUE.toString();
+ StringBuffer result = new StringBuffer(storageHook.isLazyStart() ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
+ result.append(";").append(Constants.ECLIPSE_LAZYSTART_EXCEPTIONS).append("=\""); //$NON-NLS-1$ //$NON-NLS-2$
+ String[] exceptions = storageHook.getLazyStartExcludes();
+ for (int i = 0; i < exceptions.length; i++) {
+ if (i > 0)
+ result.append(","); //$NON-NLS-1$
+ result.append(exceptions[i]);
+ }
+ result.append("\""); //$NON-NLS-1$
+ return result.toString();
+ }
+ if (Constants.BUNDLE_MANIFESTVERSION.equals(keyString))
+ return storageHook.getBundleManifestVersion() == 0 ? null : Integer.toString(storageHook.getBundleManifestVersion());
+ if (SERVICE_COMPONENT.equals(keyString))
+ return storageHook.getServiceComponent();
+ Dictionary<String, String> result = getManifest();
+ if (DEBUG)
+ System.out.println("Manifest read because of header: " + key); //$NON-NLS-1$
+ return result == null ? null : result.get(key);
+ }
+
+ public String remove(Object key) {
+ return getManifest().remove(key);
+ }
+
+ public String put(String key, String value) {
+ return getManifest().put(key, value);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ClasspathManifest.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ClasspathManifest.java
new file mode 100644
index 000000000..80e3c28cd
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ClasspathManifest.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.Manifest;
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.loader.*;
+import org.eclipse.osgi.framework.util.KeyedElement;
+
+public class ClasspathManifest implements KeyedElement {
+ public static final Object KEY = new Object();
+ public static final int HASHCODE = KEY.hashCode();
+
+ private Manifest manifest;
+ private boolean initialized = false;
+
+ public int getKeyHashCode() {
+ return HASHCODE;
+ }
+
+ public boolean compare(KeyedElement other) {
+ return other.getKey() == KEY;
+ }
+
+ public Object getKey() {
+ return KEY;
+ }
+
+ public synchronized Manifest getManifest(ClasspathEntry cpEntry, ClasspathManager loader) {
+ if (initialized)
+ return manifest;
+ if (!hasPackageInfo(cpEntry, loader)) {
+ initialized = true;
+ manifest = null;
+ return manifest;
+ }
+ BundleEntry mfEntry = cpEntry.getBundleFile().getEntry(org.eclipse.osgi.framework.internal.core.Constants.OSGI_BUNDLE_MANIFEST);
+ if (mfEntry != null) {
+ InputStream manIn = null;
+ try {
+ try {
+ manIn = mfEntry.getInputStream();
+ manifest = new Manifest(manIn);
+ } finally {
+ if (manIn != null)
+ manIn.close();
+ }
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ initialized = true;
+ return manifest;
+ }
+
+ private boolean hasPackageInfo(ClasspathEntry cpEntry, ClasspathManager loader) {
+ BaseData bundledata = null;
+ if (cpEntry.getBundleFile() == loader.getBaseData().getBundleFile())
+ bundledata = loader.getBaseData();
+ if (bundledata == null) {
+ FragmentClasspath[] fragCPs = loader.getFragmentClasspaths();
+ if (fragCPs != null)
+ for (int i = 0; i < fragCPs.length; i++)
+ if (cpEntry.getBundleFile() == fragCPs[i].getBundleData().getBundleFile()) {
+ bundledata = fragCPs[i].getBundleData();
+ break;
+ }
+ }
+ if (bundledata == null)
+ return true;
+ EclipseStorageHook storageHook = (EclipseStorageHook) bundledata.getStorageHook(EclipseStorageHook.KEY);
+ return storageHook == null ? true : storageHook.hasPackageInfo();
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ContextFinder.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ContextFinder.java
new file mode 100644
index 000000000..9ecc15b45
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/ContextFinder.java
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import org.eclipse.osgi.framework.adaptor.BundleClassLoader;
+
+public class ContextFinder extends ClassLoader implements PrivilegedAction<List<ClassLoader>> {
+ static final class Finder extends SecurityManager {
+ public Class<?>[] getClassContext() {
+ return super.getClassContext();
+ }
+ }
+
+ //This is used to detect cycle that could be caused while delegating the loading to other classloaders
+ //It keeps track on a thread basis of the set of requested classes and resources
+ private static ThreadLocal<Set<String>> cycleDetector = new ThreadLocal<Set<String>>();
+ static ClassLoader finderClassLoader;
+ static Finder contextFinder;
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ finderClassLoader = ContextFinder.class.getClassLoader();
+ contextFinder = new Finder();
+ return null;
+ }
+ });
+ }
+
+ private static Class<ContextFinder> THIS = ContextFinder.class;
+
+ private final ClassLoader parentContextClassLoader;
+
+ public ContextFinder(ClassLoader contextClassLoader) {
+ super(contextClassLoader);
+ this.parentContextClassLoader = contextClassLoader != null ? contextClassLoader : new ClassLoader(Object.class.getClassLoader()) {/*boot classloader*/};
+ }
+
+ // Return a list of all classloaders on the stack that are neither the
+ // ContextFinder classloader nor the boot classloader. The last classloader
+ // in the list is either a bundle classloader or the framework's classloader
+ // We assume that the bootclassloader never uses the context classloader to find classes in itself.
+ List<ClassLoader> basicFindClassLoaders() {
+ Class<?>[] stack = contextFinder.getClassContext();
+ List<ClassLoader> result = new ArrayList<ClassLoader>(1);
+ ClassLoader previousLoader = null;
+ for (int i = 1; i < stack.length; i++) {
+ ClassLoader tmp = stack[i].getClassLoader();
+ if (stack[i] != THIS && tmp != null && tmp != this) {
+ if (checkClassLoader(tmp)) {
+ if (previousLoader != tmp) {
+ result.add(tmp);
+ previousLoader = tmp;
+ }
+ }
+ // stop at the framework classloader or the first bundle classloader
+ if (tmp == finderClassLoader || tmp instanceof BundleClassLoader)
+ break;
+ }
+ }
+ return result;
+ }
+
+ // ensures that a classloader does not have the ContextFinder as part of the
+ // parent hierachy. A classloader which has the ContextFinder as a parent must
+ // not be used as a delegate, otherwise we endup in endless recursion.
+ private boolean checkClassLoader(ClassLoader classloader) {
+ if (classloader == null || classloader == getParent())
+ return false;
+ for (ClassLoader parent = classloader.getParent(); parent != null; parent = parent.getParent())
+ if (parent == this)
+ return false;
+ return true;
+ }
+
+ private List<ClassLoader> findClassLoaders() {
+ if (System.getSecurityManager() == null)
+ return basicFindClassLoaders();
+ return AccessController.doPrivileged(this);
+ }
+
+ public List<ClassLoader> run() {
+ return basicFindClassLoaders();
+ }
+
+ //Return whether the request for loading "name" should proceed.
+ //False is returned when a cycle is being detected
+ private boolean startLoading(String name) {
+ Set<String> classesAndResources = cycleDetector.get();
+ if (classesAndResources != null && classesAndResources.contains(name))
+ return false;
+
+ if (classesAndResources == null) {
+ classesAndResources = new HashSet<String>(3);
+ cycleDetector.set(classesAndResources);
+ }
+ classesAndResources.add(name);
+ return true;
+ }
+
+ private void stopLoading(String name) {
+ cycleDetector.get().remove(name);
+ }
+
+ protected Class<?> loadClass(String arg0, boolean arg1) throws ClassNotFoundException {
+ //Shortcut cycle
+ if (startLoading(arg0) == false)
+ throw new ClassNotFoundException(arg0);
+
+ try {
+ List<ClassLoader> toConsult = findClassLoaders();
+ for (Iterator<ClassLoader> loaders = toConsult.iterator(); loaders.hasNext();)
+ try {
+ return loaders.next().loadClass(arg0);
+ } catch (ClassNotFoundException e) {
+ // go to the next class loader
+ }
+ // avoid calling super.loadClass here because it checks the local cache (bug 127963)
+ return parentContextClassLoader.loadClass(arg0);
+ } finally {
+ stopLoading(arg0);
+ }
+ }
+
+ public URL getResource(String arg0) {
+ //Shortcut cycle
+ if (startLoading(arg0) == false)
+ return null;
+ try {
+ List<ClassLoader> toConsult = findClassLoaders();
+ for (Iterator<ClassLoader> loaders = toConsult.iterator(); loaders.hasNext();) {
+ URL result = loaders.next().getResource(arg0);
+ if (result != null)
+ return result;
+ // go to the next class loader
+ }
+ return super.getResource(arg0);
+ } finally {
+ stopLoading(arg0);
+ }
+ }
+
+ protected Enumeration<URL> findResources(String arg0) throws IOException {
+ //Shortcut cycle
+ if (startLoading(arg0) == false) {
+ @SuppressWarnings("unchecked")
+ Enumeration<URL> result = Collections.enumeration(Collections.EMPTY_LIST);
+ return result;
+ }
+ try {
+ List<ClassLoader> toConsult = findClassLoaders();
+ for (Iterator<ClassLoader> loaders = toConsult.iterator(); loaders.hasNext();) {
+ Enumeration<URL> result = loaders.next().getResources(arg0);
+ if (result != null && result.hasMoreElements())
+ return result;
+ // go to the next class loader
+ }
+ return super.findResources(arg0);
+ } finally {
+ stopLoading(arg0);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/DefaultStartupMonitor.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/DefaultStartupMonitor.java
new file mode 100644
index 000000000..9ac29d38d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/DefaultStartupMonitor.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Andrew Niefer - IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.lang.reflect.Method;
+import org.eclipse.core.runtime.adaptor.EclipseStarter;
+import org.eclipse.core.runtime.internal.stats.StatsManager;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.service.runnable.StartupMonitor;
+
+public class DefaultStartupMonitor implements StartupMonitor {
+
+ private Method updateMethod = null;
+ private Runnable splashHandler = null;
+
+ /**
+ * Create a new startup monitor using the given splash handler. The splash handle must
+ * have an updateSplash method.
+ *
+ * @param splashHandler
+ * @throws IllegalStateException
+ */
+ public DefaultStartupMonitor(Runnable splashHandler) throws IllegalStateException {
+ this.splashHandler = splashHandler;
+
+ try {
+ updateMethod = splashHandler.getClass().getMethod("updateSplash", (Class[]) null); //$NON-NLS-1$
+ } catch (SecurityException e) {
+ throw (IllegalStateException) new IllegalStateException(e.getMessage()).initCause(e);
+ } catch (NoSuchMethodException e) {
+ //TODO maybe we could do something else in the update method in this case, like print something to the console?
+ throw (IllegalStateException) new IllegalStateException(e.getMessage()).initCause(e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.adaptor.StartupMonitor#update()
+ */
+ public void update() {
+ if (updateMethod != null) {
+ try {
+ updateMethod.invoke(splashHandler, (Object[]) null);
+ } catch (Throwable e) {
+ // ignore, this is best effort
+ }
+ } else {
+ //TODO maybe we could print something interesting to the console?
+ }
+ }
+
+ public void applicationRunning() {
+ if (EclipseStarter.debug) {
+ String timeString = FrameworkProperties.getProperty("eclipse.startTime"); //$NON-NLS-1$
+ long time = timeString == null ? 0L : Long.parseLong(timeString);
+ System.out.println("Application Started: " + (System.currentTimeMillis() - time)); //$NON-NLS-1$
+ }
+ StatsManager.doneBooting();
+ splashHandler.run();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseAdaptorHook.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseAdaptorHook.java
new file mode 100644
index 000000000..657e6be86
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseAdaptorHook.java
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.IOException;
+import java.net.URLConnection;
+import java.util.*;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.SAXParserFactory;
+import org.eclipse.core.runtime.adaptor.LocationManager;
+import org.eclipse.osgi.baseadaptor.*;
+import org.eclipse.osgi.baseadaptor.hooks.AdaptorHook;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.debug.FrameworkDebugOptions;
+import org.eclipse.osgi.framework.internal.core.BundleHost;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.internal.baseadaptor.AdaptorUtil;
+import org.eclipse.osgi.service.datalocation.Location;
+import org.eclipse.osgi.service.pluginconversion.PluginConverter;
+import org.eclipse.osgi.service.resolver.PlatformAdmin;
+import org.eclipse.osgi.service.urlconversion.URLConverter;
+import org.osgi.framework.*;
+
+public class EclipseAdaptorHook implements AdaptorHook, HookConfigurator {
+ /** The SAX factory name */
+ public static final String SAXFACTORYNAME = "javax.xml.parsers.SAXParserFactory"; //$NON-NLS-1$
+ /** The DOM factory name */
+ public static final String DOMFACTORYNAME = "javax.xml.parsers.DocumentBuilderFactory"; //$NON-NLS-1$
+ private static final String RUNTIME_ADAPTOR = FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME + "/eclipseadaptor"; //$NON-NLS-1$
+ private static final String OPTION_CONVERTER = RUNTIME_ADAPTOR + "/converter/debug"; //$NON-NLS-1$
+ private static final String OPTION_LOCATION = RUNTIME_ADAPTOR + "/debug/location"; //$NON-NLS-1$
+ private static final String OPTION_CACHEDMANIFEST = RUNTIME_ADAPTOR + "/debug/cachedmanifest"; //$NON-NLS-1$
+ static final boolean SET_TCCL_XMLFACTORY = "true".equals(FrameworkProperties.getProperty("eclipse.parsers.setTCCL", "true"));//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ private BaseAdaptor adaptor;
+ private boolean noXML = false;
+ private List<ServiceRegistration<?>> registrations = new ArrayList<ServiceRegistration<?>>(10);
+
+ /**
+ * @throws BundleException
+ */
+ public void frameworkStart(BundleContext context) throws BundleException {
+ registrations.clear();
+ registerEndorsedXMLParser(context);
+ Dictionary<String, Object> locationProperties = new Hashtable<String, Object>(1);
+ Location location = LocationManager.getUserLocation();
+ if (location != null) {
+ locationProperties.put("type", LocationManager.PROP_USER_AREA); //$NON-NLS-1$
+ registrations.add(context.registerService(Location.class.getName(), location, locationProperties));
+ }
+ location = LocationManager.getInstanceLocation();
+ if (location != null) {
+ locationProperties.put("type", LocationManager.PROP_INSTANCE_AREA); //$NON-NLS-1$
+ registrations.add(context.registerService(Location.class.getName(), location, locationProperties));
+ }
+ location = LocationManager.getConfigurationLocation();
+ if (location != null) {
+ locationProperties.put("type", LocationManager.PROP_CONFIG_AREA); //$NON-NLS-1$
+ registrations.add(context.registerService(Location.class.getName(), location, locationProperties));
+ }
+ location = LocationManager.getInstallLocation();
+ if (location != null) {
+ locationProperties.put("type", LocationManager.PROP_INSTALL_AREA); //$NON-NLS-1$
+ registrations.add(context.registerService(Location.class.getName(), location, locationProperties));
+ }
+
+ location = LocationManager.getEclipseHomeLocation();
+ if (location != null) {
+ locationProperties.put("type", LocationManager.PROP_HOME_LOCATION_AREA); //$NON-NLS-1$
+ registrations.add(context.registerService(Location.class.getName(), location, locationProperties));
+ }
+
+ Dictionary<String, Object> urlProperties = new Hashtable<String, Object>();
+ urlProperties.put("protocol", new String[] {"bundleentry", "bundleresource"}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ registrations.add(context.registerService(URLConverter.class.getName(), new URLConverterImpl(), urlProperties));
+
+ registrations.add(AdaptorUtil.register(org.eclipse.osgi.service.environment.EnvironmentInfo.class.getName(), EclipseEnvironmentInfo.getDefault(), context));
+ registrations.add(AdaptorUtil.register(PlatformAdmin.class.getName(), adaptor.getPlatformAdmin(), context));
+ PluginConverter converter = PluginConverterImpl.getDefault();
+ if (converter == null)
+ converter = new PluginConverterImpl(adaptor, context);
+ registrations.add(AdaptorUtil.register(PluginConverter.class.getName(), converter, context));
+ registrations.add(AdaptorUtil.register(org.eclipse.osgi.service.localization.BundleLocalization.class.getName(), new BundleLocalizationImpl(), context));
+ }
+
+ private void registerEndorsedXMLParser(BundleContext bc) {
+ try {
+ Class.forName(SAXFACTORYNAME);
+ registrations.add(bc.registerService(SAXFACTORYNAME, new ParsingService(true), null));
+ Class.forName(DOMFACTORYNAME);
+ registrations.add(bc.registerService(DOMFACTORYNAME, new ParsingService(false), null));
+ } catch (ClassNotFoundException e) {
+ noXML = true;
+ if (Debug.DEBUG_ENABLED) {
+ String message = EclipseAdaptorMsg.ECLIPSE_ADAPTOR_ERROR_XML_SERVICE;
+ adaptor.getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, e, null));
+ }
+ }
+ }
+
+ private static class ParsingService implements ServiceFactory<Object> {
+ private final boolean isSax;
+
+ public ParsingService(boolean isSax) {
+ this.isSax = isSax;
+ }
+
+ public Object getService(Bundle bundle, ServiceRegistration<Object> registration) {
+ BundleHost host = (bundle instanceof BundleHost) ? (BundleHost) bundle : null;
+ if (!SET_TCCL_XMLFACTORY || bundle == null)
+ return createService();
+ /*
+ * Set the TCCL while creating jaxp factory instances to the
+ * requesting bundles class loader. This is needed to
+ * work around bug 285505. There are issues if multiple
+ * xerces implementations are available on the bundles class path
+ *
+ * The real issue is that the ContextFinder will only delegate
+ * to the framework class loader in this case. This class
+ * loader forces the requesting bundle to be delegated to for
+ * TCCL loads.
+ */
+ final ClassLoader savedClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ ClassLoader cl = host.getClassLoader();
+ if (cl != null)
+ Thread.currentThread().setContextClassLoader(cl);
+ return createService();
+ } finally {
+ Thread.currentThread().setContextClassLoader(savedClassLoader);
+ }
+ }
+
+ private Object createService() {
+ if (isSax)
+ return SAXParserFactory.newInstance();
+ return DocumentBuilderFactory.newInstance();
+ }
+
+ public void ungetService(Bundle bundle, ServiceRegistration<Object> registration, Object service) {
+ // Do nothing.
+ }
+ }
+
+ /**
+ * @throws BundleException
+ */
+ public void frameworkStop(BundleContext context) throws BundleException {
+ printStats();
+ if (!noXML)
+ PluginParser.releaseXMLParsing();
+ // unregister services
+ for (ServiceRegistration<?> registration : registrations)
+ registration.unregister();
+ registrations.clear();
+ }
+
+ private void printStats() {
+ FrameworkDebugOptions debugOptions = FrameworkDebugOptions.getDefault();
+ if (debugOptions == null)
+ return;
+ String registryParsing = debugOptions.getOption("org.eclipse.core.runtime/registry/parsing/timing/value"); //$NON-NLS-1$
+ if (registryParsing != null)
+ MessageHelper.debug("Time spent in registry parsing: " + registryParsing); //$NON-NLS-1$
+ String packageAdminResolution = debugOptions.getOption("debug.packageadmin/timing/value"); //$NON-NLS-1$
+ if (packageAdminResolution != null)
+ System.out.println("Time spent in package admin resolve: " + packageAdminResolution); //$NON-NLS-1$
+ String constraintResolution = debugOptions.getOption("org.eclipse.core.runtime.adaptor/resolver/timing/value"); //$NON-NLS-1$
+ if (constraintResolution != null)
+ System.out.println("Time spent resolving the dependency system: " + constraintResolution); //$NON-NLS-1$
+ }
+
+ public void frameworkStopping(BundleContext context) {
+ // do nothing
+ }
+
+ public void addProperties(Properties properties) {
+ // do nothing
+ }
+
+ /**
+ * @throws IOException
+ */
+ public URLConnection mapLocationToURLConnection(String location) throws IOException {
+ // do nothing
+ return null;
+ }
+
+ public void handleRuntimeError(Throwable error) {
+ // do nothing
+ }
+
+ public FrameworkLog createFrameworkLog() {
+ // do nothing
+ return null;
+ }
+
+ public void initialize(BaseAdaptor initAdaptor) {
+ this.adaptor = initAdaptor;
+ // EnvironmentInfo has to be initialized first to compute defaults for system context (see bug 88925)
+ EclipseEnvironmentInfo.getDefault();
+ setDebugOptions();
+ }
+
+ private void setDebugOptions() {
+ FrameworkDebugOptions options = FrameworkDebugOptions.getDefault();
+ // may be null if debugging is not enabled
+ if (options == null)
+ return;
+ PluginConverterImpl.DEBUG = options.getBooleanOption(OPTION_CONVERTER, false);
+ BasicLocation.DEBUG = options.getBooleanOption(OPTION_LOCATION, false);
+ CachedManifest.DEBUG = options.getBooleanOption(OPTION_CACHEDMANIFEST, false);
+ }
+
+ public void addHooks(HookRegistry hookRegistry) {
+ hookRegistry.addAdaptorHook(this);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseAppLauncher.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseAppLauncher.java
new file mode 100644
index 000000000..a4106fdb6
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseAppLauncher.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import org.eclipse.core.runtime.adaptor.EclipseStarter;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.internal.profile.Profile;
+import org.eclipse.osgi.service.runnable.*;
+import org.osgi.framework.*;
+
+public class EclipseAppLauncher implements ApplicationLauncher {
+ volatile private ParameterizedRunnable runnable = null;
+ private Object appContext = null;
+ private Semaphore runningLock = new Semaphore(1);
+ private Semaphore waitForAppLock = new Semaphore(0);
+ private BundleContext context;
+ private boolean relaunch = false;
+ private boolean failOnNoDefault = false;
+ private FrameworkLog log;
+
+ public EclipseAppLauncher(BundleContext context, boolean relaunch, boolean failOnNoDefault, FrameworkLog log) {
+ this.context = context;
+ this.relaunch = relaunch;
+ this.failOnNoDefault = failOnNoDefault;
+ this.log = log;
+ findRunnableService();
+ }
+
+ /*
+ * Used for backwards compatibility with < 3.2 runtime
+ */
+ private void findRunnableService() {
+ // look for a ParameterizedRunnable registered as a service by runtimes (3.0, 3.1)
+ String appClass = ParameterizedRunnable.class.getName();
+ ServiceReference<?>[] runRefs = null;
+ try {
+ runRefs = context.getServiceReferences(ParameterizedRunnable.class.getName(), "(&(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.
+ }
+ if (runRefs != null && runRefs.length > 0) {
+ // found the service use it as the application.
+ runnable = (ParameterizedRunnable) context.getService(runRefs[0]);
+ // we will never be able to relaunch with a pre 3.2 runtime
+ relaunch = false;
+ waitForAppLock.release();
+ }
+ }
+
+ /*
+ * Starts this application launcher on the current thread. This method
+ * should be called by the main thread to ensure that applications are
+ * launched in the main thread.
+ */
+ public Object start(Object defaultContext) throws Exception {
+ // here we assume that launch has been called by runtime before we started
+ // TODO this may be a bad assumption but it works for now because we register the app launcher as a service and runtime synchronously calls launch on the service
+ if (failOnNoDefault && runnable == null)
+ throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_NO_APPLICATION);
+ Object result = null;
+ boolean doRelaunch;
+ do {
+ try {
+ result = runApplication(defaultContext);
+ } catch (Exception e) {
+ if (!relaunch || (context.getBundle().getState() & Bundle.ACTIVE) == 0)
+ throw e;
+ if (log != null)
+ log.log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, EclipseAdaptorMsg.ECLIPSE_STARTUP_APP_ERROR, 1, e, null));
+ }
+ doRelaunch = (relaunch && (context.getBundle().getState() & Bundle.ACTIVE) != 0) || FrameworkProperties.getProperty(Constants.PROP_OSGI_RELAUNCH) != null;
+ } while (doRelaunch);
+ return result;
+ }
+
+ /*
+ * Waits for an application to be launched and the runs the application on the
+ * current thread (main).
+ */
+ private Object runApplication(Object defaultContext) throws Exception {
+ // wait for an application to be launched.
+ waitForAppLock.acquire();
+ // an application is ready; acquire the running lock.
+ // this must happen after we have acquired an application (by acquiring waitForAppLock above).
+ runningLock.acquire();
+ if (EclipseStarter.debug) {
+ String timeString = FrameworkProperties.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$
+ }
+ if (Profile.PROFILE && (Profile.STARTUP || Profile.BENCHMARK))
+ Profile.logTime("EclipseStarter.run(Object)()", "framework initialized! starting application..."); //$NON-NLS-1$ //$NON-NLS-2$
+ try {
+ // run the actual application on the current thread (main).
+ return runnable.run(appContext != null ? appContext : defaultContext);
+ } finally {
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logExit("EclipseStarter.run(Object)()"); //$NON-NLS-1$
+ // free the runnable application and release the lock to allow another app to be launched.
+ runnable = null;
+ appContext = null;
+ runningLock.release();
+ }
+ }
+
+ public void launch(ParameterizedRunnable app, Object applicationContext) {
+ waitForAppLock.acquire(-1); // clear out any pending apps notifications
+ if (!runningLock.acquire(-1)) // check to see if an application is currently running
+ throw new IllegalStateException("An application is aready running."); //$NON-NLS-1$
+ this.runnable = app;
+ this.appContext = applicationContext;
+ waitForAppLock.release(); // notify the main thread to launch an application.
+ runningLock.release(); // release the running lock
+ }
+
+ public void shutdown() {
+ // this method will aquire and keep the runningLock to prevent
+ // all future application launches.
+ if (runningLock.acquire(-1))
+ return; // no application is currently running.
+ ParameterizedRunnable currentRunnable = runnable;
+ if (currentRunnable instanceof ApplicationRunnable) {
+ ((ApplicationRunnable) currentRunnable).stop();
+ runningLock.acquire(60000); // timeout after 1 minute.
+ }
+ }
+
+ /*
+ * Similar to the start method this method will restart the default method on current thread.
+ * This method assumes that the default application was launched at least once and that an ApplicationDescriptor
+ * exists that can be used to relaunch the default application.
+ */
+ public Object reStart(Object argument) throws Exception {
+ ServiceReference<?> ref[] = null;
+ ref = context.getServiceReferences("org.osgi.service.application.ApplicationDescriptor", "(eclipse.application.default=true)"); //$NON-NLS-1$//$NON-NLS-2$
+ if (ref != null && ref.length > 0) {
+ Object defaultApp = context.getService(ref[0]);
+ Method launch = defaultApp.getClass().getMethod("launch", new Class[] {Map.class}); //$NON-NLS-1$
+ launch.invoke(defaultApp, new Object[] {null});
+ return start(argument);
+ }
+ throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_ERROR_NO_APPLICATION);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseClassLoadingHook.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseClassLoadingHook.java
new file mode 100644
index 000000000..c24307844
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseClassLoadingHook.java
@@ -0,0 +1,243 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.File;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import org.eclipse.osgi.baseadaptor.*;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleFile;
+import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingHook;
+import org.eclipse.osgi.baseadaptor.loader.*;
+import org.eclipse.osgi.framework.adaptor.BundleProtectionDomain;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.internal.baseadaptor.BaseClassLoadingHook;
+import org.eclipse.osgi.internal.baseadaptor.BaseStorageHook;
+
+public class EclipseClassLoadingHook implements ClassLoadingHook, HookConfigurator {
+ private static String[] NL_JAR_VARIANTS = buildNLJarVariants(EclipseEnvironmentInfo.getDefault().getNL());
+ private static boolean DEFINE_PACKAGES;
+ private final static boolean DEFINE_PACKAGE_ATTRIBUTES = !"noattributes".equals(FrameworkProperties.getProperty("osgi.classloader.define.packages")); //$NON-NLS-1$ //$NON-NLS-2$
+ private static String[] LIB_VARIANTS = buildLibraryVariants();
+ private Object pkgLock = new Object();
+
+ static {
+ try {
+ Class.forName("java.lang.Package"); //$NON-NLS-1$
+ DEFINE_PACKAGES = true;
+ } catch (ClassNotFoundException e) {
+ DEFINE_PACKAGES = false;
+ }
+ }
+
+ private static String[] buildLibraryVariants() {
+ List<String> result = new ArrayList<String>();
+ EclipseEnvironmentInfo info = EclipseEnvironmentInfo.getDefault();
+ result.add("ws/" + info.getWS() + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ result.add("os/" + info.getOS() + "/" + info.getOSArch() + "/"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ result.add("os/" + info.getOS() + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ String nl = info.getNL();
+ nl = nl.replace('_', '/');
+ while (nl.length() > 0) {
+ result.add("nl/" + nl + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ int i = nl.lastIndexOf('/');
+ nl = (i < 0) ? "" : nl.substring(0, i); //$NON-NLS-1$
+ }
+ result.add(""); //$NON-NLS-1$
+ return result.toArray(new String[result.size()]);
+ }
+
+ public byte[] processClass(String name, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry, ClasspathManager manager) {
+ if (!DEFINE_PACKAGES)
+ return null;
+ // Define the package if it is not the default package.
+ int lastIndex = name.lastIndexOf('.');
+ if (lastIndex < 0)
+ return null;
+ String packageName = name.substring(0, lastIndex);
+ Object pkg;
+ synchronized (pkgLock) {
+ pkg = manager.getBaseClassLoader().publicGetPackage(packageName);
+ if (pkg != null)
+ return null;
+ }
+
+ // get info about the package from the classpath entry's manifest.
+ String specTitle = null, specVersion = null, specVendor = null, implTitle = null, implVersion = null, implVendor = null;
+
+ if (DEFINE_PACKAGE_ATTRIBUTES) {
+ ClasspathManifest cpm = (ClasspathManifest) classpathEntry.getUserObject(ClasspathManifest.KEY);
+ if (cpm == null) {
+ cpm = new ClasspathManifest();
+ classpathEntry.addUserObject(cpm);
+ }
+ Manifest mf = cpm.getManifest(classpathEntry, manager);
+ if (mf != null) {
+ Attributes mainAttributes = mf.getMainAttributes();
+ String dirName = packageName.replace('.', '/') + '/';
+ Attributes packageAttributes = mf.getAttributes(dirName);
+ boolean noEntry = false;
+ if (packageAttributes == null) {
+ noEntry = true;
+ packageAttributes = mainAttributes;
+ }
+ specTitle = packageAttributes.getValue(Attributes.Name.SPECIFICATION_TITLE);
+ if (specTitle == null && !noEntry)
+ specTitle = mainAttributes.getValue(Attributes.Name.SPECIFICATION_TITLE);
+ specVersion = packageAttributes.getValue(Attributes.Name.SPECIFICATION_VERSION);
+ if (specVersion == null && !noEntry)
+ specVersion = mainAttributes.getValue(Attributes.Name.SPECIFICATION_VERSION);
+ specVendor = packageAttributes.getValue(Attributes.Name.SPECIFICATION_VENDOR);
+ if (specVendor == null && !noEntry)
+ specVendor = mainAttributes.getValue(Attributes.Name.SPECIFICATION_VENDOR);
+ implTitle = packageAttributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
+ if (implTitle == null && !noEntry)
+ implTitle = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
+ implVersion = packageAttributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
+ if (implVersion == null && !noEntry)
+ implVersion = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
+ implVendor = packageAttributes.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
+ if (implVendor == null && !noEntry)
+ implVendor = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
+ }
+ }
+
+ // The package is not defined yet define it before we define the class.
+ // TODO still need to seal packages.
+ synchronized (pkgLock) {
+ pkg = manager.getBaseClassLoader().publicGetPackage(packageName);
+ if (pkg != null)
+ return null;
+ manager.getBaseClassLoader().publicDefinePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, null);
+ }
+ // not doing any byte processing
+ return null;
+ }
+
+ public boolean addClassPathEntry(ArrayList<ClasspathEntry> cpEntries, String cp, ClasspathManager hostmanager, BaseData sourcedata, ProtectionDomain sourcedomain) {
+ String var = hasPrefix(cp);
+ if (var != null)
+ // find internal library using eclipse predefined vars
+ return addInternalClassPath(var, cpEntries, cp, hostmanager, sourcedata, sourcedomain);
+ if (cp.startsWith(BaseStorageHook.EXTERNAL_LIB_PREFIX)) {
+ cp = cp.substring(BaseStorageHook.EXTERNAL_LIB_PREFIX.length());
+ // find external library using system property substitution
+ ClasspathEntry cpEntry = hostmanager.getExternalClassPath(BaseStorageHook.substituteVars(cp), sourcedata, sourcedomain);
+ if (cpEntry != null) {
+ cpEntries.add(cpEntry);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean addInternalClassPath(String var, ArrayList<ClasspathEntry> cpEntries, String cp, ClasspathManager hostloader, BaseData sourcedata, ProtectionDomain sourcedomain) {
+ if (var.equals("ws")) //$NON-NLS-1$
+ return ClasspathManager.addClassPathEntry(cpEntries, "ws/" + EclipseEnvironmentInfo.getDefault().getWS() + cp.substring(4), hostloader, sourcedata, sourcedomain); //$NON-NLS-1$
+ if (var.equals("os")) //$NON-NLS-1$
+ return ClasspathManager.addClassPathEntry(cpEntries, "os/" + EclipseEnvironmentInfo.getDefault().getOS() + cp.substring(4), hostloader, sourcedata, sourcedomain); //$NON-NLS-1$
+ if (var.equals("nl")) { //$NON-NLS-1$
+ cp = cp.substring(4);
+ for (int i = 0; i < NL_JAR_VARIANTS.length; i++)
+ if (ClasspathManager.addClassPathEntry(cpEntries, "nl/" + NL_JAR_VARIANTS[i] + cp, hostloader, sourcedata, sourcedomain)) //$NON-NLS-1$
+ return true;
+ }
+ return false;
+ }
+
+ //return a String representing the string found between the $s
+ private static String hasPrefix(String libPath) {
+ if (libPath.startsWith("$ws$")) //$NON-NLS-1$
+ return "ws"; //$NON-NLS-1$
+ if (libPath.startsWith("$os$")) //$NON-NLS-1$
+ return "os"; //$NON-NLS-1$
+ if (libPath.startsWith("$nl$")) //$NON-NLS-1$
+ return "nl"; //$NON-NLS-1$
+ return null;
+ }
+
+ private static String[] buildNLJarVariants(String nl) {
+ List<String> result = new ArrayList<String>();
+ nl = nl.replace('_', '/');
+ while (nl.length() > 0) {
+ result.add("nl/" + nl + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ int i = nl.lastIndexOf('/');
+ nl = (i < 0) ? "" : nl.substring(0, i); //$NON-NLS-1$
+ }
+ result.add(""); //$NON-NLS-1$
+ return result.toArray(new String[result.size()]);
+ }
+
+ public void recordClassDefine(String name, Class<?> clazz, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry, ClasspathManager manager) {
+ // do nothing
+ }
+
+ public String findLibrary(BaseData data, String libName) {
+ if (libName.length() == 0)
+ return null;
+ if (libName.charAt(0) == '/' || libName.charAt(0) == '\\')
+ libName = libName.substring(1);
+ String mappedLibName = System.mapLibraryName(libName);
+ String result = searchVariants(data, mappedLibName);
+ if (result != null)
+ return result;
+ String[] mappedLibNames = BaseClassLoadingHook.mapLibraryNames(mappedLibName);
+ for (int i = 0; i < mappedLibNames.length && result == null; i++)
+ result = searchVariants(data, mappedLibNames[i]);
+ return result;
+ }
+
+ private String searchVariants(BaseData bundledata, String path) {
+ for (int i = 0; i < LIB_VARIANTS.length; i++) {
+ BundleFile baseBundleFile = bundledata.getBundleFile();
+ BundleEntry libEntry = baseBundleFile.getEntry(LIB_VARIANTS[i] + path);
+ if (libEntry != null) {
+ File libFile = baseBundleFile.getFile(LIB_VARIANTS[i] + path, true);
+ if (libFile == null)
+ return null;
+ // see bug 88697 - HP requires libraries to have executable permissions
+ if (org.eclipse.osgi.service.environment.Constants.OS_HPUX.equals(EclipseEnvironmentInfo.getDefault().getOS())) {
+ try {
+ // use the string array method in case there is a space in the path
+ Runtime.getRuntime().exec(new String[] {"chmod", "755", libFile.getAbsolutePath()}).waitFor(); //$NON-NLS-1$ //$NON-NLS-2$
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return libFile.getAbsolutePath();
+ }
+ }
+ return null;
+ }
+
+ public ClassLoader getBundleClassLoaderParent() {
+ return null; // do nothing
+ }
+
+ public void addHooks(HookRegistry hookRegistry) {
+ hookRegistry.addClassLoadingHook(this);
+ }
+
+ public BaseClassLoader createClassLoader(ClassLoader parent, ClassLoaderDelegate delegate, BundleProtectionDomain domain, BaseData data, String[] bundleclasspath) {
+ // do nothing
+ return null;
+ }
+
+ public void initializedClassLoader(BaseClassLoader baseClassLoader, BaseData data) {
+ // do nothing
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseEnvironmentInfo.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseEnvironmentInfo.java
new file mode 100644
index 000000000..0d091d7f4
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseEnvironmentInfo.java
@@ -0,0 +1,243 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.util.*;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.service.environment.Constants;
+import org.eclipse.osgi.service.environment.EnvironmentInfo;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Internal class.
+ */
+public class EclipseEnvironmentInfo implements EnvironmentInfo {
+ private static EclipseEnvironmentInfo singleton;
+ private static String nl;
+ private static String os;
+ private static String ws;
+ private static String arch;
+ private volatile static String[] allArgs;
+ private volatile static String[] frameworkArgs;
+ private volatile static String[] appArgs;
+
+ // While we recognize the SunOS operating system, we change
+ // this internally to be Solaris.
+ private static final String INTERNAL_OS_SUNOS = "SunOS"; //$NON-NLS-1$
+ private static final String INTERNAL_OS_LINUX = "Linux"; //$NON-NLS-1$
+ private static final String INTERNAL_OS_MACOSX = "Mac OS"; //$NON-NLS-1$
+ private static final String INTERNAL_OS_AIX = "AIX"; //$NON-NLS-1$
+ private static final String INTERNAL_OS_HPUX = "HP-UX"; //$NON-NLS-1$
+ private static final String INTERNAL_OS_QNX = "QNX"; //$NON-NLS-1$
+ private static final String INTERNAL_OS_OS400 = "OS/400"; //$NON-NLS-1$
+ private static final String INTERNAL_OS_OS390 = "OS/390"; //$NON-NLS-1$
+ private static final String INTERNAL_OS_ZOS = "z/OS"; //$NON-NLS-1$
+
+ // While we recognize the i386 architecture, we change
+ // this internally to be x86.
+ private static final String INTERNAL_ARCH_I386 = "i386"; //$NON-NLS-1$
+ // While we recognize the amd64 architecture, we change
+ // this internally to be x86_64.
+ private static final String INTERNAL_AMD64 = "amd64"; //$NON-NLS-1$
+
+ private EclipseEnvironmentInfo() {
+ super();
+ setupSystemContext();
+ }
+
+ public static EclipseEnvironmentInfo getDefault() {
+ if (singleton == null)
+ singleton = new EclipseEnvironmentInfo();
+ return singleton;
+ }
+
+ public boolean inDevelopmentMode() {
+ return FrameworkProperties.getProperty("osgi.dev") != null; //$NON-NLS-1$
+ }
+
+ public boolean inDebugMode() {
+ return FrameworkProperties.getProperty("osgi.debug") != null; //$NON-NLS-1$
+ }
+
+ public String[] getCommandLineArgs() {
+ return allArgs;
+ }
+
+ public String[] getFrameworkArgs() {
+ return frameworkArgs;
+ }
+
+ public String[] getNonFrameworkArgs() {
+ return appArgs;
+ }
+
+ public String getOSArch() {
+ return arch;
+ }
+
+ public String getNL() {
+ return nl;
+ }
+
+ public String getOS() {
+ return os;
+ }
+
+ public String getWS() {
+ return ws;
+ }
+
+ /**
+ * Initializes the execution context for this run of the platform. The context
+ * includes information about the locale, operating system and window system.
+ *
+ * NOTE: The OS, WS, and ARCH values should never be null. The executable should
+ * be setting these values and therefore this code path is obsolete for Eclipse
+ * when run from the executable.
+ */
+ private static void setupSystemContext() {
+ // if the user didn't set the locale with a command line argument then use the default.
+ nl = FrameworkProperties.getProperty("osgi.nl"); //$NON-NLS-1$
+ if (nl != null) {
+ StringTokenizer tokenizer = new StringTokenizer(nl, "_"); //$NON-NLS-1$
+ int segments = tokenizer.countTokens();
+ try {
+ Locale userLocale = null;
+ switch (segments) {
+ case 1 :
+ // use the 2 arg constructor to maintain compatibility with 1.3.1
+ userLocale = new Locale(tokenizer.nextToken(), ""); //$NON-NLS-1$
+ break;
+ case 2 :
+ userLocale = new Locale(tokenizer.nextToken(), tokenizer.nextToken());
+ break;
+ case 3 :
+ userLocale = new Locale(tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken());
+ break;
+ default :
+ // if the user passed us in a bogus value then log a message and use the default
+ System.err.println(NLS.bind(EclipseAdaptorMsg.error_badNL, nl));
+ userLocale = Locale.getDefault();
+ break;
+ }
+ Locale.setDefault(userLocale);
+ // TODO what the heck is this for?? why not just use osgi.nl
+ FrameworkProperties.setProperty("osgi.nl.user", nl); //$NON-NLS-1$
+ } catch (NoSuchElementException e) {
+ // fall through and use the default
+ }
+ }
+ nl = Locale.getDefault().toString();
+ FrameworkProperties.setProperty("osgi.nl", nl); //$NON-NLS-1$
+
+ // if the user didn't set the operating system with a command line
+ // argument then use the default.
+ os = FrameworkProperties.getProperty("osgi.os"); //$NON-NLS-1$
+ if (os == null) {
+ os = guessOS(FrameworkProperties.getProperty("os.name"));//$NON-NLS-1$);
+ FrameworkProperties.setProperty("osgi.os", os); //$NON-NLS-1$
+ }
+
+ // if the user didn't set the window system with a command line
+ // argument then use the default.
+ ws = FrameworkProperties.getProperty("osgi.ws"); //$NON-NLS-1$
+ if (ws == null) {
+ ws = guessWS(os);
+ FrameworkProperties.setProperty("osgi.ws", ws); //$NON-NLS-1$
+ }
+
+ // if the user didn't set the system architecture with a command line
+ // argument then use the default.
+ arch = FrameworkProperties.getProperty("osgi.arch"); //$NON-NLS-1$
+ if (arch == null) {
+ String name = FrameworkProperties.getProperty("os.arch");//$NON-NLS-1$
+ // Map i386 architecture to x86
+ if (name.equalsIgnoreCase(INTERNAL_ARCH_I386))
+ arch = Constants.ARCH_X86;
+ // Map amd64 architecture to x86_64
+ else if (name.equalsIgnoreCase(INTERNAL_AMD64))
+ arch = Constants.ARCH_X86_64;
+ else
+ arch = name;
+ FrameworkProperties.setProperty("osgi.arch", arch); //$NON-NLS-1$
+ }
+ }
+
+ public static void setAllArgs(String[] allArgs) {
+ // do not check if this is set already to allow arguments to change when multiple applications are launched
+ EclipseEnvironmentInfo.allArgs = allArgs;
+ }
+
+ public static void setAppArgs(String[] appArgs) {
+ // do not check if this is set already to allow arguments to change when multiple applications are launched
+ EclipseEnvironmentInfo.appArgs = appArgs;
+ }
+
+ public static void setFrameworkArgs(String[] frameworkArgs) {
+ // do not check if this is set already to allow arguments to change when multiple applications are launched
+ EclipseEnvironmentInfo.frameworkArgs = frameworkArgs;
+ }
+
+ public static String guessWS(String osName) {
+ // setup default values for known OSes if nothing was specified
+ if (osName.equals(Constants.OS_WIN32))
+ return Constants.WS_WIN32;
+ if (osName.equals(Constants.OS_LINUX))
+ return Constants.WS_GTK;
+ if (osName.equals(Constants.OS_MACOSX))
+ return Constants.WS_COCOA;
+ if (osName.equals(Constants.OS_HPUX))
+ return Constants.WS_MOTIF;
+ if (osName.equals(Constants.OS_AIX))
+ return Constants.WS_MOTIF;
+ if (osName.equals(Constants.OS_SOLARIS))
+ return Constants.WS_GTK;
+ if (osName.equals(Constants.OS_QNX))
+ return Constants.WS_PHOTON;
+ return Constants.WS_UNKNOWN;
+ }
+
+ public static String guessOS(String osName) {
+ // check to see if the OS name is "Windows 98" or some other
+ // flavour which should be converted to win32.
+ if (osName.regionMatches(true, 0, Constants.OS_WIN32, 0, 3))
+ return Constants.OS_WIN32;
+ // EXCEPTION: All mappings of SunOS convert to Solaris
+ if (osName.equalsIgnoreCase(INTERNAL_OS_SUNOS))
+ return Constants.OS_SOLARIS;
+ if (osName.equalsIgnoreCase(INTERNAL_OS_LINUX))
+ return Constants.OS_LINUX;
+ if (osName.equalsIgnoreCase(INTERNAL_OS_QNX))
+ return Constants.OS_QNX;
+ if (osName.equalsIgnoreCase(INTERNAL_OS_AIX))
+ return Constants.OS_AIX;
+ if (osName.equalsIgnoreCase(INTERNAL_OS_HPUX))
+ return Constants.OS_HPUX;
+ if (osName.equalsIgnoreCase(INTERNAL_OS_OS400))
+ return Constants.OS_OS400;
+ if (osName.equalsIgnoreCase(INTERNAL_OS_OS390))
+ return Constants.OS_OS390;
+ if (osName.equalsIgnoreCase(INTERNAL_OS_ZOS))
+ return Constants.OS_ZOS;
+ // os.name on Mac OS can be either Mac OS or Mac OS X
+ if (osName.regionMatches(true, 0, INTERNAL_OS_MACOSX, 0, INTERNAL_OS_MACOSX.length()))
+ return Constants.OS_MACOSX;
+ return Constants.OS_UNKNOWN;
+ }
+
+ public String getProperty(String key) {
+ return FrameworkProperties.getProperty(key);
+ }
+
+ public String setProperty(String key, String value) {
+ return FrameworkProperties.setProperty(key, value);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseErrorHandler.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseErrorHandler.java
new file mode 100644
index 000000000..afad88113
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseErrorHandler.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.IOException;
+import java.net.URLConnection;
+import java.util.Properties;
+import org.eclipse.osgi.baseadaptor.*;
+import org.eclipse.osgi.baseadaptor.hooks.AdaptorHook;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+public class EclipseErrorHandler implements AdaptorHook, HookConfigurator {
+ // System property used to prevent VM exit when unexpected errors occur
+ private static final String PROP_EXITONERROR = "eclipse.exitOnError"; //$NON-NLS-1$
+ private BaseAdaptor adaptor;
+
+ /**
+ * @throws BundleException
+ */
+ public void frameworkStart(BundleContext context) throws BundleException {
+ // do nothing
+ }
+
+ /**
+ * @throws BundleException
+ */
+ public void frameworkStop(BundleContext context) throws BundleException {
+ // do nothing
+ }
+
+ public void frameworkStopping(BundleContext context) {
+ // do nothing
+ }
+
+ public void addProperties(Properties properties) {
+ // do nothing
+ }
+
+ /**
+ * @throws IOException
+ */
+ public URLConnection mapLocationToURLConnection(String location) throws IOException {
+ // do nothing
+ return null;
+ }
+
+ private boolean isFatalException(Throwable error) {
+ if (error instanceof VirtualMachineError) {
+ return true;
+ }
+ if (error instanceof ThreadDeath) {
+ return true;
+ }
+ return false;
+ }
+
+ public void handleRuntimeError(Throwable error) {
+ // this is the important method to handle errors
+ boolean exitOnError = false;
+ try {
+ // check the prop each time this happens (should NEVER happen!)
+ exitOnError = Boolean.valueOf(FrameworkProperties.getProperty(EclipseErrorHandler.PROP_EXITONERROR, "true")).booleanValue(); //$NON-NLS-1$
+ String message = EclipseAdaptorMsg.ECLIPSE_ADAPTOR_RUNTIME_ERROR;
+ if (exitOnError && isFatalException(error))
+ message += ' ' + EclipseAdaptorMsg.ECLIPSE_ADAPTOR_EXITING;
+ FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, error, null);
+ adaptor.getFrameworkLog().log(logEntry);
+ } catch (Throwable t) {
+ // we may be in a currupted state and must be able to handle any
+ // errors (ie OutOfMemoryError)
+ // that may occur when handling the first error; this is REALLY the
+ // last resort.
+ try {
+ error.printStackTrace();
+ t.printStackTrace();
+ } catch (Throwable t1) {
+ // if we fail that then we are beyond help.
+ }
+ } finally {
+ // do the exit outside the try block just incase another runtime
+ // error was thrown while logging
+ if (exitOnError && isFatalException(error))
+ System.exit(13);
+ }
+ }
+
+ public void addHooks(HookRegistry hookRegistry) {
+ hookRegistry.addAdaptorHook(this);
+ }
+
+ public FrameworkLog createFrameworkLog() {
+ // do nothing
+ return null;
+ }
+
+ public void initialize(BaseAdaptor initAdaptor) {
+ this.adaptor = initAdaptor;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLazyStarter.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLazyStarter.java
new file mode 100644
index 000000000..e1c34d4df
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLazyStarter.java
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.*;
+import org.eclipse.osgi.baseadaptor.*;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.hooks.AdaptorHook;
+import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingStatsHook;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathEntry;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathManager;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.adaptor.StatusException;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.internal.core.*;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.StateHelper;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+
+public class EclipseLazyStarter implements ClassLoadingStatsHook, AdaptorHook, HookConfigurator {
+ private static final boolean throwErrorOnFailedStart = "true".equals(FrameworkProperties.getProperty("osgi.compatibility.errorOnFailedStart", "true")); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+ private BaseAdaptor adaptor;
+ // holds the current activation trigger class and the ClasspathManagers that need to be activated
+ private ThreadLocal<List<Object>> activationStack = new ThreadLocal<List<Object>>();
+ // used to store exceptions that occurred while activating a bundle
+ // keyed by ClasspathManager->Exception
+ // WeakHashMap is used to prevent pinning the ClasspathManager objects.
+ private final Map<ClasspathManager, TerminatingClassNotFoundException> errors = Collections.synchronizedMap(new WeakHashMap<ClasspathManager, TerminatingClassNotFoundException>());
+
+ public void preFindLocalClass(String name, ClasspathManager manager) throws ClassNotFoundException {
+ AbstractBundle bundle = (AbstractBundle) manager.getBaseData().getBundle();
+ // If the bundle is active, uninstalled or stopping then the bundle has already
+ // been initialized (though it may have been destroyed) so just return the class.
+ if ((bundle.getState() & (Bundle.ACTIVE | Bundle.UNINSTALLED | Bundle.STOPPING)) != 0)
+ return;
+ EclipseStorageHook storageHook = (EclipseStorageHook) manager.getBaseData().getStorageHook(EclipseStorageHook.KEY);
+ // The bundle is not active and does not require activation, just return the class
+ if (!shouldActivateFor(name, manager.getBaseData(), storageHook, manager))
+ return;
+ List<Object> stack = activationStack.get();
+ if (stack == null) {
+ stack = new ArrayList<Object>(6);
+ activationStack.set(stack);
+ }
+ // the first element in the stack is the name of the trigger class,
+ // each element after the trigger class is a classpath manager
+ // that must be activated after the trigger class has been defined (see postFindLocalClass)
+ int size = stack.size();
+ if (size > 1) {
+ for (int i = size - 1; i >= 1; i--)
+ if (manager == stack.get(i))
+ // the manager is already on the stack in which case we are already in the process of loading the trigger class
+ return;
+ }
+ Thread threadChangingState = bundle.getStateChanging();
+ if (bundle.getState() == Bundle.STARTING && threadChangingState == Thread.currentThread())
+ return; // this thread is starting the bundle already
+ if (size == 0)
+ stack.add(name);
+ stack.add(manager);
+ }
+
+ public void postFindLocalClass(String name, Class<?> clazz, ClasspathManager manager) throws ClassNotFoundException {
+ List<Object> stack = activationStack.get();
+ if (stack == null)
+ return;
+ int size = stack.size();
+ if (size <= 1 || stack.get(0) != name)
+ return;
+ // if we have a stack we must clear it even if (clazz == null)
+ ClasspathManager[] managers = null;
+ managers = new ClasspathManager[size - 1];
+ for (int i = 1; i < size; i++)
+ managers[i - 1] = (ClasspathManager) stack.get(i);
+ stack.clear();
+ if (clazz == null)
+ return;
+ for (int i = managers.length - 1; i >= 0; i--) {
+ if (errors.get(managers[i]) != null) {
+ if (throwErrorOnFailedStart)
+ throw errors.get(managers[i]);
+ continue;
+ }
+
+ // The bundle must be started.
+ // Note that another thread may already be starting this bundle;
+ // In this case we will timeout after a default of 5 seconds and record the BundleException
+ long startTime = System.currentTimeMillis();
+ try {
+ // do not persist the start of this bundle
+ managers[i].getBaseClassLoader().getDelegate().setLazyTrigger();
+ } catch (BundleException e) {
+ Bundle bundle = managers[i].getBaseData().getBundle();
+ Throwable cause = e.getCause();
+ if (cause != null && cause instanceof StatusException) {
+ StatusException status = (StatusException) cause;
+ if ((status.getStatusCode() & StatusException.CODE_ERROR) == 0) {
+ if (status.getStatus() instanceof Thread) {
+ String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_CONCURRENT_STARTUP, new Object[] {Thread.currentThread(), name, status.getStatus(), bundle, new Long(System.currentTimeMillis() - startTime)});
+ adaptor.getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.WARNING, 0, message, 0, e, null));
+ }
+ continue;
+ }
+ }
+ String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_ACTIVATION, bundle.getSymbolicName(), Long.toString(bundle.getBundleId()));
+ TerminatingClassNotFoundException error = new TerminatingClassNotFoundException(message, e);
+ errors.put(managers[i], error);
+ if (throwErrorOnFailedStart) {
+ adaptor.getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, e, null));
+ throw error;
+ }
+ adaptor.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundle, new BundleException(message, e));
+ }
+ }
+ }
+
+ public void preFindLocalResource(String name, ClasspathManager manager) {
+ // do nothing
+ }
+
+ public void postFindLocalResource(String name, URL resource, ClasspathManager manager) {
+ // do nothing
+ }
+
+ public void recordClassDefine(String name, Class<?> clazz, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry, ClasspathManager manager) {
+ // do nothing
+ }
+
+ private boolean shouldActivateFor(String className, BaseData bundledata, EclipseStorageHook storageHook, ClasspathManager manager) throws ClassNotFoundException {
+ if (!isLazyStartable(className, bundledata, storageHook))
+ return false;
+ //if (manager.getBaseClassLoader().getDelegate().isLazyTriggerSet())
+ // return false;
+ // Don't activate non-starting bundles
+ if (bundledata.getBundle().getState() == Bundle.RESOLVED) {
+ if (throwErrorOnFailedStart) {
+ TerminatingClassNotFoundException error = errors.get(manager);
+ if (error != null)
+ throw error;
+ }
+ return (bundledata.getStatus() & Constants.BUNDLE_STARTED) != 0;
+ }
+ return true;
+ }
+
+ private boolean isLazyStartable(String className, BaseData bundledata, EclipseStorageHook storageHook) {
+ if (storageHook == null)
+ return false;
+ boolean lazyStart = storageHook.isLazyStart();
+ String[] excludes = storageHook.getLazyStartExcludes();
+ String[] includes = storageHook.getLazyStartIncludes();
+ // no exceptions, it is easy to figure it out
+ if (excludes == null && includes == null)
+ return lazyStart;
+ // otherwise, we need to check if the package is in the exceptions list
+ int dotPosition = className.lastIndexOf('.');
+ // the class has no package name... no exceptions apply
+ if (dotPosition == -1)
+ return lazyStart;
+ String packageName = className.substring(0, dotPosition);
+ if (lazyStart)
+ return ((includes == null || contains(includes, packageName)) && (excludes == null || !contains(excludes, packageName)));
+ return (excludes != null && contains(excludes, packageName));
+ }
+
+ private boolean contains(String[] array, String element) {
+ for (int i = 0; i < array.length; i++)
+ if (array[i].equals(element))
+ return true;
+ return false;
+ }
+
+ public void addHooks(HookRegistry hookRegistry) {
+ hookRegistry.addClassLoadingStatsHook(this);
+ hookRegistry.addAdaptorHook(this);
+ }
+
+ public void addProperties(Properties properties) {
+ // do nothing
+ }
+
+ public FrameworkLog createFrameworkLog() {
+ // do nothing
+ return null;
+ }
+
+ /**
+ * @throws BundleException
+ */
+ public void frameworkStart(BundleContext context) throws BundleException {
+ // nothing
+ }
+
+ /**
+ * @throws BundleException
+ */
+ public void frameworkStop(BundleContext context) throws BundleException {
+ // nothing
+ }
+
+ public void frameworkStopping(BundleContext context) {
+ if (!Debug.DEBUG_ENABLED)
+ return;
+
+ BundleDescription[] allBundles = adaptor.getState().getResolvedBundles();
+ StateHelper stateHelper = adaptor.getPlatformAdmin().getStateHelper();
+ Object[][] cycles = stateHelper.sortBundles(allBundles);
+ logCycles(cycles);
+ }
+
+ public void handleRuntimeError(Throwable error) {
+ // do nothing
+
+ }
+
+ public void initialize(BaseAdaptor baseAdaptor) {
+ this.adaptor = baseAdaptor;
+ }
+
+ /**
+ * @throws IOException
+ */
+ public URLConnection mapLocationToURLConnection(String location) throws IOException {
+ // do nothing
+ return null;
+ }
+
+ private void logCycles(Object[][] cycles) {
+ // log cycles
+ if (cycles.length > 0) {
+ StringBuffer cycleText = new StringBuffer("["); //$NON-NLS-1$
+ for (int i = 0; i < cycles.length; i++) {
+ cycleText.append('[');
+ for (int j = 0; j < cycles[i].length; j++) {
+ cycleText.append(((BundleDescription) cycles[i][j]).getSymbolicName());
+ cycleText.append(',');
+ }
+ cycleText.insert(cycleText.length() - 1, ']');
+ }
+ cycleText.setCharAt(cycleText.length() - 1, ']');
+ String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_BUNDLESTOPPER_CYCLES_FOUND, cycleText);
+ FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.WARNING, 0, message, 0, null, null);
+ adaptor.getFrameworkLog().log(entry);
+ }
+ }
+
+ private static class TerminatingClassNotFoundException extends ClassNotFoundException implements StatusException {
+ private static final long serialVersionUID = -6730732895632169173L;
+ private Throwable cause;
+
+ public TerminatingClassNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ this.cause = cause;
+ }
+
+ public Object getStatus() {
+ return cause;
+ }
+
+ public int getStatusCode() {
+ return StatusException.CODE_ERROR;
+ }
+
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogFactory.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogFactory.java
new file mode 100644
index 000000000..416bbdada
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogFactory.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.*;
+import org.eclipse.equinox.log.Logger;
+import org.eclipse.equinox.log.internal.LogServiceManager;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.osgi.framework.*;
+import org.osgi.service.log.LogService;
+
+public class EclipseLogFactory implements ServiceFactory<FrameworkLog> {
+ final EclipseLogWriter defaultWriter;
+ final LogServiceManager logManager;
+
+ public EclipseLogFactory(EclipseLogWriter defaultWriter, LogServiceManager logManager) {
+ this.defaultWriter = defaultWriter;
+ this.logManager = logManager;
+ }
+
+ public FrameworkLog getService(final Bundle bundle, ServiceRegistration<FrameworkLog> registration) {
+ return createFrameworkLog(bundle, defaultWriter);
+ }
+
+ FrameworkLog createFrameworkLog(Bundle bundle, EclipseLogWriter eclipseWriter) {
+ final EclipseLogWriter logWriter = eclipseWriter == null ? defaultWriter : eclipseWriter;
+ final Logger logger = bundle == null ? logManager.getSystemBundleLog().getLogger(eclipseWriter.getLoggerName()) : logManager.getSystemBundleLog().getLogger(bundle, logWriter.getLoggerName());
+ return new FrameworkLog() {
+
+ public void setWriter(Writer newWriter, boolean append) {
+ logWriter.setWriter(newWriter, append);
+ }
+
+ public void setFile(File newFile, boolean append) throws IOException {
+ logWriter.setFile(newFile, append);
+ }
+
+ public void setConsoleLog(boolean consoleLog) {
+ logWriter.setConsoleLog(consoleLog);
+ }
+
+ public void log(FrameworkLogEntry logEntry) {
+ logger.log(logEntry, convertLevel(logEntry), logEntry.getMessage(), logEntry.getThrowable());
+ }
+
+ public void log(FrameworkEvent frameworkEvent) {
+ Bundle b = frameworkEvent.getBundle();
+ Throwable t = frameworkEvent.getThrowable();
+ String entry = b.getSymbolicName() == null ? b.getLocation() : b.getSymbolicName();
+ int severity;
+ switch (frameworkEvent.getType()) {
+ case FrameworkEvent.INFO :
+ severity = FrameworkLogEntry.INFO;
+ break;
+ case FrameworkEvent.ERROR :
+ severity = FrameworkLogEntry.ERROR;
+ break;
+ case FrameworkEvent.WARNING :
+ severity = FrameworkLogEntry.WARNING;
+ break;
+ default :
+ severity = FrameworkLogEntry.OK;
+ }
+ FrameworkLogEntry logEntry = new FrameworkLogEntry(entry, severity, 0, "", 0, t, null); //$NON-NLS-1$
+ log(logEntry);
+ }
+
+ public File getFile() {
+ return logWriter.getFile();
+ }
+
+ public void close() {
+ logWriter.close();
+ }
+ };
+ }
+
+ public void ungetService(Bundle bundle, ServiceRegistration<FrameworkLog> registration, FrameworkLog service) {
+ // nothing
+ }
+
+ static int convertLevel(FrameworkLogEntry logEntry) {
+ switch (logEntry.getSeverity()) {
+ case FrameworkLogEntry.ERROR :
+ return LogService.LOG_ERROR;
+ case FrameworkLogEntry.WARNING :
+ return LogService.LOG_WARNING;
+ case FrameworkLogEntry.INFO :
+ return LogService.LOG_INFO;
+ case FrameworkLogEntry.OK :
+ return LogService.LOG_DEBUG;
+ case FrameworkLogEntry.CANCEL :
+ default :
+ return 32; // unknown
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogHook.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogHook.java
new file mode 100644
index 000000000..f4555e6b5
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogHook.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.*;
+import java.net.URLConnection;
+import java.util.*;
+import org.eclipse.core.runtime.adaptor.EclipseStarter;
+import org.eclipse.core.runtime.adaptor.LocationManager;
+import org.eclipse.equinox.log.internal.LogServiceManager;
+import org.eclipse.osgi.baseadaptor.*;
+import org.eclipse.osgi.baseadaptor.hooks.AdaptorHook;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.osgi.internal.baseadaptor.AdaptorUtil;
+import org.eclipse.osgi.service.datalocation.Location;
+import org.osgi.framework.*;
+
+public class EclipseLogHook implements HookConfigurator, AdaptorHook {
+ static final String EQUINOX_LOGGER_NAME = "org.eclipse.equinox.logger"; //$NON-NLS-1$
+ static final String PERF_LOGGER_NAME = "org.eclipse.performance.logger"; //$NON-NLS-1$
+ private static final String PROP_LOG_ENABLED = "eclipse.log.enabled"; //$NON-NLS-1$
+
+ // The eclipse log file extension */
+ private static final String LOG_EXT = ".log"; //$NON-NLS-1$
+ private final LogServiceManager logServiceManager;
+ private final EclipseLogFactory eclipseLogFactory;
+ private final EclipseLogWriter logWriter;
+ private final EclipseLogWriter perfWriter;
+
+ public EclipseLogHook() {
+ String logFileProp = FrameworkProperties.getProperty(EclipseStarter.PROP_LOGFILE);
+ boolean enabled = "true".equals(FrameworkProperties.getProperty(PROP_LOG_ENABLED, "true")); //$NON-NLS-1$ //$NON-NLS-2$
+ if (logFileProp != null) {
+ logWriter = new EclipseLogWriter(new File(logFileProp), EQUINOX_LOGGER_NAME, enabled);
+ } 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()) + EclipseLogHook.LOG_EXT;
+ File logFile = new File(configAreaDirectory, logFileName);
+ FrameworkProperties.setProperty(EclipseStarter.PROP_LOGFILE, logFile.getAbsolutePath());
+ logWriter = new EclipseLogWriter(logFile, EQUINOX_LOGGER_NAME, enabled);
+ } else
+ logWriter = new EclipseLogWriter((Writer) null, EQUINOX_LOGGER_NAME, enabled);
+ }
+
+ File logFile = logWriter.getFile();
+ if (logFile != null) {
+ File perfLogFile = new File(logFile.getParentFile(), "performance.log"); //$NON-NLS-1$
+ perfWriter = new EclipseLogWriter(perfLogFile, PERF_LOGGER_NAME, true);
+ } else {
+ perfWriter = new EclipseLogWriter((Writer) null, PERF_LOGGER_NAME, true);
+ }
+ if ("true".equals(FrameworkProperties.getProperty(EclipseStarter.PROP_CONSOLE_LOG))) //$NON-NLS-1$
+ logWriter.setConsoleLog(true);
+ logServiceManager = new LogServiceManager(logWriter, perfWriter);
+ eclipseLogFactory = new EclipseLogFactory(logWriter, logServiceManager);
+
+ }
+
+ private ServiceRegistration<?> frameworkLogReg;
+ private ServiceRegistration<?> perfLogReg;
+
+ public void addHooks(HookRegistry hookRegistry) {
+ hookRegistry.addAdaptorHook(this);
+ }
+
+ public void initialize(BaseAdaptor initAdaptor) {
+ // Nothing
+ }
+
+ /**
+ * @throws BundleException
+ */
+ public void frameworkStart(BundleContext context) throws BundleException {
+ logServiceManager.start(context);
+ frameworkLogReg = AdaptorUtil.register(FrameworkLog.class.getName(), eclipseLogFactory, context);
+ perfLogReg = registerPerformanceLog(context);
+ }
+
+ /**
+ * @throws BundleException
+ */
+ public void frameworkStop(BundleContext context) throws BundleException {
+ frameworkLogReg.unregister();
+ perfLogReg.unregister();
+ logServiceManager.stop(context);
+ }
+
+ public void frameworkStopping(BundleContext context) {
+ // do nothing
+
+ }
+
+ public void addProperties(Properties properties) {
+ // do nothing
+ }
+
+ /**
+ * @throws IOException
+ */
+ public URLConnection mapLocationToURLConnection(String location) throws IOException {
+ // do nothing
+ return null;
+ }
+
+ public void handleRuntimeError(Throwable error) {
+ // do nothing
+ }
+
+ public FrameworkLog createFrameworkLog() {
+ return eclipseLogFactory.createFrameworkLog(null, logWriter);
+ }
+
+ private ServiceRegistration<?> registerPerformanceLog(BundleContext context) {
+ Object service = createPerformanceLog(context.getBundle());
+ String serviceName = FrameworkLog.class.getName();
+ Dictionary<String, Object> serviceProperties = new Hashtable<String, Object>(7);
+ Dictionary<String, String> headers = context.getBundle().getHeaders();
+
+ serviceProperties.put(Constants.SERVICE_VENDOR, headers.get(Constants.BUNDLE_VENDOR));
+ serviceProperties.put(Constants.SERVICE_RANKING, new Integer(Integer.MIN_VALUE));
+ serviceProperties.put(Constants.SERVICE_PID, context.getBundle().getBundleId() + '.' + service.getClass().getName());
+ serviceProperties.put(FrameworkLog.SERVICE_PERFORMANCE, Boolean.TRUE.toString());
+
+ return context.registerService(serviceName, service, serviceProperties);
+ }
+
+ private FrameworkLog createPerformanceLog(Bundle systemBundle) {
+ return eclipseLogFactory.createFrameworkLog(systemBundle, perfWriter);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogWriter.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogWriter.java
new file mode 100644
index 000000000..36eab8b08
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseLogWriter.java
@@ -0,0 +1,726 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.security.AccessController;
+import java.util.Calendar;
+import java.util.Date;
+import org.eclipse.core.runtime.adaptor.EclipseStarter;
+import org.eclipse.equinox.log.*;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.framework.util.SecureAction;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.service.log.LogEntry;
+import org.osgi.service.log.LogService;
+
+public class EclipseLogWriter implements SynchronousLogListener, LogFilter {
+ private static final String PASSWORD = "-password"; //$NON-NLS-1$
+ /** The session tag */
+ private static final String SESSION = "!SESSION"; //$NON-NLS-1$
+ /** The entry tag */
+ private static final String ENTRY = "!ENTRY"; //$NON-NLS-1$
+ /** The sub-entry tag */
+ private static final String SUBENTRY = "!SUBENTRY"; //$NON-NLS-1$
+ /** The message tag */
+ private static final String MESSAGE = "!MESSAGE"; //$NON-NLS-1$
+ /** The stacktrace tag */
+ private static final String STACK = "!STACK"; //$NON-NLS-1$
+
+ /** The line separator used in the log output */
+ private static final String LINE_SEPARATOR;
+ static {
+ String s = System.getProperty("line.separator"); //$NON-NLS-1$
+ LINE_SEPARATOR = s == null ? "\n" : s; //$NON-NLS-1$
+ }
+ //Constants for rotating log file
+ /** The default size a log file can grow before it is rotated */
+ private static final int DEFAULT_LOG_SIZE = 1000;
+ /** The default number of backup log files */
+ private static final int DEFAULT_LOG_FILES = 10;
+ /** The minimum size limit for log rotation */
+ private static final int LOG_SIZE_MIN = 10;
+
+ /** The system property used to specify the log level */
+ private static final String PROP_LOG_LEVEL = "eclipse.log.level"; //$NON-NLS-1$
+ /** The system property used to specify size a log file can grow before it is rotated */
+ private static final String PROP_LOG_SIZE_MAX = "eclipse.log.size.max"; //$NON-NLS-1$
+ /** The system property used to specify the maximim number of backup log files to use */
+ private static final String PROP_LOG_FILE_MAX = "eclipse.log.backup.max"; //$NON-NLS-1$
+ /** The extension used for log files */
+ private static final String LOG_EXT = ".log"; //$NON-NLS-1$
+ /** The extension markup to use for backup log files*/
+ private static final String BACKUP_MARK = ".bak_"; //$NON-NLS-1$
+
+ /** The system property used to specify command line args should be omitted from the log */
+ private static final String PROP_LOG_INCLUDE_COMMAND_LINE = "eclipse.log.include.commandline"; //$NON-NLS-1$
+ private static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());
+
+ /** Indicates if the console messages should be printed to the console (System.out) */
+ private boolean consoleLog = false;
+ /** Indicates if the next log message is part of a new session */
+ private boolean newSession = true;
+ /**
+ * The File object to store messages. This value may be null.
+ */
+ private File outFile;
+
+ /**
+ * The Writer to log messages to.
+ */
+ private Writer writer;
+
+ private final String loggerName;
+ private final boolean enabled;
+
+ int maxLogSize = DEFAULT_LOG_SIZE; // The value is in KB.
+ int maxLogFiles = DEFAULT_LOG_FILES;
+ int backupIdx = 0;
+
+ private int logLevel = FrameworkLogEntry.OK;
+ private boolean includeCommandLine = true;
+
+ /**
+ * Constructs an EclipseLog which uses the specified File to log messages to
+ * @param outFile a file to log messages to
+ */
+ public EclipseLogWriter(File outFile, String loggerName, boolean enabled) {
+ this.outFile = outFile;
+ this.writer = null;
+ this.loggerName = loggerName;
+ this.enabled = enabled;
+ readLogProperties();
+ }
+
+ /**
+ * Constructs an EclipseLog which uses the specified Writer to log messages to
+ * @param writer a writer to log messages to
+ */
+ public EclipseLogWriter(Writer writer, String loggerName, boolean enabled) {
+ if (writer == null)
+ // log to System.err by default
+ this.writer = logForStream(System.err);
+ else
+ this.writer = writer;
+ this.loggerName = loggerName;
+ this.enabled = enabled;
+ }
+
+ private Throwable getRoot(Throwable t) {
+ Throwable root = null;
+ if (t instanceof BundleException)
+ root = ((BundleException) t).getNestedException();
+ if (t instanceof InvocationTargetException)
+ root = ((InvocationTargetException) t).getTargetException();
+ // skip inner InvocationTargetExceptions and BundleExceptions
+ if (root instanceof InvocationTargetException || root instanceof BundleException) {
+ Throwable deeplyNested = getRoot(root);
+ if (deeplyNested != null)
+ // if we have something more specific, use it, otherwise keep what we have
+ root = deeplyNested;
+ }
+ return root;
+ }
+
+ /**
+ * Helper method for writing out argument arrays.
+ * @param header the header
+ * @param args the list of arguments
+ */
+ private void writeArgs(String header, String[] args) throws IOException {
+ if (args == null || args.length == 0)
+ return;
+ write(header);
+ for (int i = 0; i < args.length; i++) {
+ //mask out the password argument for security
+ if (i > 0 && PASSWORD.equals(args[i - 1]))
+ write(" (omitted)"); //$NON-NLS-1$
+ else
+ write(" " + args[i]); //$NON-NLS-1$
+ }
+ writeln();
+ }
+
+ /**
+ * Returns the session timestamp. This is the time the platform was started
+ * @return the session timestamp
+ */
+ private String getSessionTimestamp() {
+ // Main should have set the session start-up timestamp so return that.
+ // Return the "now" time if not available.
+ String ts = FrameworkProperties.getProperty("eclipse.startTime"); //$NON-NLS-1$
+ if (ts != null) {
+ try {
+ return getDate(new Date(Long.parseLong(ts)));
+ } catch (NumberFormatException e) {
+ // fall through and use the timestamp from right now
+ }
+ }
+ return getDate(new Date());
+ }
+
+ /**
+ * Writes the session
+ * @throws IOException if an error occurs writing to the log
+ */
+ private void writeSession() throws IOException {
+ write(SESSION);
+ writeSpace();
+ String date = getSessionTimestamp();
+ write(date);
+ writeSpace();
+ for (int i = SESSION.length() + date.length(); i < 78; i++) {
+ write("-"); //$NON-NLS-1$
+ }
+ writeln();
+ // Write out certain values found in System.getProperties()
+ try {
+ String key = "eclipse.buildId"; //$NON-NLS-1$
+ String value = FrameworkProperties.getProperty(key, "unknown"); //$NON-NLS-1$
+ writeln(key + "=" + value); //$NON-NLS-1$
+
+ key = "java.fullversion"; //$NON-NLS-1$
+ value = System.getProperty(key);
+ if (value == null) {
+ key = "java.version"; //$NON-NLS-1$
+ value = System.getProperty(key);
+ writeln(key + "=" + value); //$NON-NLS-1$
+ key = "java.vendor"; //$NON-NLS-1$
+ value = System.getProperty(key);
+ writeln(key + "=" + value); //$NON-NLS-1$
+ } else {
+ writeln(key + "=" + value); //$NON-NLS-1$
+ }
+ } catch (Exception e) {
+ // If we're not allowed to get the values of these properties
+ // then just skip over them.
+ }
+ // The Bootloader has some information that we might be interested in.
+ write("BootLoader constants: OS=" + EclipseEnvironmentInfo.getDefault().getOS()); //$NON-NLS-1$
+ write(", ARCH=" + EclipseEnvironmentInfo.getDefault().getOSArch()); //$NON-NLS-1$
+ write(", WS=" + EclipseEnvironmentInfo.getDefault().getWS()); //$NON-NLS-1$
+ writeln(", NL=" + EclipseEnvironmentInfo.getDefault().getNL()); //$NON-NLS-1$
+ // Add the command-line arguments used to invoke the platform
+ // XXX: this includes runtime-private arguments - should we do that?
+ if (includeCommandLine) {
+ writeArgs("Framework arguments: ", EclipseEnvironmentInfo.getDefault().getNonFrameworkArgs()); //$NON-NLS-1$
+ writeArgs("Command-line arguments: ", EclipseEnvironmentInfo.getDefault().getCommandLineArgs()); //$NON-NLS-1$
+ }
+ }
+
+ public void close() {
+ try {
+ if (writer != null) {
+ Writer tmpWriter = writer;
+ writer = null;
+ tmpWriter.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * If a File is used to log messages to then the File opened and a Writer is created
+ * to log messages to.
+ */
+ private void openFile() {
+ if (writer == null) {
+ if (outFile != null) {
+ try {
+ writer = logForStream(secureAction.getFileOutputStream(outFile, true));
+ } catch (IOException e) {
+ writer = logForStream(System.err);
+ }
+ } else {
+ writer = logForStream(System.err);
+ }
+ }
+ }
+
+ /**
+ * If a File is used to log messages to then the writer is closed.
+ */
+ private void closeFile() {
+ if (outFile != null) {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ // we cannot log here; just print the stacktrace.
+ e.printStackTrace();
+ }
+ writer = null;
+ }
+ }
+ }
+
+ private synchronized void log(FrameworkLogEntry logEntry) {
+ if (logEntry == null)
+ return;
+ if (!isLoggable(logEntry.getSeverity()))
+ return;
+ try {
+ checkLogFileSize();
+ openFile();
+ if (newSession) {
+ writeSession();
+ newSession = false;
+ }
+ writeLog(0, logEntry);
+ writer.flush();
+ } catch (Exception e) {
+ // any exceptions during logging should be caught
+ System.err.println("An exception occurred while writing to the platform log:");//$NON-NLS-1$
+ e.printStackTrace(System.err);
+ System.err.println("Logging to the console instead.");//$NON-NLS-1$
+ //we failed to write, so dump log entry to console instead
+ try {
+ writer = logForStream(System.err);
+ writeLog(0, logEntry);
+ writer.flush();
+ } catch (Exception e2) {
+ System.err.println("An exception occurred while logging to the console:");//$NON-NLS-1$
+ e2.printStackTrace(System.err);
+ }
+ } finally {
+ closeFile();
+ }
+ }
+
+ public synchronized void setWriter(Writer newWriter, boolean append) {
+ setOutput(null, newWriter, append);
+ }
+
+ /**
+ * @throws IOException
+ */
+ public synchronized void setFile(File newFile, boolean append) throws IOException {
+ if (newFile != null && !newFile.equals(this.outFile)) {
+ // If it's a new file, then reset.
+ readLogProperties();
+ backupIdx = 0;
+ }
+ setOutput(newFile, null, append);
+ FrameworkProperties.setProperty(EclipseStarter.PROP_LOGFILE, newFile == null ? "" : newFile.getAbsolutePath()); //$NON-NLS-1$
+ }
+
+ public synchronized File getFile() {
+ return outFile;
+ }
+
+ public void setConsoleLog(boolean consoleLog) {
+ this.consoleLog = consoleLog;
+ }
+
+ private void setOutput(File newOutFile, Writer newWriter, boolean append) {
+ if (newOutFile == null || !newOutFile.equals(this.outFile)) {
+ if (this.writer != null) {
+ try {
+ this.writer.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ this.writer = null;
+ }
+ // Append old outFile to newWriter. We only attempt to do this
+ // if the current Writer is backed by a File and this is not
+ // a new session.
+ File oldOutFile = this.outFile;
+ this.outFile = newOutFile;
+ this.writer = newWriter;
+ boolean copyFailed = false;
+ if (append && oldOutFile != null && oldOutFile.isFile()) {
+ Reader fileIn = null;
+ try {
+ openFile();
+ fileIn = new InputStreamReader(secureAction.getFileInputStream(oldOutFile), "UTF-8"); //$NON-NLS-1$
+ copyReader(fileIn, this.writer);
+ } catch (IOException e) {
+ copyFailed = true;
+ e.printStackTrace();
+ } finally {
+ if (fileIn != null) {
+ try {
+ fileIn.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ // delete the old file if copying didn't fail
+ if (!copyFailed)
+ oldOutFile.delete();
+ }
+ closeFile();
+ }
+ }
+ }
+ }
+
+ private void copyReader(Reader reader, Writer aWriter) throws IOException {
+ char buffer[] = new char[1024];
+ int count;
+ while ((count = reader.read(buffer, 0, buffer.length)) > 0) {
+ aWriter.write(buffer, 0, count);
+ }
+ }
+
+ /**
+ * Returns a date string using the correct format for the log.
+ * @param date the Date to format
+ * @return a date string.
+ */
+ private String getDate(Date date) {
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ StringBuffer sb = new StringBuffer();
+ appendPaddedInt(c.get(Calendar.YEAR), 4, sb).append('-');
+ appendPaddedInt(c.get(Calendar.MONTH) + 1, 2, sb).append('-');
+ appendPaddedInt(c.get(Calendar.DAY_OF_MONTH), 2, sb).append(' ');
+ appendPaddedInt(c.get(Calendar.HOUR_OF_DAY), 2, sb).append(':');
+ appendPaddedInt(c.get(Calendar.MINUTE), 2, sb).append(':');
+ appendPaddedInt(c.get(Calendar.SECOND), 2, sb).append('.');
+ appendPaddedInt(c.get(Calendar.MILLISECOND), 3, sb);
+ return sb.toString();
+ }
+
+ private StringBuffer appendPaddedInt(int value, int pad, StringBuffer buffer) {
+ pad = pad - 1;
+ if (pad == 0)
+ return buffer.append(Integer.toString(value));
+ int padding = (int) Math.pow(10, pad);
+ if (value >= padding)
+ return buffer.append(Integer.toString(value));
+ while (padding > value && padding > 1) {
+ buffer.append('0');
+ padding = padding / 10;
+ }
+ buffer.append(value);
+ return buffer;
+ }
+
+ /**
+ * Returns a stacktrace string using the correct format for the log
+ * @param t the Throwable to get the stacktrace for
+ * @return a stacktrace string
+ */
+ private String getStackTrace(Throwable t) {
+ if (t == null)
+ return null;
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+
+ t.printStackTrace(pw);
+ // ensure the root exception is fully logged
+ Throwable root = getRoot(t);
+ if (root != null) {
+ pw.println("Root exception:"); //$NON-NLS-1$
+ root.printStackTrace(pw);
+ }
+ return sw.toString();
+ }
+
+ /**
+ * Returns a Writer for the given OutputStream
+ * @param output an OutputStream to use for the Writer
+ * @return a Writer for the given OutputStream
+ */
+ private Writer logForStream(OutputStream output) {
+ try {
+ return new BufferedWriter(new OutputStreamWriter(output, "UTF-8")); //$NON-NLS-1$
+ } catch (UnsupportedEncodingException e) {
+ return new BufferedWriter(new OutputStreamWriter(output));
+ }
+ }
+
+ /**
+ * Writes the log entry to the log using the specified depth. A depth value of 0
+ * indicates that the log entry is the root entry. Any value greater than 0 indicates
+ * a sub-entry.
+ * @param depth the depth of th entry
+ * @param entry the entry to log
+ * @throws IOException if any error occurs writing to the log
+ */
+ private void writeLog(int depth, FrameworkLogEntry entry) throws IOException {
+ writeEntry(depth, entry);
+ writeMessage(entry);
+ writeStack(entry);
+
+ FrameworkLogEntry[] children = entry.getChildren();
+ if (children != null) {
+ for (int i = 0; i < children.length; i++) {
+ writeLog(depth + 1, children[i]);
+ }
+ }
+ }
+
+ /**
+ * Writes the ENTRY or SUBENTRY header for an entry. A depth value of 0
+ * indicates that the log entry is the root entry. Any value greater than 0 indicates
+ * a sub-entry.
+ * @param depth the depth of th entry
+ * @param entry the entry to write the header for
+ * @throws IOException if any error occurs writing to the log
+ */
+ private void writeEntry(int depth, FrameworkLogEntry entry) throws IOException {
+ if (depth == 0) {
+ writeln(); // write a blank line before all !ENTRY tags bug #64406
+ write(ENTRY);
+ } else {
+ write(SUBENTRY);
+ writeSpace();
+ write(Integer.toString(depth));
+ }
+ writeSpace();
+ write(entry.getEntry());
+ writeSpace();
+ write(Integer.toString(entry.getSeverity()));
+ writeSpace();
+ write(Integer.toString(entry.getBundleCode()));
+ writeSpace();
+ write(getDate(new Date()));
+ writeln();
+ }
+
+ /**
+ * Writes the MESSAGE header to the log for the given entry.
+ * @param entry the entry to write the message for
+ * @throws IOException if any error occurs writing to the log
+ */
+ private void writeMessage(FrameworkLogEntry entry) throws IOException {
+ write(MESSAGE);
+ writeSpace();
+ writeln(entry.getMessage());
+ }
+
+ /**
+ * Writes the STACK header to the log for the given entry.
+ * @param entry the entry to write the stacktrace for
+ * @throws IOException if any error occurs writing to the log
+ */
+ private void writeStack(FrameworkLogEntry entry) throws IOException {
+ Throwable t = entry.getThrowable();
+ if (t != null) {
+ String stack = getStackTrace(t);
+ write(STACK);
+ writeSpace();
+ write(Integer.toString(entry.getStackCode()));
+ writeln();
+ write(stack);
+ }
+ }
+
+ /**
+ * Writes the given message to the log.
+ * @param message the message
+ * @throws IOException if any error occurs writing to the log
+ */
+ private void write(String message) throws IOException {
+ if (message != null) {
+ writer.write(message);
+ if (consoleLog)
+ System.out.print(message);
+ }
+ }
+
+ /**
+ * Writes the given message to the log and a newline.
+ * @param s the message
+ * @throws IOException if any error occurs writing to the log
+ */
+ private void writeln(String s) throws IOException {
+ write(s);
+ writeln();
+ }
+
+ /**
+ * Writes a newline log.
+ * @throws IOException if any error occurs writing to the log
+ */
+ private void writeln() throws IOException {
+ write(LINE_SEPARATOR);
+ }
+
+ /**
+ * Writes a space to the log.
+ * @throws IOException if any error occurs writing to the log
+ */
+ private void writeSpace() throws IOException {
+ write(" "); //$NON-NLS-1$
+ }
+
+ /**
+ * Checks the log file size. If the log file size reaches the limit then the log
+ * is rotated
+ * @return false if an error occured trying to rotate the log
+ */
+ private boolean checkLogFileSize() {
+ if (maxLogSize == 0)
+ return true; // no size limitation.
+
+ boolean isBackupOK = true;
+ if (outFile != null) {
+ if ((secureAction.length(outFile) >> 10) > maxLogSize) { // Use KB as file size unit.
+ String logFilename = outFile.getAbsolutePath();
+
+ // Delete old backup file that will be replaced.
+ String backupFilename = ""; //$NON-NLS-1$
+ if (logFilename.toLowerCase().endsWith(LOG_EXT)) {
+ backupFilename = logFilename.substring(0, logFilename.length() - LOG_EXT.length()) + BACKUP_MARK + backupIdx + LOG_EXT;
+ } else {
+ backupFilename = logFilename + BACKUP_MARK + backupIdx;
+ }
+ File backupFile = new File(backupFilename);
+ if (backupFile.exists()) {
+ if (!backupFile.delete()) {
+ System.err.println("Error when trying to delete old log file: " + backupFile.getName());//$NON-NLS-1$
+ if (backupFile.renameTo(new File(backupFile.getAbsolutePath() + System.currentTimeMillis()))) {
+ System.err.println("So we rename it to filename: " + backupFile.getName()); //$NON-NLS-1$
+ } else {
+ System.err.println("And we also cannot rename it!"); //$NON-NLS-1$
+ isBackupOK = false;
+ }
+ }
+ }
+
+ // Rename current log file to backup one.
+ boolean isRenameOK = outFile.renameTo(backupFile);
+ if (!isRenameOK) {
+ System.err.println("Error when trying to rename log file to backup one."); //$NON-NLS-1$
+ isBackupOK = false;
+ }
+ File newFile = new File(logFilename);
+ setOutput(newFile, null, false);
+
+ // Write a new SESSION header to new log file.
+ openFile();
+ try {
+ writeSession();
+ writeln();
+ writeln("This is a continuation of log file " + backupFile.getAbsolutePath());//$NON-NLS-1$
+ writeln("Created Time: " + getDate(new Date(System.currentTimeMillis()))); //$NON-NLS-1$
+ writer.flush();
+ } catch (IOException ioe) {
+ ioe.printStackTrace(System.err);
+ }
+ closeFile();
+ backupIdx = (++backupIdx) % maxLogFiles;
+ }
+ }
+ return isBackupOK;
+ }
+
+ /**
+ * Reads the PROP_LOG_SIZE_MAX and PROP_LOG_FILE_MAX properties.
+ */
+ private void readLogProperties() {
+ String newMaxLogSize = secureAction.getProperty(PROP_LOG_SIZE_MAX);
+ if (newMaxLogSize != null) {
+ maxLogSize = Integer.parseInt(newMaxLogSize);
+ if (maxLogSize != 0 && maxLogSize < LOG_SIZE_MIN) {
+ // If the value is '0', then it means no size limitation.
+ // Also, make sure no inappropriate(too small) assigned value.
+ maxLogSize = LOG_SIZE_MIN;
+ }
+ }
+
+ String newMaxLogFiles = secureAction.getProperty(PROP_LOG_FILE_MAX);
+ if (newMaxLogFiles != null) {
+ maxLogFiles = Integer.parseInt(newMaxLogFiles);
+ if (maxLogFiles < 1) {
+ // Make sure no invalid assigned value. (at least >= 1)
+ maxLogFiles = DEFAULT_LOG_FILES;
+ }
+ }
+
+ String newLogLevel = secureAction.getProperty(PROP_LOG_LEVEL);
+ if (newLogLevel != null) {
+ if (newLogLevel.equals("ERROR")) //$NON-NLS-1$
+ logLevel = FrameworkLogEntry.ERROR;
+ else if (newLogLevel.equals("WARNING")) //$NON-NLS-1$
+ logLevel = FrameworkLogEntry.ERROR | FrameworkLogEntry.WARNING;
+ else if (newLogLevel.equals("INFO")) //$NON-NLS-1$
+ logLevel = FrameworkLogEntry.INFO | FrameworkLogEntry.ERROR | FrameworkLogEntry.WARNING | FrameworkLogEntry.CANCEL;
+ else
+ logLevel = FrameworkLogEntry.OK; // OK (0) means log everything
+ }
+
+ includeCommandLine = "true".equals(secureAction.getProperty(PROP_LOG_INCLUDE_COMMAND_LINE, "true")); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ /**
+ * Determines if the log entry should be logged based on log level.
+ */
+ private boolean isLoggable(int fwkEntrySeverity) {
+ if (logLevel == 0)
+ return true;
+ return (fwkEntrySeverity & logLevel) != 0;
+ }
+
+ public boolean isLoggable(Bundle bundle, String loggableName, int loggableLevel) {
+ if (!enabled)
+ return false;
+ if (loggerName.equals(loggableName))
+ return isLoggable(convertSeverity(loggableLevel));
+ if (EclipseLogHook.PERF_LOGGER_NAME.equals(loggableName))
+ // we don't want to do anything with performance logger unless
+ // this is the performance logger (check done above).
+ return false;
+ if (!EclipseLogHook.EQUINOX_LOGGER_NAME.equals(loggerName))
+ // only the equinox log writer should pay attention to other logs
+ return false;
+ // for now only log errors; probably need this to be configurable
+ return loggableLevel == LogService.LOG_ERROR;
+ }
+
+ public void logged(LogEntry entry) {
+ if (!(entry instanceof ExtendedLogEntry))
+ // TODO this should never happen
+ return;
+ ExtendedLogEntry extended = (ExtendedLogEntry) entry;
+ Object context = extended.getContext();
+ if (context instanceof FrameworkLogEntry) {
+ log((FrameworkLogEntry) context);
+ return;
+ }
+ // OK we are now in a case where someone logged a normal entry to the real LogService
+ log(new FrameworkLogEntry(getFwkEntryTag(entry), convertSeverity(entry.getLevel()), 0, entry.getMessage(), 0, entry.getException(), null));
+ }
+
+ private static String getFwkEntryTag(LogEntry entry) {
+ Bundle b = entry.getBundle();
+ if (b != null && b.getSymbolicName() != null)
+ return b.getSymbolicName();
+ return "unknown"; //$NON-NLS-1$
+ }
+
+ private static int convertSeverity(int entryLevel) {
+ switch (entryLevel) {
+ case LogService.LOG_ERROR :
+ return FrameworkLogEntry.ERROR;
+ case LogService.LOG_WARNING :
+ return FrameworkLogEntry.WARNING;
+ case LogService.LOG_INFO :
+ return FrameworkLogEntry.INFO;
+ case LogService.LOG_DEBUG :
+ return FrameworkLogEntry.OK;
+ default :
+ return 32; // unknown
+ }
+ }
+
+ public String getLoggerName() {
+ return loggerName;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseStorageHook.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseStorageHook.java
new file mode 100644
index 000000000..78aa333aa
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/EclipseStorageHook.java
@@ -0,0 +1,522 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.*;
+import java.net.URL;
+import java.security.*;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import org.eclipse.core.runtime.adaptor.LocationManager;
+import org.eclipse.osgi.baseadaptor.*;
+import org.eclipse.osgi.baseadaptor.hooks.StorageHook;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.framework.util.Headers;
+import org.eclipse.osgi.framework.util.KeyedElement;
+import org.eclipse.osgi.internal.baseadaptor.AdaptorUtil;
+import org.eclipse.osgi.service.datalocation.Location;
+import org.eclipse.osgi.service.pluginconversion.PluginConversionException;
+import org.eclipse.osgi.util.ManifestElement;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Version;
+
+public final class EclipseStorageHook implements StorageHook, HookConfigurator {
+ // System property used to check timestamps of the bundles in the configuration
+ private static final String PROP_CHECK_CONFIG = "osgi.checkConfiguration"; //$NON-NLS-1$
+ private static final String PROP_COMPATIBILITY_LAZYSTART = "osgi.compatibility.eagerStart.LazyActivation"; //$NON-NLS-1$
+ private static final boolean COMPATIBILITY_LAZYSTART = Boolean.valueOf(FrameworkProperties.getProperty(PROP_COMPATIBILITY_LAZYSTART, "true")).booleanValue(); //$NON-NLS-1$
+ private static final int STORAGE_VERION = 4;
+
+ public static final String KEY = EclipseStorageHook.class.getName();
+ public static final int HASHCODE = KEY.hashCode();
+
+ private static final byte FLAG_LAZY_START = 0x01;
+ private static final byte FLAG_HAS_PACKAGE_INFO = 0x02;
+ // Note that the 0x04 was used in previous versions, if a new flag is needed then do not reuse this one
+ //private static final byte FLAG_ACTIVATE_ON_CLASSLOAD = 0x04;
+ // Flag to indicate that an include directive is present on the lazy activation policy
+ private static final byte FLAG_HAS_LAZY_INCLUDE = 0x08;
+
+ /** data to detect modification made in the manifest */
+ private long manifestTimeStamp = 0;
+ private byte manifestType = PluginConverterImpl.MANIFEST_TYPE_UNKNOWN;
+
+ private BaseData bundledata;
+
+ /** the Plugin-Class header */
+ private String pluginClass = null;
+ /** Eclipse-LazyStart header */
+ private String[] lazyStartExcludes;
+ private String[] lazyStartIncludes;
+ private int bundleManfestVersion;
+ /** shortcut to know if a bundle has a buddy */
+ private String buddyList;
+ /** shortcut to know if a bundle is a registrant to a registered policy */
+ private String registeredBuddyList;
+ /** DS Service Component header */
+ private String serviceComponent;
+ private byte flags = 0;
+
+ public int getStorageVersion() {
+ return STORAGE_VERION;
+ }
+
+ /**
+ * @throws BundleException
+ */
+ public StorageHook create(BaseData data) throws BundleException {
+ EclipseStorageHook storageHook = new EclipseStorageHook();
+ storageHook.bundledata = data;
+ return storageHook;
+ }
+
+ @SuppressWarnings("deprecation")
+ public void initialize(Dictionary<String, String> manifest) throws BundleException {
+ String activationPolicy = manifest.get(Constants.BUNDLE_ACTIVATIONPOLICY);
+ if (activationPolicy != null) {
+ parseActivationPolicy(this, activationPolicy);
+ } else {
+ String lazyStart = manifest.get(Constants.ECLIPSE_LAZYSTART);
+ if (lazyStart == null)
+ lazyStart = manifest.get(Constants.ECLIPSE_AUTOSTART);
+ parseLazyStart(this, lazyStart);
+ }
+ try {
+ String versionString = manifest.get(Constants.BUNDLE_MANIFESTVERSION);
+ bundleManfestVersion = versionString == null ? 0 : Integer.parseInt(versionString);
+ } catch (NumberFormatException nfe) {
+ bundleManfestVersion = 0;
+ }
+ pluginClass = manifest.get(Constants.PLUGIN_CLASS);
+ buddyList = manifest.get(Constants.BUDDY_LOADER);
+ registeredBuddyList = manifest.get(Constants.REGISTERED_POLICY);
+ if (hasPackageInfo(bundledata.getEntry(Constants.OSGI_BUNDLE_MANIFEST)))
+ flags |= FLAG_HAS_PACKAGE_INFO;
+ String genFrom = manifest.get(PluginConverterImpl.GENERATED_FROM);
+ if (genFrom != null) {
+ ManifestElement generatedFrom = ManifestElement.parseHeader(PluginConverterImpl.GENERATED_FROM, genFrom)[0];
+ if (generatedFrom != null) {
+ manifestTimeStamp = Long.parseLong(generatedFrom.getValue());
+ manifestType = Byte.parseByte(generatedFrom.getAttribute(PluginConverterImpl.MANIFEST_TYPE_ATTRIBUTE));
+ }
+ }
+ if (isAutoStartable()) {
+ bundledata.setStatus(bundledata.getStatus() | Constants.BUNDLE_LAZY_START);
+ if (COMPATIBILITY_LAZYSTART)
+ bundledata.setStatus(bundledata.getStatus() | Constants.BUNDLE_STARTED | Constants.BUNDLE_ACTIVATION_POLICY);
+ }
+ serviceComponent = manifest.get(CachedManifest.SERVICE_COMPONENT);
+ }
+
+ public StorageHook load(BaseData target, DataInputStream in) throws IOException {
+ EclipseStorageHook storageHook = new EclipseStorageHook();
+ storageHook.bundledata = target;
+ storageHook.flags = in.readByte();
+ int pkgCount = in.readInt();
+ String[] packageList = pkgCount > 0 ? new String[pkgCount] : null;
+ for (int i = 0; i < pkgCount; i++)
+ packageList[i] = in.readUTF();
+ storageHook.lazyStartExcludes = packageList;
+ if ((storageHook.flags & FLAG_HAS_LAZY_INCLUDE) != 0) {
+ pkgCount = in.readInt();
+ packageList = pkgCount > 0 ? new String[pkgCount] : null;
+ for (int i = 0; i < pkgCount; i++)
+ packageList[i] = in.readUTF();
+ storageHook.lazyStartIncludes = packageList;
+ }
+ storageHook.buddyList = AdaptorUtil.readString(in, false);
+ storageHook.registeredBuddyList = AdaptorUtil.readString(in, false);
+ storageHook.pluginClass = AdaptorUtil.readString(in, false);
+ storageHook.manifestTimeStamp = in.readLong();
+ storageHook.manifestType = in.readByte();
+ storageHook.bundleManfestVersion = in.readInt();
+ if (storageHook.isAutoStartable()) {
+ if ((target.getStatus() & Constants.BUNDLE_LAZY_START) == 0)
+ target.setStatus(target.getStatus() | Constants.BUNDLE_LAZY_START);
+ // if the compatibility flag is set then we must make sure the persistent start bit is set and the activation policy bit;
+ // if the persistent start bit was already set then we should not set the activation policy bit because this is an "eager" started bundle.
+ if (COMPATIBILITY_LAZYSTART && (target.getStatus() & Constants.BUNDLE_STARTED) == 0)
+ target.setStatus(target.getStatus() | Constants.BUNDLE_STARTED | Constants.BUNDLE_ACTIVATION_POLICY);
+ }
+ storageHook.serviceComponent = AdaptorUtil.readString(in, false);
+ return storageHook;
+ }
+
+ public void save(DataOutputStream out) throws IOException {
+ if (bundledata == null)
+ throw new IllegalStateException();
+ // when this is stored back we always use the has include/exclude flag
+ out.writeByte(flags);
+ String[] excludes = getLazyStartExcludes();
+ if (excludes == null)
+ out.writeInt(0);
+ else {
+ out.writeInt(excludes.length);
+ for (int i = 0; i < excludes.length; i++)
+ out.writeUTF(excludes[i]);
+ }
+ if ((flags & FLAG_HAS_LAZY_INCLUDE) != 0) {
+ String[] includes = getLazyStartIncludes();
+ if (includes == null)
+ out.writeInt(0);
+ else {
+ out.writeInt(includes.length);
+ for (int i = 0; i < includes.length; i++)
+ out.writeUTF(includes[i]);
+ }
+ }
+ AdaptorUtil.writeStringOrNull(out, getBuddyList());
+ AdaptorUtil.writeStringOrNull(out, getRegisteredBuddyList());
+ AdaptorUtil.writeStringOrNull(out, getPluginClass());
+ out.writeLong(getManifestTimeStamp());
+ out.writeByte(getManifestType());
+ out.writeInt(getBundleManifestVersion());
+ AdaptorUtil.writeStringOrNull(out, serviceComponent);
+ }
+
+ public int getKeyHashCode() {
+ return HASHCODE;
+ }
+
+ public boolean compare(KeyedElement other) {
+ return other.getKey() == KEY;
+ }
+
+ public Object getKey() {
+ return KEY;
+ }
+
+ public boolean isLazyStart() {
+ return (flags & FLAG_LAZY_START) == FLAG_LAZY_START;
+ }
+
+ public String[] getLazyStartExcludes() {
+ return lazyStartExcludes;
+ }
+
+ public String[] getLazyStartIncludes() {
+ return lazyStartIncludes;
+ }
+
+ public String getBuddyList() {
+ return buddyList;
+ }
+
+ public boolean hasPackageInfo() {
+ return (flags & FLAG_HAS_PACKAGE_INFO) == FLAG_HAS_PACKAGE_INFO;
+ }
+
+ public String getPluginClass() {
+ return pluginClass;
+ }
+
+ public String getRegisteredBuddyList() {
+ return registeredBuddyList;
+ }
+
+ public long getManifestTimeStamp() {
+ return manifestTimeStamp;
+ }
+
+ public byte getManifestType() {
+ return manifestType;
+ }
+
+ public int getBundleManifestVersion() {
+ return bundleManfestVersion;
+ }
+
+ public String getServiceComponent() {
+ return serviceComponent;
+ }
+
+ /**
+ * Checks whether this bundle is auto started for all resource/class loads or only for a
+ * subset of resource/classloads
+ * @return true if the bundle is auto started; false otherwise
+ */
+ public boolean isAutoStartable() {
+ return isLazyStart() || (lazyStartExcludes != null && lazyStartExcludes.length > 0);
+ }
+
+ private void parseLazyStart(EclipseStorageHook storageHook, String headerValue) {
+ storageHook.lazyStartExcludes = null;
+ ManifestElement[] allElements = null;
+ try {
+ allElements = ManifestElement.parseHeader(Constants.ECLIPSE_LAZYSTART, headerValue);
+ } catch (BundleException e) {
+ // just use the default settings (no auto activation)
+ String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_CANNOT_GET_HEADERS, storageHook.bundledata.getLocation());
+ bundledata.getAdaptor().getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, e, null));
+ }
+ //Eclipse-AutoStart not found...
+ if (allElements == null)
+ return;
+ // the single value for this element should be true|false
+ if ("true".equalsIgnoreCase(allElements[0].getValue())) //$NON-NLS-1$
+ storageHook.flags |= FLAG_LAZY_START;
+ // look for any exceptions (the attribute) to the autoActivate setting
+ String[] exceptions = ManifestElement.getArrayFromList(allElements[0].getAttribute(Constants.ECLIPSE_LAZYSTART_EXCEPTIONS));
+ storageHook.lazyStartExcludes = exceptions;
+ }
+
+ private void parseActivationPolicy(EclipseStorageHook storageHook, String headerValue) {
+ storageHook.lazyStartExcludes = null;
+ ManifestElement[] allElements = null;
+ try {
+ allElements = ManifestElement.parseHeader(Constants.BUNDLE_ACTIVATIONPOLICY, headerValue);
+ } catch (BundleException e) {
+ // just use the default settings (no auto activation)
+ String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CLASSLOADER_CANNOT_GET_HEADERS, storageHook.bundledata.getLocation());
+ bundledata.getAdaptor().getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, e, null));
+ }
+ //Bundle-ActivationPolicy not found.
+ if (allElements == null)
+ return;
+ // the single value for this type is lazy
+ if (!Constants.ACTIVATION_LAZY.equalsIgnoreCase(allElements[0].getValue()))
+ return;
+ storageHook.flags |= FLAG_LAZY_START;
+ // look for any include or exclude attrs
+ storageHook.lazyStartExcludes = ManifestElement.getArrayFromList(allElements[0].getDirective(Constants.EXCLUDE_DIRECTIVE));
+ storageHook.lazyStartIncludes = ManifestElement.getArrayFromList(allElements[0].getDirective(Constants.INCLUDE_DIRECTIVE));
+ if (storageHook.lazyStartIncludes != null)
+ storageHook.flags |= FLAG_HAS_LAZY_INCLUDE;
+ }
+
+ // Used to check the bundle manifest file for any package information.
+ // This is used when '.' is on the Bundle-ClassPath to prevent reading
+ // the bundle manifest for pacakge information when loading classes.
+ private static boolean hasPackageInfo(URL url) {
+ if (url == null)
+ return false;
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new InputStreamReader(url.openStream()));
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (line.length() < 20)
+ continue;
+ switch (line.charAt(0)) {
+ case 'S' :
+ if (line.charAt(1) == 'p')
+ if (line.startsWith("Specification-Title: ") || line.startsWith("Specification-Version: ") || line.startsWith("Specification-Vendor: ")) //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
+ return true;
+ break;
+ case 'I' :
+ if (line.startsWith("Implementation-Title: ") || line.startsWith("Implementation-Version: ") || line.startsWith("Implementation-Vendor: ")) //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
+ return true;
+ break;
+ }
+ }
+ } catch (IOException ioe) {
+ // do nothing
+ } finally {
+ if (br != null)
+ try {
+ br.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ return false;
+ }
+
+ public void addHooks(HookRegistry hookRegistry) {
+ hookRegistry.addStorageHook(this);
+ }
+
+ private void checkTimeStamp() throws IllegalArgumentException {
+ if (!checkManifestTimeStamp())
+ throw new IllegalArgumentException();
+ }
+
+ private boolean checkManifestTimeStamp() {
+ if (!"true".equalsIgnoreCase(FrameworkProperties.getProperty(EclipseStorageHook.PROP_CHECK_CONFIG))) //$NON-NLS-1$
+ return true;
+ if (PluginConverterImpl.getTimeStamp(bundledata.getBundleFile().getBaseFile(), getManifestType()) == getManifestTimeStamp()) {
+ if ((getManifestType() & (PluginConverterImpl.MANIFEST_TYPE_JAR | PluginConverterImpl.MANIFEST_TYPE_BUNDLE)) != 0)
+ return true;
+ String cacheLocation = FrameworkProperties.getProperty(LocationManager.PROP_MANIFEST_CACHE);
+ Location parentConfiguration = LocationManager.getConfigurationLocation().getParentLocation();
+ if (parentConfiguration != null) {
+ try {
+ return checkManifestAndParent(cacheLocation, bundledata.getSymbolicName(), bundledata.getVersion().toString(), getManifestType()) != null;
+ } catch (BundleException e) {
+ return false;
+ }
+ }
+ File cacheFile = new File(cacheLocation, bundledata.getSymbolicName() + '_' + bundledata.getVersion() + ".MF"); //$NON-NLS-1$
+ if (cacheFile.isFile())
+ return true;
+ }
+ return false;
+ }
+
+ private Headers<String, String> checkManifestAndParent(String cacheLocation, String symbolicName, String version, byte inputType) throws BundleException {
+ Headers<String, String> result = basicCheckManifest(cacheLocation, symbolicName, version, inputType);
+ if (result != null)
+ return result;
+ Location parentConfiguration = null;
+ if ((parentConfiguration = LocationManager.getConfigurationLocation().getParentLocation()) != null)
+ result = basicCheckManifest(new File(parentConfiguration.getURL().getFile(), FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME + '/' + LocationManager.MANIFESTS_DIR).toString(), symbolicName, version, inputType);
+ return result;
+ }
+
+ private Headers<String, String> basicCheckManifest(String cacheLocation, String symbolicName, String version, byte inputType) throws BundleException {
+ File currentFile = new File(cacheLocation, symbolicName + '_' + version + ".MF"); //$NON-NLS-1$
+ if (PluginConverterImpl.upToDate(currentFile, bundledata.getBundleFile().getBaseFile(), inputType)) {
+ try {
+ return Headers.parseManifest(new FileInputStream(currentFile));
+ } catch (FileNotFoundException e) {
+ // do nothing.
+ }
+ }
+ return null;
+ }
+
+ Dictionary<String, String> createCachedManifest(boolean firstTime) throws BundleException {
+ return firstTime ? getGeneratedManifest() : new CachedManifest(this);
+ }
+
+ public Dictionary<String, String> getGeneratedManifest() throws BundleException {
+ if (System.getSecurityManager() == null)
+ return getGeneratedManifest0();
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Dictionary<String, String>>() {
+ public Dictionary<String, String> run() throws BundleException {
+ return getGeneratedManifest0();
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ throw (BundleException) e.getException();
+ }
+ }
+
+ final Dictionary<String, String> getGeneratedManifest0() throws BundleException {
+ Dictionary<String, String> builtIn = AdaptorUtil.loadManifestFrom(bundledata);
+ if (builtIn != null) {
+ // the bundle has a built-in manifest - we may not have to generate one
+ if (!isComplete(builtIn)) {
+ Dictionary<String, String> generatedManifest = generateManifest(builtIn);
+ if (generatedManifest != null)
+ return generatedManifest;
+ }
+ // the manifest is complete or we could not complete it - take it as it is
+ manifestType = PluginConverterImpl.MANIFEST_TYPE_BUNDLE;
+ File baseFile = bundledata.getBundleFile().getBaseFile();
+ if (baseFile != null && bundledata.getBundleFile().getBaseFile().isFile()) {
+ manifestTimeStamp = bundledata.getBundleFile().getBaseFile().lastModified();
+ manifestType |= PluginConverterImpl.MANIFEST_TYPE_JAR;
+ } else
+ manifestTimeStamp = bundledata.getBundleFile().getEntry(Constants.OSGI_BUNDLE_MANIFEST).getTime();
+ return builtIn;
+ }
+ Dictionary<String, String> result = generateManifest(null);
+ if (result == null)
+ throw new BundleException(NLS.bind(EclipseAdaptorMsg.ECLIPSE_DATA_MANIFEST_NOT_FOUND, bundledata.getLocation()));
+ return result;
+ }
+
+ private Dictionary<String, String> generateManifest(Dictionary<String, String> builtIn) throws BundleException {
+ String cacheLocation = FrameworkProperties.getProperty(LocationManager.PROP_MANIFEST_CACHE);
+ if (bundledata.getSymbolicName() != null) {
+ Headers<String, String> existingHeaders = checkManifestAndParent(cacheLocation, bundledata.getSymbolicName(), bundledata.getVersion().toString(), manifestType);
+ if (existingHeaders != null)
+ return existingHeaders;
+ }
+
+ PluginConverterImpl converter = PluginConverterImpl.getDefault();
+ if (converter == null)
+ converter = new PluginConverterImpl(bundledata.getAdaptor(), bundledata.getAdaptor().getContext());
+
+ Dictionary<String, String> generatedManifest;
+ try {
+ generatedManifest = converter.convertManifest(bundledata.getBundleFile().getBaseFile(), true, null, true, null);
+ } catch (PluginConversionException pce) {
+ String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_ERROR_CONVERTING, bundledata.getBundleFile().getBaseFile());
+ throw new BundleException(message, BundleException.MANIFEST_ERROR, pce);
+ }
+
+ //Now we know the symbolicId and the version of the bundle, we check to see if don't have a manifest for it already
+ Version version = Version.parseVersion(generatedManifest.get(Constants.BUNDLE_VERSION));
+ String symbolicName = ManifestElement.parseHeader(org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME, generatedManifest.get(org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME))[0].getValue();
+ ManifestElement generatedFrom = ManifestElement.parseHeader(PluginConverterImpl.GENERATED_FROM, generatedManifest.get(PluginConverterImpl.GENERATED_FROM))[0];
+ Headers<String, String> existingHeaders = checkManifestAndParent(cacheLocation, symbolicName, version.toString(), Byte.parseByte(generatedFrom.getAttribute(PluginConverterImpl.MANIFEST_TYPE_ATTRIBUTE)));
+ //We don't have a manifest.
+ manifestTimeStamp = Long.parseLong(generatedFrom.getValue());
+ manifestType = Byte.parseByte(generatedFrom.getAttribute(PluginConverterImpl.MANIFEST_TYPE_ATTRIBUTE));
+ if (bundledata.getAdaptor().isReadOnly() || existingHeaders != null)
+ return existingHeaders;
+
+ //merge the original manifest with the generated one
+ if (builtIn != null) {
+ Enumeration<String> keysEnum = builtIn.keys();
+ while (keysEnum.hasMoreElements()) {
+ String key = keysEnum.nextElement();
+ generatedManifest.put(key, builtIn.get(key));
+ }
+ }
+
+ //write the generated manifest
+ File bundleManifestLocation = new File(cacheLocation, symbolicName + '_' + version.toString() + ".MF"); //$NON-NLS-1$
+ try {
+ converter.writeManifest(bundleManifestLocation, generatedManifest, true);
+ } catch (Exception e) {
+ //TODO Need to log
+ }
+ return generatedManifest;
+
+ }
+
+ private boolean isComplete(Dictionary<String, String> manifest) {
+ // a manifest is complete if it has a Bundle-SymbolicName entry...
+ if (manifest.get(org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME) != null)
+ return true;
+ // ...or it does not have a plugin/fragment manifest where to get the other entries from
+ return bundledata.getEntry(PluginConverterImpl.PLUGIN_MANIFEST) == null && bundledata.getEntry(PluginConverterImpl.FRAGMENT_MANIFEST) == null;
+ }
+
+ public BaseData getBaseData() {
+ return bundledata;
+ }
+
+ public void copy(StorageHook storageHook) {
+ // copy nothing all must be re-read from a manifest
+ }
+
+ public void validate() throws IllegalArgumentException {
+ checkTimeStamp();
+ }
+
+ public FrameworkAdaptor getAdaptor() {
+ if (bundledata != null)
+ return bundledata.getAdaptor();
+ return null;
+ }
+
+ public Dictionary<String, String> getManifest(boolean firstLoad) throws BundleException {
+ return createCachedManifest(firstLoad);
+ }
+
+ public boolean forgetStatusChange(int status) {
+ return false;
+ }
+
+ public boolean forgetStartLevelChange(int startlevel) {
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/IModel.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/IModel.java
new file mode 100644
index 000000000..1a870c822
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/IModel.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.runtime.internal.adaptor;
+
+/**
+ * Internal class.
+ */
+public interface IModel {
+ public static final int INDENT = 2;
+ public static final int RADIX = 36;
+
+ public static final String TRUE = "true"; //$NON-NLS-1$
+ public static final String FALSE = "false"; //$NON-NLS-1$
+
+ public static final String REGISTRY = "plugin-registry"; //$NON-NLS-1$
+ public static final String REGISTRY_PATH = "path"; //$NON-NLS-1$
+
+ public static final String FRAGMENT = "fragment"; //$NON-NLS-1$
+ public static final String FRAGMENT_ID = "id"; //$NON-NLS-1$
+ public static final String FRAGMENT_NAME = "name"; //$NON-NLS-1$
+ public static final String FRAGMENT_PROVIDER = "provider-name"; //$NON-NLS-1$
+ public static final String FRAGMENT_VERSION = "version"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_ID = "plugin-id"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_VERSION = "plugin-version"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_MATCH = "match"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_MATCH_PERFECT = "perfect"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_MATCH_EQUIVALENT = "equivalent"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_MATCH_COMPATIBLE = "compatible"; //$NON-NLS-1$
+ public static final String FRAGMENT_PLUGIN_MATCH_GREATER_OR_EQUAL = "greaterOrEqual"; //$NON-NLS-1$
+
+ public static final String PLUGIN = "plugin"; //$NON-NLS-1$
+ public static final String PLUGIN_ID = "id"; //$NON-NLS-1$
+ public static final String PLUGIN_NAME = "name"; //$NON-NLS-1$
+ public static final String PLUGIN_VENDOR = "vendor-name"; //$NON-NLS-1$
+ public static final String PLUGIN_PROVIDER = "provider-name"; //$NON-NLS-1$
+ public static final String PLUGIN_VERSION = "version"; //$NON-NLS-1$
+ public static final String PLUGIN_CLASS = "class"; //$NON-NLS-1$
+
+ public static final String PLUGIN_REQUIRES = "requires"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_PLATFORM = "platform-version"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_PLUGIN = "plugin"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_PLUGIN_VERSION = "version"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_OPTIONAL = "optional"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_IMPORT = "import"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_EXPORT = "export"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH = "match"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH_EXACT = "exact"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH_PERFECT = "perfect"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH_EQUIVALENT = "equivalent"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH_COMPATIBLE = "compatible"; //$NON-NLS-1$
+ public static final String PLUGIN_REQUIRES_MATCH_GREATER_OR_EQUAL = "greaterOrEqual"; //$NON-NLS-1$
+
+ public static final String PLUGIN_KEY_VERSION_SEPARATOR = "_"; //$NON-NLS-1$
+
+ public static final String RUNTIME = "runtime"; //$NON-NLS-1$
+
+ public static final String LIBRARY = "library"; //$NON-NLS-1$
+ public static final String LIBRARY_NAME = "name"; //$NON-NLS-1$
+ public static final String LIBRARY_SOURCE = "source"; //$NON-NLS-1$
+ public static final String LIBRARY_TYPE = "type"; //$NON-NLS-1$
+ public static final String LIBRARY_EXPORT = "export"; //$NON-NLS-1$
+ public static final String LIBRARY_EXPORT_MASK = "name"; //$NON-NLS-1$
+ public static final String LIBRARY_PACKAGES = "packages"; //$NON-NLS-1$
+ public static final String LIBRARY_PACKAGES_PREFIXES = "prefixes"; //$NON-NLS-1$
+
+ public static final String EXTENSION_POINT = "extension-point"; //$NON-NLS-1$
+ public static final String EXTENSION_POINT_NAME = "name"; //$NON-NLS-1$
+ public static final String EXTENSION_POINT_ID = "id"; //$NON-NLS-1$
+ public static final String EXTENSION_POINT_SCHEMA = "schema"; //$NON-NLS-1$
+
+ public static final String EXTENSION = "extension"; //$NON-NLS-1$
+ public static final String EXTENSION_NAME = "name"; //$NON-NLS-1$
+ public static final String EXTENSION_ID = "id"; //$NON-NLS-1$
+ public static final String EXTENSION_TARGET = "point"; //$NON-NLS-1$
+
+ public static final String ELEMENT = "element"; //$NON-NLS-1$
+ public static final String ELEMENT_NAME = "name"; //$NON-NLS-1$
+ public static final String ELEMENT_VALUE = "value"; //$NON-NLS-1$
+
+ public static final String PROPERTY = "property"; //$NON-NLS-1$
+ public static final String PROPERTY_NAME = "name"; //$NON-NLS-1$
+ public static final String PROPERTY_VALUE = "value"; //$NON-NLS-1$
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/IPluginInfo.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/IPluginInfo.java
new file mode 100644
index 000000000..9520f88eb
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/IPluginInfo.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.util.*;
+
+/**
+ * Interface used as an entry to the IPluginConverter
+ *
+ * <p>Internal class.</p>
+ */
+public interface IPluginInfo {
+ public Map<String, List<String>> getLibraries();
+
+ public String[] getLibrariesName();
+
+ public ArrayList<PluginParser.Prerequisite> getRequires();
+
+ public String getMasterId();
+
+ public String getMasterVersion();
+
+ public String getMasterMatch();
+
+ public String getPluginClass();
+
+ public String getUniqueId();
+
+ public String getVersion();
+
+ public boolean isFragment();
+
+ public Set<String> getPackageFilters();
+
+ public String getPluginName();
+
+ public String getProviderName();
+
+ public boolean isSingleton();
+
+ public boolean hasExtensionExtensionPoints();
+
+ String validateForm();
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/MessageHelper.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/MessageHelper.java
new file mode 100644
index 000000000..e1cf601b1
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/MessageHelper.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.util.Date;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.Constants;
+
+/**
+ * @since 3.3
+ */
+public class MessageHelper {
+ public static String getResolutionFailureMessage(VersionConstraint unsatisfied) {
+ if (unsatisfied.isResolved())
+ throw new IllegalArgumentException();
+ if (unsatisfied instanceof ImportPackageSpecification) {
+ if (ImportPackageSpecification.RESOLUTION_OPTIONAL.equals(((ImportPackageSpecification) unsatisfied).getDirective(Constants.RESOLUTION_DIRECTIVE)))
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_MISSING_OPTIONAL_IMPORTED_PACKAGE, toString(unsatisfied));
+ if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(((ImportPackageSpecification) unsatisfied).getDirective(Constants.RESOLUTION_DIRECTIVE)))
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_MISSING_DYNAMIC_IMPORTED_PACKAGE, toString(unsatisfied));
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_MISSING_IMPORTED_PACKAGE, toString(unsatisfied));
+ } else if (unsatisfied instanceof BundleSpecification) {
+ if (((BundleSpecification) unsatisfied).isOptional())
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_MISSING_OPTIONAL_REQUIRED_BUNDLE, toString(unsatisfied));
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_MISSING_REQUIRED_BUNDLE, toString(unsatisfied));
+ } else if (unsatisfied instanceof HostSpecification) {
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_MISSING_HOST, toString(unsatisfied));
+ } else if (unsatisfied instanceof NativeCodeSpecification) {
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_MISSING_NATIVECODE, unsatisfied.toString());
+ } else if (unsatisfied instanceof GenericSpecification) {
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_MISSING_REQUIRED_CAPABILITY, unsatisfied.toString());
+ }
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_MISSING_REQUIREMENT, unsatisfied.toString());
+ }
+
+ /**
+ * Print a debug message to the console.
+ * Pre-pend the message with the current date and the name of the current thread.
+ */
+ public static void debug(String message) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(new Date(System.currentTimeMillis()));
+ buffer.append(" - ["); //$NON-NLS-1$
+ buffer.append(Thread.currentThread().getName());
+ buffer.append("] "); //$NON-NLS-1$
+ buffer.append(message);
+ System.out.println(buffer.toString());
+ }
+
+ private static String toString(VersionConstraint constraint) {
+ org.eclipse.osgi.service.resolver.VersionRange versionRange = constraint.getVersionRange();
+ if (versionRange == null)
+ return constraint.getName();
+ return constraint.getName() + '_' + versionRange;
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/PluginConverterImpl.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/PluginConverterImpl.java
new file mode 100644
index 000000000..fe1447f2e
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/PluginConverterImpl.java
@@ -0,0 +1,761 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.eclipse.core.runtime.adaptor.LocationManager;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.internal.baseadaptor.DevClassPathHelper;
+import org.eclipse.osgi.service.pluginconversion.PluginConversionException;
+import org.eclipse.osgi.service.pluginconversion.PluginConverter;
+import org.eclipse.osgi.service.resolver.VersionRange;
+import org.eclipse.osgi.util.ManifestElement;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+
+/**
+ * Internal class.
+ */
+public class PluginConverterImpl implements PluginConverter {
+ public static boolean DEBUG = false;
+ /** bundle manifest type unknown */
+ static public final byte MANIFEST_TYPE_UNKNOWN = 0x00;
+ /** bundle manifest type bundle (META-INF/MANIFEST.MF) */
+ static public final byte MANIFEST_TYPE_BUNDLE = 0x01;
+ /** bundle manifest type plugin (plugin.xml) */
+ static public final byte MANIFEST_TYPE_PLUGIN = 0x02;
+ /** bundle manifest type fragment (fragment.xml) */
+ static public final byte MANIFEST_TYPE_FRAGMENT = 0x04;
+ /** bundle manifest type jared bundle */
+ static public final byte MANIFEST_TYPE_JAR = 0x08;
+ private static final String SEMICOLON = "; "; //$NON-NLS-1$
+ private static final String UTF_8 = "UTF-8"; //$NON-NLS-1$
+ private static final String LIST_SEPARATOR = ",\n "; //$NON-NLS-1$
+ private static final String LINE_SEPARATOR = "\n "; //$NON-NLS-1$
+ private static final String DOT = "."; //$NON-NLS-1$
+ private static int MAXLINE = 511;
+ private BundleContext context;
+ private FrameworkAdaptor adaptor;
+ private BufferedWriter out;
+ private IPluginInfo pluginInfo;
+ private File pluginManifestLocation;
+ private ZipFile pluginZip;
+ private Dictionary<String, String> generatedManifest;
+ private byte manifestType;
+ private Version target;
+ private Dictionary<String, String> devProperties;
+ static final Version TARGET31 = new Version(3, 1, 0);
+ static final Version TARGET32 = new Version(3, 2, 0);
+ private static final String MANIFEST_VERSION = "Manifest-Version"; //$NON-NLS-1$
+ private static final String PLUGIN_PROPERTIES_FILENAME = "plugin"; //$NON-NLS-1$
+ private static PluginConverterImpl instance;
+ @SuppressWarnings("deprecation")
+ private static final String[] ARCH_LIST = {org.eclipse.osgi.service.environment.Constants.ARCH_PA_RISC, org.eclipse.osgi.service.environment.Constants.ARCH_PPC, org.eclipse.osgi.service.environment.Constants.ARCH_SPARC, org.eclipse.osgi.service.environment.Constants.ARCH_X86, org.eclipse.osgi.service.environment.Constants.ARCH_AMD64, org.eclipse.osgi.service.environment.Constants.ARCH_IA64};
+ static public final String FRAGMENT_MANIFEST = "fragment.xml"; //$NON-NLS-1$
+ static public final String GENERATED_FROM = "Generated-from"; //$NON-NLS-1$
+ static public final String MANIFEST_TYPE_ATTRIBUTE = "type"; //$NON-NLS-1$
+ private static final String[] OS_LIST = {org.eclipse.osgi.service.environment.Constants.OS_AIX, org.eclipse.osgi.service.environment.Constants.OS_HPUX, org.eclipse.osgi.service.environment.Constants.OS_LINUX, org.eclipse.osgi.service.environment.Constants.OS_MACOSX, org.eclipse.osgi.service.environment.Constants.OS_QNX, org.eclipse.osgi.service.environment.Constants.OS_SOLARIS, org.eclipse.osgi.service.environment.Constants.OS_WIN32};
+ protected static final String PI_RUNTIME = "org.eclipse.core.runtime"; //$NON-NLS-1$
+ protected static final String PI_BOOT = "org.eclipse.core.boot"; //$NON-NLS-1$
+ protected static final String PI_RUNTIME_COMPATIBILITY = "org.eclipse.core.runtime.compatibility"; //$NON-NLS-1$
+ static public final String PLUGIN_MANIFEST = "plugin.xml"; //$NON-NLS-1$
+ private static final String COMPATIBILITY_ACTIVATOR = "org.eclipse.core.internal.compatibility.PluginActivator"; //$NON-NLS-1$
+ private static final String[] WS_LIST = {org.eclipse.osgi.service.environment.Constants.WS_CARBON, org.eclipse.osgi.service.environment.Constants.WS_GTK, org.eclipse.osgi.service.environment.Constants.WS_MOTIF, org.eclipse.osgi.service.environment.Constants.WS_PHOTON, org.eclipse.osgi.service.environment.Constants.WS_WIN32};
+ private static final String IGNORE_DOT = "@ignoredot@"; //$NON-NLS-1$
+
+ public static PluginConverterImpl getDefault() {
+ return instance;
+ }
+
+ public PluginConverterImpl(FrameworkAdaptor adaptor, BundleContext context) {
+ this.context = context;
+ this.adaptor = adaptor;
+ instance = this;
+ }
+
+ private void init() {
+ // need to make sure these fields are cleared out for each conversion.
+ out = null;
+ pluginInfo = null;
+ pluginManifestLocation = null;
+ pluginZip = null;
+ generatedManifest = new Hashtable<String, String>(10);
+ manifestType = MANIFEST_TYPE_UNKNOWN;
+ target = null;
+ devProperties = null;
+ }
+
+ private void fillPluginInfo(File pluginBaseLocation) throws PluginConversionException {
+ pluginManifestLocation = pluginBaseLocation;
+ if (pluginManifestLocation == null)
+ throw new IllegalArgumentException();
+ InputStream pluginFile = null;
+ try {
+ try {
+ pluginFile = findPluginManifest(pluginBaseLocation);
+ } catch (IOException e) {
+ throw new PluginConversionException(NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_FILENOTFOUND, pluginBaseLocation.getAbsolutePath()), e);
+ }
+ if (pluginFile == null)
+ throw new PluginConversionException(NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_FILENOTFOUND, pluginBaseLocation.getAbsolutePath()));
+ pluginInfo = parsePluginInfo(pluginFile);
+ } finally {
+ if (pluginZip != null)
+ try {
+ pluginZip.close();
+ pluginZip = null;
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ String validation = pluginInfo.validateForm();
+ if (validation != null)
+ throw new PluginConversionException(validation);
+ }
+
+ private Set<String> filterExport(Set<String> exportToFilter, Collection<String> filter) {
+ if (filter == null || filter.contains("*")) //$NON-NLS-1$
+ return exportToFilter;
+ Set<String> filteredExport = new HashSet<String>(exportToFilter.size());
+ for (String anExport : exportToFilter) {
+ for (String aFilter : filter) {
+ int dotStar = aFilter.indexOf(".*"); //$NON-NLS-1$
+ if (dotStar != -1)
+ aFilter = aFilter.substring(0, dotStar);
+ if (anExport.equals(aFilter)) {
+ filteredExport.add(anExport);
+ break;
+ }
+ }
+ }
+ return filteredExport;
+ }
+
+ private List<String> findOSJars(File pluginRoot, String path, boolean filter) {
+ path = path.substring(4);
+ List<String> found = new ArrayList<String>(0);
+ for (int i = 0; i < OS_LIST.length; i++) {
+ //look for os/osname/path
+ String searchedPath = "os/" + OS_LIST[i] + "/" + path; //$NON-NLS-1$ //$NON-NLS-2$
+ if (new File(pluginRoot, searchedPath).exists())
+ found.add(searchedPath + (filter ? ";(os=" + WS_LIST[i] + ")" : "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ //look for os/osname/archname/path
+ for (int j = 0; j < ARCH_LIST.length; j++) {
+ searchedPath = "os/" + OS_LIST[i] + "/" + ARCH_LIST[j] + "/" + path; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ if (new File(pluginRoot, searchedPath).exists()) {
+ found.add(searchedPath + (filter ? ";(& (os=" + WS_LIST[i] + ") (arch=" + ARCH_LIST[j] + ")" : "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ }
+ }
+ }
+ return found;
+ }
+
+ private InputStream findPluginManifest(File baseLocation) throws IOException {
+ //Here, we can not use the bundlefile because it may explode the jar and returns a location from which we will not be able to derive the jars location
+ if (pluginZip != null)
+ try {
+ pluginZip.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ pluginZip = null;
+ if (!baseLocation.isDirectory()) {
+ manifestType |= MANIFEST_TYPE_JAR;
+ pluginZip = new ZipFile(baseLocation);
+ }
+
+ if (pluginZip != null) {
+ ZipEntry manifestEntry = pluginZip.getEntry(PLUGIN_MANIFEST);
+ if (manifestEntry != null) {
+ manifestType |= MANIFEST_TYPE_PLUGIN;
+ return pluginZip.getInputStream(manifestEntry);
+ }
+ } else {
+ File manifestFile = new File(baseLocation, PLUGIN_MANIFEST);
+ if (manifestFile.exists()) {
+ manifestType |= MANIFEST_TYPE_PLUGIN;
+ return new FileInputStream(manifestFile);
+ }
+ }
+
+ if (pluginZip != null) {
+ ZipEntry manifestEntry = pluginZip.getEntry(FRAGMENT_MANIFEST);
+ if (manifestEntry != null) {
+ manifestType |= MANIFEST_TYPE_PLUGIN;
+ return pluginZip.getInputStream(manifestEntry);
+ }
+ } else {
+ File manifestFile = new File(baseLocation, FRAGMENT_MANIFEST);
+ if (manifestFile.exists()) {
+ manifestType |= MANIFEST_TYPE_FRAGMENT;
+ return new FileInputStream(manifestFile);
+ }
+ }
+
+ return null;
+ }
+
+ private List<String> findWSJars(File pluginRoot, String path, boolean filter) {
+ path = path.substring(4);
+ List<String> found = new ArrayList<String>(0);
+ for (int i = 0; i < WS_LIST.length; i++) {
+ String searchedPath = "ws/" + WS_LIST[i] + path; //$NON-NLS-1$
+ if (new File(pluginRoot, searchedPath).exists()) {
+ found.add(searchedPath + (filter ? ";(ws=" + WS_LIST[i] + ")" : "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+ return found;
+ }
+
+ protected void fillManifest(boolean compatibilityManifest, boolean analyseJars) {
+ generateManifestVersion();
+ generateHeaders();
+ generateClasspath();
+ generateActivator();
+ generatePluginClass();
+ if (analyseJars)
+ generateProvidePackage();
+ generateRequireBundle();
+ generateLocalizationEntry();
+ generateEclipseHeaders();
+ if (compatibilityManifest) {
+ generateTimestamp();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public void writeManifest(File generationLocation, Dictionary<String, String> manifestToWrite, boolean compatibilityManifest) throws PluginConversionException {
+ long start = System.currentTimeMillis();
+ try {
+ File parentFile = new File(generationLocation.getParent());
+ parentFile.mkdirs();
+ generationLocation.createNewFile();
+ if (!generationLocation.isFile()) {
+ String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_ERROR_CREATING_BUNDLE_MANIFEST, this.pluginInfo.getUniqueId(), generationLocation);
+ throw new PluginConversionException(message);
+ }
+ // replaces any eventual existing file
+ manifestToWrite = new Hashtable<String, String>((Hashtable) manifestToWrite);
+ // MANIFEST.MF files must be written using UTF-8
+ out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(generationLocation), UTF_8));
+ writeEntry(MANIFEST_VERSION, manifestToWrite.remove(MANIFEST_VERSION));
+ writeEntry(GENERATED_FROM, manifestToWrite.remove(GENERATED_FROM)); //Need to do this first uptoDate check expect the generated-from tag to be in the first line
+ // always attempt to write the Bundle-ManifestVersion header if it exists (bug 109863)
+ writeEntry(Constants.BUNDLE_MANIFESTVERSION, manifestToWrite.remove(Constants.BUNDLE_MANIFESTVERSION));
+ writeEntry(Constants.BUNDLE_NAME, manifestToWrite.remove(Constants.BUNDLE_NAME));
+ writeEntry(Constants.BUNDLE_SYMBOLICNAME, manifestToWrite.remove(Constants.BUNDLE_SYMBOLICNAME));
+ writeEntry(Constants.BUNDLE_VERSION, manifestToWrite.remove(Constants.BUNDLE_VERSION));
+ writeEntry(Constants.BUNDLE_CLASSPATH, manifestToWrite.remove(Constants.BUNDLE_CLASSPATH));
+ writeEntry(Constants.BUNDLE_ACTIVATOR, manifestToWrite.remove(Constants.BUNDLE_ACTIVATOR));
+ writeEntry(Constants.BUNDLE_VENDOR, manifestToWrite.remove(Constants.BUNDLE_VENDOR));
+ writeEntry(Constants.FRAGMENT_HOST, manifestToWrite.remove(Constants.FRAGMENT_HOST));
+ writeEntry(Constants.BUNDLE_LOCALIZATION, manifestToWrite.remove(Constants.BUNDLE_LOCALIZATION));
+ // always attempt to write the Export-Package header if it exists (bug 109863)
+ writeEntry(Constants.EXPORT_PACKAGE, manifestToWrite.remove(Constants.EXPORT_PACKAGE));
+ // always attempt to write the Provide-Package header if it exists (bug 109863)
+ writeEntry(Constants.PROVIDE_PACKAGE, manifestToWrite.remove(Constants.PROVIDE_PACKAGE));
+ writeEntry(Constants.REQUIRE_BUNDLE, manifestToWrite.remove(Constants.REQUIRE_BUNDLE));
+ Enumeration<String> keys = manifestToWrite.keys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ writeEntry(key, manifestToWrite.get(key));
+ }
+ out.flush();
+ } catch (IOException e) {
+ String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_ERROR_CREATING_BUNDLE_MANIFEST, this.pluginInfo.getUniqueId(), generationLocation);
+ throw new PluginConversionException(message, e);
+ } finally {
+ if (out != null)
+ try {
+ out.close();
+ } catch (IOException e) {
+ // only report problems writing to/flushing the file
+ }
+ }
+ if (DEBUG)
+ System.out.println("Time to write out converted manifest to: " + generationLocation + ": " + (System.currentTimeMillis() - start) + "ms."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ private void generateLocalizationEntry() {
+ generatedManifest.put(Constants.BUNDLE_LOCALIZATION, PLUGIN_PROPERTIES_FILENAME);
+ }
+
+ private void generateManifestVersion() {
+ generatedManifest.put(MANIFEST_VERSION, "1.0"); //$NON-NLS-1$
+ }
+
+ private boolean requireRuntimeCompatibility() {
+ ArrayList<PluginParser.Prerequisite> requireList = pluginInfo.getRequires();
+ for (Iterator<PluginParser.Prerequisite> iter = requireList.iterator(); iter.hasNext();) {
+ if (iter.next().getName().equalsIgnoreCase(PI_RUNTIME_COMPATIBILITY))
+ return true;
+ }
+ return false;
+ }
+
+ private void generateActivator() {
+ if (!pluginInfo.isFragment())
+ if (!requireRuntimeCompatibility()) {
+ String pluginClass = pluginInfo.getPluginClass();
+ if (pluginClass != null && !pluginClass.trim().equals("")) //$NON-NLS-1$
+ generatedManifest.put(Constants.BUNDLE_ACTIVATOR, pluginClass);
+ } else {
+ generatedManifest.put(Constants.BUNDLE_ACTIVATOR, COMPATIBILITY_ACTIVATOR);
+ }
+ }
+
+ private void generateClasspath() {
+ String[] classpath = pluginInfo.getLibrariesName();
+ if (classpath.length != 0)
+ generatedManifest.put(Constants.BUNDLE_CLASSPATH, getStringFromArray(classpath, LIST_SEPARATOR));
+ }
+
+ private void generateHeaders() {
+ if (TARGET31.compareTo(target) <= 0)
+ generatedManifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); //$NON-NLS-1$
+ generatedManifest.put(Constants.BUNDLE_NAME, pluginInfo.getPluginName());
+ generatedManifest.put(Constants.BUNDLE_VERSION, pluginInfo.getVersion());
+ generatedManifest.put(Constants.BUNDLE_SYMBOLICNAME, getSymbolicNameEntry());
+ String provider = pluginInfo.getProviderName();
+ if (provider != null)
+ generatedManifest.put(Constants.BUNDLE_VENDOR, provider);
+ if (pluginInfo.isFragment()) {
+ StringBuffer hostBundle = new StringBuffer();
+ hostBundle.append(pluginInfo.getMasterId());
+ String versionRange = getVersionRange(pluginInfo.getMasterVersion(), pluginInfo.getMasterMatch()); // TODO need to get match rule here!
+ if (versionRange != null)
+ hostBundle.append(versionRange);
+ generatedManifest.put(Constants.FRAGMENT_HOST, hostBundle.toString());
+ }
+ }
+
+ /*
+ * Generates an entry in the form:
+ * <symbolic-name>[; singleton=true]
+ */
+ private String getSymbolicNameEntry() {
+ // false is the default, so don't bother adding anything
+ if (!pluginInfo.isSingleton())
+ return pluginInfo.getUniqueId();
+ StringBuffer result = new StringBuffer(pluginInfo.getUniqueId());
+ result.append(SEMICOLON);
+ result.append(Constants.SINGLETON_DIRECTIVE);
+ String assignment = TARGET31.compareTo(target) <= 0 ? ":=" : "="; //$NON-NLS-1$ //$NON-NLS-2$
+ result.append(assignment).append("true"); //$NON-NLS-1$
+ return result.toString();
+ }
+
+ private void generatePluginClass() {
+ if (requireRuntimeCompatibility()) {
+ String pluginClass = pluginInfo.getPluginClass();
+ if (pluginClass != null)
+ generatedManifest.put(Constants.PLUGIN_CLASS, pluginClass);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void generateProvidePackage() {
+ Set<String> exports = getExports();
+ if (exports != null && exports.size() != 0) {
+ generatedManifest.put(TARGET31.compareTo(target) <= 0 ? Constants.EXPORT_PACKAGE : Constants.PROVIDE_PACKAGE, getStringFromCollection(exports, LIST_SEPARATOR));
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void generateRequireBundle() {
+ ArrayList<PluginParser.Prerequisite> requiredBundles = pluginInfo.getRequires();
+ if (requiredBundles.size() == 0)
+ return;
+ StringBuffer bundleRequire = new StringBuffer();
+ for (Iterator<PluginParser.Prerequisite> iter = requiredBundles.iterator(); iter.hasNext();) {
+ PluginParser.Prerequisite element = iter.next();
+ StringBuffer modImport = new StringBuffer(element.getName());
+ String versionRange = getVersionRange(element.getVersion(), element.getMatch());
+ if (versionRange != null)
+ modImport.append(versionRange);
+ if (element.isExported()) {
+ if (TARGET31.compareTo(target) <= 0)
+ modImport.append(';').append(Constants.VISIBILITY_DIRECTIVE).append(":=").append(Constants.VISIBILITY_REEXPORT);//$NON-NLS-1$
+ else
+ modImport.append(';').append(Constants.REPROVIDE_ATTRIBUTE).append("=true");//$NON-NLS-1$
+ }
+ if (element.isOptional()) {
+ if (TARGET31.compareTo(target) <= 0)
+ modImport.append(';').append(Constants.RESOLUTION_DIRECTIVE).append(":=").append(Constants.RESOLUTION_OPTIONAL);//$NON-NLS-1$
+ else
+ modImport.append(';').append(Constants.OPTIONAL_ATTRIBUTE).append("=true");//$NON-NLS-1$
+ }
+ bundleRequire.append(modImport.toString());
+ if (iter.hasNext())
+ bundleRequire.append(LIST_SEPARATOR);
+ }
+ generatedManifest.put(Constants.REQUIRE_BUNDLE, bundleRequire.toString());
+ }
+
+ private void generateTimestamp() {
+ // so it is easy to tell which ones are generated
+ generatedManifest.put(GENERATED_FROM, Long.toString(getTimeStamp(pluginManifestLocation, manifestType)) + ";" + MANIFEST_TYPE_ATTRIBUTE + "=" + manifestType); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ @SuppressWarnings("deprecation")
+ private void generateEclipseHeaders() {
+ if (pluginInfo.isFragment())
+ return;
+
+ String pluginClass = pluginInfo.getPluginClass();
+ if (pluginInfo.hasExtensionExtensionPoints() || (pluginClass != null && !pluginClass.trim().equals(""))) //$NON-NLS-1$
+ generatedManifest.put(TARGET32.compareTo(target) <= 0 ? Constants.ECLIPSE_LAZYSTART : Constants.ECLIPSE_AUTOSTART, "true"); //$NON-NLS-1$
+ }
+
+ private Set<String> getExports() {
+ Map<String, List<String>> libs = pluginInfo.getLibraries();
+ if (libs == null)
+ return null;
+
+ //If we are in dev mode, then add the binary folders on the list libs with the export clause set to be the cumulation of the export clause of the real libs
+ if (devProperties != null || DevClassPathHelper.inDevelopmentMode()) {
+ String[] devClassPath = DevClassPathHelper.getDevClassPath(pluginInfo.getUniqueId(), devProperties);
+ // collect export clauses
+ List<String> allExportClauses = new ArrayList<String>(libs.size());
+ Set<Map.Entry<String, List<String>>> libEntries = libs.entrySet();
+ for (Iterator<Map.Entry<String, List<String>>> iter = libEntries.iterator(); iter.hasNext();) {
+ Map.Entry<String, List<String>> element = iter.next();
+ allExportClauses.addAll(element.getValue());
+ }
+ if (devClassPath != null) {
+ // bug 88498
+ // if there is a devClassPath defined for this plugin and the @ignoredot@ flag is true
+ // then we will ignore the '.' library specified in the plugin.xml
+ String[] ignoreDotProp = DevClassPathHelper.getDevClassPath(IGNORE_DOT, devProperties);
+ if (devClassPath.length > 0 && ignoreDotProp != null && ignoreDotProp.length > 0 && "true".equals(ignoreDotProp[0])) //$NON-NLS-1$
+ libs.remove(DOT);
+ for (int i = 0; i < devClassPath.length; i++)
+ libs.put(devClassPath[i], allExportClauses);
+ }
+ }
+
+ Set<String> result = new TreeSet<String>();
+ Set<Map.Entry<String, List<String>>> libEntries = libs.entrySet();
+ for (Iterator<Map.Entry<String, List<String>>> iter = libEntries.iterator(); iter.hasNext();) {
+ Map.Entry<String, List<String>> element = iter.next();
+ List<String> filter = element.getValue();
+ if (filter.size() == 0) //If the library is not exported, then ignore it
+ continue;
+ String libEntryText = element.getKey().trim();
+ File libraryLocation;
+ if (libEntryText.equals(DOT))
+ libraryLocation = pluginManifestLocation;
+ else {
+ // in development time, libEntries may contain absolute locations (linked folders)
+ File libEntryAsPath = new File(libEntryText);
+ libraryLocation = libEntryAsPath.isAbsolute() ? libEntryAsPath : new File(pluginManifestLocation, libEntryText);
+ }
+ Set<String> exports = null;
+ if (libraryLocation.exists()) {
+ if (libraryLocation.isFile())
+ exports = filterExport(getExportsFromJAR(libraryLocation), filter); //TODO Need to handle $xx$ variables
+ else if (libraryLocation.isDirectory())
+ exports = filterExport(getExportsFromDir(libraryLocation), filter);
+ } else {
+ List<String> expandedLibs = getLibrariesExpandingVariables(element.getKey(), false);
+ exports = new HashSet<String>();
+ for (Iterator<String> iterator = expandedLibs.iterator(); iterator.hasNext();) {
+ String libName = iterator.next();
+ File libFile = new File(pluginManifestLocation, libName);
+ if (libFile.isFile()) {
+ exports.addAll(filterExport(getExportsFromJAR(libFile), filter));
+ }
+ }
+ }
+ if (exports != null)
+ result.addAll(exports);
+ }
+ return result;
+ }
+
+ private Set<String> getExportsFromDir(File location) {
+ return getExportsFromDir(location, ""); //$NON-NLS-1$
+ }
+
+ private Set<String> getExportsFromDir(File location, String packageName) {
+ String prefix = (packageName.length() > 0) ? (packageName + '.') : ""; //$NON-NLS-1$
+ String[] files = location.list();
+ Set<String> exportedPaths = new HashSet<String>();
+ boolean containsFile = false;
+ if (files != null)
+ for (int i = 0; i < files.length; i++) {
+ if (!isValidPackageName(files[i]))
+ continue;
+ File pkgFile = new File(location, files[i]);
+ if (pkgFile.isDirectory())
+ exportedPaths.addAll(getExportsFromDir(pkgFile, prefix + files[i]));
+ else
+ containsFile = true;
+ }
+ if (containsFile)
+ // Allow the default package to be provided. If the default package
+ // contains a File then use "." as the package name to provide for default.
+ if (packageName.length() > 0)
+ exportedPaths.add(packageName);
+ else
+ exportedPaths.add(DOT);
+ return exportedPaths;
+ }
+
+ private Set<String> getExportsFromJAR(File jarFile) {
+ Set<String> names = new HashSet<String>();
+ ZipFile file = null;
+ try {
+ file = new ZipFile(jarFile);
+ } catch (IOException e) {
+ String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_PLUGIN_LIBRARY_IGNORED, jarFile, pluginInfo.getUniqueId());
+ adaptor.getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, e, null));
+ return names;
+ }
+ //Run through the entries
+ for (Enumeration<? extends ZipEntry> entriesEnum = file.entries(); entriesEnum.hasMoreElements();) {
+ ZipEntry entry = entriesEnum.nextElement();
+ String name = entry.getName();
+ if (!isValidPackageName(name))
+ continue;
+ int lastSlash = name.lastIndexOf("/"); //$NON-NLS-1$
+ //Ignore folders that do not contain files
+ if (lastSlash != -1) {
+ if (lastSlash != name.length() - 1 && name.lastIndexOf(' ') == -1)
+ names.add(name.substring(0, lastSlash).replace('/', '.'));
+ } else {
+ // Allow the default package to be provided. If the default package
+ // contains a File then use "." as the package name to provide for default.
+ names.add(DOT);
+ }
+ }
+ try {
+ file.close();
+ } catch (IOException e) {
+ // Nothing to do
+ }
+ return names;
+ }
+
+ private List<String> getLibrariesExpandingVariables(String libraryPath, boolean filter) {
+ String var = hasPrefix(libraryPath);
+ if (var == null) {
+ List<String> returnValue = new ArrayList<String>(1);
+ returnValue.add(libraryPath);
+ return returnValue;
+ }
+ if (var.equals("ws")) { //$NON-NLS-1$
+ return findWSJars(pluginManifestLocation, libraryPath, filter);
+ }
+ if (var.equals("os")) { //$NON-NLS-1$
+ return findOSJars(pluginManifestLocation, libraryPath, filter);
+ }
+ return new ArrayList<String>(0);
+ }
+
+ //return a String representing the string found between the $s
+ private String hasPrefix(String libPath) {
+ if (libPath.startsWith("$ws$")) //$NON-NLS-1$
+ return "ws"; //$NON-NLS-1$
+ if (libPath.startsWith("$os$")) //$NON-NLS-1$
+ return "os"; //$NON-NLS-1$
+ if (libPath.startsWith("$nl$")) //$NON-NLS-1$
+ return "nl"; //$NON-NLS-1$
+ return null;
+ }
+
+ private boolean isValidPackageName(String name) {
+ if (name.indexOf(' ') > 0 || name.equalsIgnoreCase("META-INF") || name.startsWith("META-INF/")) //$NON-NLS-1$ //$NON-NLS-2$
+ return false;
+ return true;
+ }
+
+ /**
+ * Parses the plugin manifest to find out: - the plug-in unique identifier -
+ * the plug-in version - runtime/libraries entries - the plug-in class -
+ * the master plugin (for a fragment)
+ */
+ private IPluginInfo parsePluginInfo(InputStream pluginLocation) throws PluginConversionException {
+ InputStream input = null;
+ try {
+ input = new BufferedInputStream(pluginLocation);
+ return new PluginParser(adaptor, context, target).parsePlugin(input);
+ } catch (Exception e) {
+ String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_ERROR_PARSING_PLUGIN_MANIFEST, pluginManifestLocation);
+ throw new PluginConversionException(message, e);
+ } finally {
+ if (input != null)
+ try {
+ input.close();
+ } catch (IOException e) {
+ //ignore exception
+ }
+ }
+ }
+
+ public static boolean upToDate(File generationLocation, File pluginLocation, byte manifestType) {
+ if (!generationLocation.isFile())
+ return false;
+ String secondLine = null;
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(generationLocation)));
+ reader.readLine();
+ secondLine = reader.readLine();
+ } catch (IOException e) {
+ // not a big deal - we could not read an existing manifest
+ return false;
+ } finally {
+ if (reader != null)
+ try {
+ reader.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ String tag = GENERATED_FROM + ": "; //$NON-NLS-1$
+ if (secondLine == null || !secondLine.startsWith(tag))
+ return false;
+
+ secondLine = secondLine.substring(tag.length());
+ ManifestElement generatedFrom;
+ try {
+ generatedFrom = ManifestElement.parseHeader(PluginConverterImpl.GENERATED_FROM, secondLine)[0];
+ } catch (BundleException be) {
+ return false;
+ }
+ String timestampStr = generatedFrom.getValue();
+ try {
+ return Long.parseLong(timestampStr.trim()) == getTimeStamp(pluginLocation, manifestType);
+ } catch (NumberFormatException nfe) {
+ // not a big deal - just a bogus existing manifest that will be ignored
+ }
+ return false;
+ }
+
+ public static long getTimeStamp(File pluginLocation, byte manifestType) {
+ if ((manifestType & MANIFEST_TYPE_JAR) != 0)
+ return pluginLocation.lastModified();
+ else if ((manifestType & MANIFEST_TYPE_PLUGIN) != 0)
+ return new File(pluginLocation, PLUGIN_MANIFEST).lastModified();
+ else if ((manifestType & MANIFEST_TYPE_FRAGMENT) != 0)
+ return new File(pluginLocation, FRAGMENT_MANIFEST).lastModified();
+ else if ((manifestType & MANIFEST_TYPE_BUNDLE) != 0)
+ return new File(pluginLocation, Constants.OSGI_BUNDLE_MANIFEST).lastModified();
+ return -1;
+ }
+
+ private void writeEntry(String key, String value) throws IOException {
+ if (value != null && value.length() > 0) {
+ out.write(splitOnComma(key + ": " + value)); //$NON-NLS-1$
+ out.write('\n');
+ }
+ }
+
+ private String splitOnComma(String value) {
+ if (value.length() < MAXLINE || value.indexOf(LINE_SEPARATOR) >= 0)
+ return value; // assume the line is already split
+ String[] values = ManifestElement.getArrayFromList(value);
+ if (values == null || values.length == 0)
+ return value;
+ StringBuffer sb = new StringBuffer(value.length() + ((values.length - 1) * LIST_SEPARATOR.length()));
+ for (int i = 0; i < values.length - 1; i++)
+ sb.append(values[i]).append(LIST_SEPARATOR);
+ sb.append(values[values.length - 1]);
+ return sb.toString();
+ }
+
+ private String getStringFromArray(String[] values, String separator) {
+ if (values == null)
+ return ""; //$NON-NLS-1$
+ StringBuffer result = new StringBuffer();
+ for (int i = 0; i < values.length; i++) {
+ if (i > 0)
+ result.append(separator);
+ result.append(values[i]);
+ }
+ return result.toString();
+ }
+
+ private String getStringFromCollection(Collection<String> collection, String separator) {
+ StringBuffer result = new StringBuffer();
+ boolean first = true;
+ for (Iterator<String> i = collection.iterator(); i.hasNext();) {
+ if (first)
+ first = false;
+ else
+ result.append(separator);
+ result.append(i.next());
+ }
+ return result.toString();
+ }
+
+ public synchronized Dictionary<String, String> convertManifest(File pluginBaseLocation, boolean compatibility, String targetVersion, boolean analyseJars, Dictionary<String, String> devProps) throws PluginConversionException {
+ long start = System.currentTimeMillis();
+ if (DEBUG)
+ System.out.println("Convert " + pluginBaseLocation); //$NON-NLS-1$
+ init();
+ this.target = targetVersion == null ? TARGET32 : new Version(targetVersion);
+ this.devProperties = devProps;
+ fillPluginInfo(pluginBaseLocation);
+ fillManifest(compatibility, analyseJars);
+ if (DEBUG)
+ System.out.println("Time to convert manifest for: " + pluginBaseLocation + ": " + (System.currentTimeMillis() - start) + "ms."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return generatedManifest;
+ }
+
+ public synchronized File convertManifest(File pluginBaseLocation, File bundleManifestLocation, boolean compatibilityManifest, String targetVersion, boolean analyseJars, Dictionary<String, String> devProps) throws PluginConversionException {
+ convertManifest(pluginBaseLocation, compatibilityManifest, targetVersion, analyseJars, devProps);
+ if (bundleManifestLocation == null) {
+ String cacheLocation = FrameworkProperties.getProperty(LocationManager.PROP_MANIFEST_CACHE);
+ bundleManifestLocation = new File(cacheLocation, pluginInfo.getUniqueId() + '_' + pluginInfo.getVersion() + ".MF"); //$NON-NLS-1$
+ }
+ if (upToDate(bundleManifestLocation, pluginManifestLocation, manifestType))
+ return bundleManifestLocation;
+ writeManifest(bundleManifestLocation, generatedManifest, compatibilityManifest);
+ return bundleManifestLocation;
+ }
+
+ private String getVersionRange(String reqVersion, String matchRule) {
+ if (reqVersion == null)
+ return null;
+
+ Version minVersion = Version.parseVersion(reqVersion);
+ String versionRange;
+ if (matchRule != null) {
+ if (matchRule.equalsIgnoreCase(IModel.PLUGIN_REQUIRES_MATCH_PERFECT)) {
+ versionRange = new VersionRange(minVersion, true, minVersion, true).toString();
+ } else if (matchRule.equalsIgnoreCase(IModel.PLUGIN_REQUIRES_MATCH_EQUIVALENT)) {
+ versionRange = new VersionRange(minVersion, true, new Version(minVersion.getMajor(), minVersion.getMinor() + 1, 0, ""), false).toString(); //$NON-NLS-1$
+ } else if (matchRule.equalsIgnoreCase(IModel.PLUGIN_REQUIRES_MATCH_COMPATIBLE)) {
+ versionRange = new VersionRange(minVersion, true, new Version(minVersion.getMajor() + 1, 0, 0, ""), false).toString(); //$NON-NLS-1$
+ } else if (matchRule.equalsIgnoreCase(IModel.PLUGIN_REQUIRES_MATCH_GREATER_OR_EQUAL)) {
+ // just return the reqVersion here without any version range
+ versionRange = reqVersion;
+ } else {
+ versionRange = new VersionRange(minVersion, true, new Version(minVersion.getMajor() + 1, 0, 0, ""), false).toString(); //$NON-NLS-1$
+ }
+ } else {
+ versionRange = new VersionRange(minVersion, true, new Version(minVersion.getMajor() + 1, 0, 0, ""), false).toString(); //$NON-NLS-1$
+ }
+
+ StringBuffer result = new StringBuffer();
+ result.append(';').append(Constants.BUNDLE_VERSION_ATTRIBUTE).append('=');
+ result.append('\"').append(versionRange).append('\"');
+ return result.toString();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/PluginParser.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/PluginParser.java
new file mode 100644
index 000000000..1448b28ff
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/PluginParser.java
@@ -0,0 +1,710 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.InputStream;
+import java.util.*;
+import javax.xml.parsers.SAXParserFactory;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+import org.osgi.util.tracker.ServiceTracker;
+import org.xml.sax.*;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Internal class.
+ */
+public class PluginParser extends DefaultHandler implements IModel {
+ private static ServiceTracker<SAXParserFactory, SAXParserFactory> xmlTracker = null;
+
+ private PluginInfo manifestInfo = new PluginInfo();
+ private BundleContext context;
+ private FrameworkAdaptor adaptor;
+ Version target; // The targeted platform for the given manifest
+ static final Version TARGET21 = new Version(2, 1, 0);
+
+ public class PluginInfo implements IPluginInfo {
+ String schemaVersion;
+ String pluginId;
+ String version;
+ String vendor;
+
+ // an ordered list of library path names.
+ List<String> libraryPaths;
+ // TODO Should get rid of the libraries map and just have a
+ // list of library export statements instead. Library paths must
+ // preserve order.
+ Map<String, List<String>> libraries; //represent the libraries and their export statement
+ ArrayList<PluginParser.Prerequisite> requires;
+ private boolean requiresExpanded = false; //indicates if the requires have been processed.
+ boolean compatibilityFound = false; //set to true is the requirement list contain compatilibity
+ String pluginClass;
+ String masterPluginId;
+ String masterVersion;
+ String masterMatch;
+ private Set<String> filters;
+ String pluginName;
+ boolean singleton;
+ boolean fragment;
+ private final static String TARGET21_STRING = "2.1"; //$NON-NLS-1$
+ boolean hasExtensionExtensionPoints = false;
+
+ public boolean isFragment() {
+ return fragment;
+ }
+
+ public String toString() {
+ return "plugin-id: " + pluginId + " version: " + version + " libraries: " + libraries + " class:" + pluginClass + " master: " + masterPluginId + " master-version: " + masterVersion + " requires: " + requires + " singleton: " + singleton; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
+ }
+
+ public Map<String, List<String>> getLibraries() {
+ if (libraries == null)
+ return new HashMap<String, List<String>>(0);
+ return libraries;
+ }
+
+ public ArrayList<Prerequisite> getRequires() {
+ if (!TARGET21.equals(target) && schemaVersion == null && !requiresExpanded) {
+ requiresExpanded = true;
+ if (requires == null) {
+ requires = new ArrayList<Prerequisite>(1);
+ requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME, TARGET21_STRING, false, false, IModel.PLUGIN_REQUIRES_MATCH_GREATER_OR_EQUAL));
+ requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, false, null));
+ } else {
+ //Add elements on the requirement list of ui and help.
+ for (int i = 0; i < requires.size(); i++) {
+ Prerequisite analyzed = requires.get(i);
+ if ("org.eclipse.ui".equals(analyzed.getName())) { //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.ui.workbench.texteditor", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.jface.text", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.ui.editors", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.ui.views", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.ui.ide", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ } else if ("org.eclipse.help".equals(analyzed.getName())) { //$NON-NLS-1$
+ requires.add(i + 1, new Prerequisite("org.eclipse.help.base", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
+ } else if (PluginConverterImpl.PI_RUNTIME.equals(analyzed.getName()) && !compatibilityFound) {
+ requires.add(i + 1, new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, analyzed.isExported(), null));
+ }
+ }
+ if (!requires.contains(new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, false, null))) {
+ requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, false, null));
+ }
+ //Remove any prereq on runtime and add a prereq on runtime 2.1
+ //This is used to recognize the version for which the given plugin was initially targeted.
+ Prerequisite runtimePrereq = new Prerequisite(PluginConverterImpl.PI_RUNTIME, null, false, false, null);
+ requires.remove(runtimePrereq);
+ requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME, TARGET21_STRING, false, false, IModel.PLUGIN_REQUIRES_MATCH_GREATER_OR_EQUAL));
+ }
+ }
+ if (requires == null)
+ return requires = new ArrayList<Prerequisite>(0);
+
+ return requires;
+ }
+
+ public String getMasterId() {
+ return masterPluginId;
+ }
+
+ public String getMasterVersion() {
+ return masterVersion;
+ }
+
+ public String getMasterMatch() {
+ return masterMatch;
+ }
+
+ public String getPluginClass() {
+ return pluginClass;
+ }
+
+ public String getUniqueId() {
+ return pluginId;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public Set<String> getPackageFilters() {
+ return filters;
+ }
+
+ public String[] getLibrariesName() {
+ if (libraryPaths == null)
+ return new String[0];
+ return libraryPaths.toArray(new String[libraryPaths.size()]);
+ }
+
+ public String getPluginName() {
+ return pluginName;
+ }
+
+ public String getProviderName() {
+ return vendor;
+ }
+
+ public boolean isSingleton() {
+ return singleton;
+ }
+
+ public boolean hasExtensionExtensionPoints() {
+ return hasExtensionExtensionPoints;
+ }
+
+ public String getRoot() {
+ return isFragment() ? FRAGMENT : PLUGIN;
+ }
+
+ /*
+ * Provides some basic form of validation. Since plugin/fragment is the only mandatory
+ * attribute, it is the only one we cara about here.
+ */
+ public String validateForm() {
+ if (this.pluginId == null)
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_MISSING_ATTRIBUTE, new String[] {getRoot(), PLUGIN_ID, getRoot()});
+ if (this.pluginName == null)
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_MISSING_ATTRIBUTE, new String[] {getRoot(), PLUGIN_NAME, getRoot()});
+ if (this.version == null)
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_MISSING_ATTRIBUTE, new String[] {getRoot(), PLUGIN_VERSION, getRoot()});
+ if (isFragment() && this.masterPluginId == null)
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_MISSING_ATTRIBUTE, new String[] {getRoot(), FRAGMENT_PLUGIN_ID, getRoot()});
+ if (isFragment() && this.masterVersion == null)
+ return NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_MISSING_ATTRIBUTE, new String[] {getRoot(), FRAGMENT_PLUGIN_VERSION, getRoot()});
+ return null;
+ }
+ }
+
+ // Current State Information
+ Stack<Integer> stateStack = new Stack<Integer>();
+
+ // Current object stack (used to hold the current object we are populating in this plugin info
+ Stack<Object> objectStack = new Stack<Object>();
+ Locator locator = null;
+
+ // Valid States
+ private static final int IGNORED_ELEMENT_STATE = 0;
+ private static final int INITIAL_STATE = 1;
+ private static final int PLUGIN_STATE = 2;
+ private static final int PLUGIN_RUNTIME_STATE = 3;
+ private static final int PLUGIN_REQUIRES_STATE = 4;
+ private static final int PLUGIN_EXTENSION_POINT_STATE = 5;
+ private static final int PLUGIN_EXTENSION_STATE = 6;
+ private static final int RUNTIME_LIBRARY_STATE = 7;
+ private static final int LIBRARY_EXPORT_STATE = 8;
+ private static final int PLUGIN_REQUIRES_IMPORT_STATE = 9;
+ private static final int FRAGMENT_STATE = 11;
+
+ public PluginParser(FrameworkAdaptor adaptor, BundleContext context, Version target) {
+ super();
+ this.context = context;
+ this.adaptor = adaptor;
+ this.target = target;
+ }
+
+ /**
+ * Receive a Locator object for document events.
+ *
+ * <p>
+ * By default, do nothing. Application writers may override this method in
+ * a subclass if they wish to store the locator for use with other document
+ * events.
+ * </p>
+ *
+ * @param locator A locator for all SAX document events.
+ * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
+ * @see org.xml.sax.Locator
+ */
+ public void setDocumentLocator(Locator locator) {
+ this.locator = locator;
+ }
+
+ public void endDocument() {
+ // nothing
+ }
+
+ public void endElement(String uri, String elementName, String qName) {
+ switch (stateStack.peek().intValue()) {
+ case IGNORED_ELEMENT_STATE :
+ stateStack.pop();
+ break;
+ case INITIAL_STATE :
+ // shouldn't get here
+ // internalError(Policy.bind("parse.internalStack", elementName)); //$NON-NLS-1$
+ break;
+ case PLUGIN_STATE :
+ case FRAGMENT_STATE :
+ break;
+ case PLUGIN_RUNTIME_STATE :
+ if (elementName.equals(RUNTIME)) {
+ stateStack.pop();
+ }
+ break;
+ case PLUGIN_REQUIRES_STATE :
+ if (elementName.equals(PLUGIN_REQUIRES)) {
+ stateStack.pop();
+ objectStack.pop();
+ }
+ break;
+ case PLUGIN_EXTENSION_POINT_STATE :
+ if (elementName.equals(EXTENSION_POINT)) {
+ stateStack.pop();
+ }
+ break;
+ case PLUGIN_EXTENSION_STATE :
+ if (elementName.equals(EXTENSION)) {
+ stateStack.pop();
+ }
+ break;
+ case RUNTIME_LIBRARY_STATE :
+ if (elementName.equals(LIBRARY)) {
+ String curLibrary = (String) objectStack.pop();
+ if (!curLibrary.trim().equals("")) { //$NON-NLS-1$
+ @SuppressWarnings("unchecked")
+ List<String> exports = (List<String>) objectStack.pop();
+ if (manifestInfo.libraries == null) {
+ manifestInfo.libraries = new HashMap<String, List<String>>(3);
+ manifestInfo.libraryPaths = new ArrayList<String>(3);
+ }
+ manifestInfo.libraries.put(curLibrary, exports);
+ manifestInfo.libraryPaths.add(curLibrary.replace('\\', '/'));
+ }
+ stateStack.pop();
+ }
+ break;
+ case LIBRARY_EXPORT_STATE :
+ if (elementName.equals(LIBRARY_EXPORT)) {
+ stateStack.pop();
+ }
+ break;
+ case PLUGIN_REQUIRES_IMPORT_STATE :
+ if (elementName.equals(PLUGIN_REQUIRES_IMPORT)) {
+ stateStack.pop();
+ }
+ break;
+ }
+ }
+
+ public void error(SAXParseException ex) {
+ logStatus(ex);
+ }
+
+ public void fatalError(SAXParseException ex) throws SAXException {
+ logStatus(ex);
+ throw ex;
+ }
+
+ public void handleExtensionPointState(String elementName, Attributes attributes) {
+ // nothing to do for extension-points' children
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ manifestInfo.hasExtensionExtensionPoints = true;
+ }
+
+ public void handleExtensionState(String elementName, Attributes attributes) {
+ // nothing to do for extensions' children
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ manifestInfo.hasExtensionExtensionPoints = true;
+ }
+
+ public void handleInitialState(String elementName, Attributes attributes) {
+ if (elementName.equals(PLUGIN)) {
+ stateStack.push(new Integer(PLUGIN_STATE));
+ parsePluginAttributes(attributes);
+ } else if (elementName.equals(FRAGMENT)) {
+ manifestInfo.fragment = true;
+ stateStack.push(new Integer(FRAGMENT_STATE));
+ parseFragmentAttributes(attributes);
+ } else {
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ internalError(elementName);
+ }
+ }
+
+ public void handleLibraryExportState(String elementName, Attributes attributes) {
+ // All elements ignored.
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ }
+
+ public void handleLibraryState(String elementName, Attributes attributes) {
+ if (elementName.equals(LIBRARY_EXPORT)) {
+ // Change State
+ stateStack.push(new Integer(LIBRARY_EXPORT_STATE));
+ // The top element on the stack much be a library element
+ String currentLib = (String) objectStack.peek();
+ if (attributes == null)
+ return;
+ String maskValue = attributes.getValue("", LIBRARY_EXPORT_MASK); //$NON-NLS-1$
+ // pop off the library - already in currentLib
+ objectStack.pop();
+ @SuppressWarnings("unchecked")
+ List<String> exportMask = (List<String>) objectStack.peek();
+ // push library back on
+ objectStack.push(currentLib);
+ //Split the export upfront
+ if (maskValue != null) {
+ StringTokenizer tok = new StringTokenizer(maskValue, ","); //$NON-NLS-1$
+ while (tok.hasMoreTokens()) {
+ String value = tok.nextToken();
+ if (!exportMask.contains(maskValue))
+ exportMask.add(value.trim());
+ }
+ }
+ return;
+ }
+ if (elementName.equals(LIBRARY_PACKAGES)) {
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ return;
+ }
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ internalError(elementName);
+ return;
+ }
+
+ public void handlePluginState(String elementName, Attributes attributes) {
+ if (elementName.equals(RUNTIME)) {
+ // We should only have one Runtime element in a plugin or fragment
+ Object whatIsIt = objectStack.peek();
+ if ((whatIsIt instanceof PluginInfo) && ((PluginInfo) objectStack.peek()).libraries != null) {
+ // This is at least the 2nd Runtime element we have hit. Ignore it.
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ return;
+ }
+ stateStack.push(new Integer(PLUGIN_RUNTIME_STATE));
+ // Push a new vector to hold all the library entries objectStack.push(new Vector());
+ return;
+ }
+ if (elementName.equals(PLUGIN_REQUIRES)) {
+ stateStack.push(new Integer(PLUGIN_REQUIRES_STATE));
+ // Push a new vector to hold all the prerequisites
+ objectStack.push(new ArrayList<String>());
+ parseRequiresAttributes(attributes);
+ return;
+ }
+ if (elementName.equals(EXTENSION_POINT)) {
+ // mark the plugin as singleton and ignore all elements under extension (if there are any)
+ manifestInfo.singleton = true;
+ stateStack.push(new Integer(PLUGIN_EXTENSION_POINT_STATE));
+ return;
+ }
+ if (elementName.equals(EXTENSION)) {
+ // mark the plugin as singleton and ignore all elements under extension (if there are any)
+ manifestInfo.singleton = true;
+ stateStack.push(new Integer(PLUGIN_EXTENSION_STATE));
+ return;
+ }
+ // If we get to this point, the element name is one we don't currently accept.
+ // Set the state to indicate that this element will be ignored
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ internalError(elementName);
+ }
+
+ public void handleRequiresImportState(String elementName, Attributes attributes) {
+ // All elements ignored.
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ }
+
+ public void handleRequiresState(String elementName, Attributes attributes) {
+ if (elementName.equals(PLUGIN_REQUIRES_IMPORT)) {
+ parsePluginRequiresImport(attributes);
+ return;
+ }
+ // If we get to this point, the element name is one we don't currently accept.
+ // Set the state to indicate that this element will be ignored
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ internalError(elementName);
+ }
+
+ public void handleRuntimeState(String elementName, Attributes attributes) {
+ if (elementName.equals(LIBRARY)) {
+ // Change State
+ stateStack.push(new Integer(RUNTIME_LIBRARY_STATE));
+ // Process library attributes
+ parseLibraryAttributes(attributes);
+ return;
+ }
+ // If we get to this point, the element name is one we don't currently accept.
+ // Set the state to indicate that this element will be ignored
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ internalError(elementName);
+ }
+
+ private void logStatus(SAXParseException ex) {
+ String name = ex.getSystemId();
+ if (name == null)
+ name = ""; //$NON-NLS-1$
+ else
+ name = name.substring(1 + name.lastIndexOf("/")); //$NON-NLS-1$
+ String msg;
+ if (name.equals("")) //$NON-NLS-1$
+ msg = NLS.bind(EclipseAdaptorMsg.parse_error, ex.getMessage());
+ else
+ msg = NLS.bind(EclipseAdaptorMsg.parse_errorNameLineColumn, new String[] {name, Integer.toString(ex.getLineNumber()), Integer.toString(ex.getColumnNumber()), ex.getMessage()});
+
+ FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, msg, 0, ex, null);
+ adaptor.getFrameworkLog().log(entry);
+ }
+
+ synchronized public PluginInfo parsePlugin(InputStream in) throws Exception {
+ SAXParserFactory factory = acquireXMLParsing(context);
+ if (factory == null) {
+ FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, EclipseAdaptorMsg.ECLIPSE_CONVERTER_NO_SAX_FACTORY, 0, null, null);
+ adaptor.getFrameworkLog().log(entry);
+ return null;
+ }
+
+ factory.setNamespaceAware(true);
+ factory.setNamespaceAware(true);
+ try {
+ factory.setFeature("http://xml.org/sax/features/string-interning", true); //$NON-NLS-1$
+ } catch (SAXException se) {
+ // ignore; we can still operate without string-interning
+ }
+ factory.setValidating(false);
+ factory.newSAXParser().parse(in, this);
+ return manifestInfo;
+ }
+
+ public static SAXParserFactory acquireXMLParsing(BundleContext context) {
+ if (xmlTracker == null) {
+ xmlTracker = new ServiceTracker<SAXParserFactory, SAXParserFactory>(context, "javax.xml.parsers.SAXParserFactory", null); //$NON-NLS-1$
+ xmlTracker.open();
+ }
+ SAXParserFactory result = xmlTracker.getService();
+ if (result != null)
+ return result;
+ // backup to using jaxp to create a new instance
+ return SAXParserFactory.newInstance();
+ }
+
+ public static void releaseXMLParsing() {
+ if (xmlTracker != null)
+ xmlTracker.close();
+ }
+
+ public void parseFragmentAttributes(Attributes attributes) {
+ // process attributes
+ objectStack.push(manifestInfo);
+ int len = attributes.getLength();
+ for (int i = 0; i < len; i++) {
+ String attrName = attributes.getLocalName(i);
+ String attrValue = attributes.getValue(i).trim();
+ if (attrName.equals(FRAGMENT_ID))
+ manifestInfo.pluginId = attrValue;
+ else if (attrName.equals(FRAGMENT_NAME))
+ manifestInfo.pluginName = attrValue;
+ else if (attrName.equals(FRAGMENT_VERSION))
+ manifestInfo.version = attrValue;
+ else if (attrName.equals(FRAGMENT_PROVIDER))
+ manifestInfo.vendor = attrValue;
+ else if (attrName.equals(FRAGMENT_PLUGIN_ID))
+ manifestInfo.masterPluginId = attrValue;
+ else if (attrName.equals(FRAGMENT_PLUGIN_VERSION))
+ manifestInfo.masterVersion = attrValue;
+ else if (attrName.equals(FRAGMENT_PLUGIN_MATCH))
+ manifestInfo.masterMatch = attrValue;
+ }
+ }
+
+ public void parseLibraryAttributes(Attributes attributes) {
+ // Push a vector to hold the export mask
+ objectStack.push(new ArrayList<String>());
+ String current = attributes.getValue("", LIBRARY_NAME); //$NON-NLS-1$
+ objectStack.push(current);
+ }
+
+ public void parsePluginAttributes(Attributes attributes) {
+ // process attributes
+ objectStack.push(manifestInfo);
+ int len = attributes.getLength();
+ for (int i = 0; i < len; i++) {
+ String attrName = attributes.getLocalName(i);
+ String attrValue = attributes.getValue(i).trim();
+ if (attrName.equals(PLUGIN_ID))
+ manifestInfo.pluginId = attrValue;
+ else if (attrName.equals(PLUGIN_NAME))
+ manifestInfo.pluginName = attrValue;
+ else if (attrName.equals(PLUGIN_VERSION))
+ manifestInfo.version = attrValue;
+ else if (attrName.equals(PLUGIN_VENDOR) || (attrName.equals(PLUGIN_PROVIDER)))
+ manifestInfo.vendor = attrValue;
+ else if (attrName.equals(PLUGIN_CLASS))
+ manifestInfo.pluginClass = attrValue;
+ }
+ }
+
+ public class Prerequisite {
+ String name;
+ String version;
+ boolean optional;
+ boolean export;
+ String match;
+
+ public boolean isExported() {
+ return export;
+ }
+
+ public String getMatch() {
+ return match;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public Prerequisite(String preqName, String prereqVersion, boolean isOtional, boolean isExported, String prereqMatch) {
+ name = preqName;
+ version = prereqVersion;
+ optional = isOtional;
+ export = isExported;
+ match = prereqMatch;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public boolean equals(Object prereq) {
+ if (!(prereq instanceof Prerequisite))
+ return false;
+ return name.equals(((Prerequisite) prereq).name);
+ }
+
+ public int hashCode() {
+ return name.hashCode();
+ }
+ }
+
+ public void parsePluginRequiresImport(Attributes attributes) {
+ if (manifestInfo.requires == null) {
+ manifestInfo.requires = new ArrayList<Prerequisite>();
+ // to avoid cycles
+ // if (!manifestInfo.pluginId.equals(PluginConverterImpl.PI_RUNTIME)) //$NON-NLS-1$
+ // manifestInfo.requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME, null, false, false, null)); //$NON-NLS-1$
+ }
+ // process attributes
+ String plugin = attributes.getValue("", PLUGIN_REQUIRES_PLUGIN); //$NON-NLS-1$
+ if (plugin == null)
+ return;
+ if (plugin.equals(PluginConverterImpl.PI_BOOT))
+ return;
+ if (plugin.equals(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY))
+ manifestInfo.compatibilityFound = true;
+ String version = attributes.getValue("", PLUGIN_REQUIRES_PLUGIN_VERSION); //$NON-NLS-1$
+ String optional = attributes.getValue("", PLUGIN_REQUIRES_OPTIONAL); //$NON-NLS-1$
+ String export = attributes.getValue("", PLUGIN_REQUIRES_EXPORT); //$NON-NLS-1$
+ String match = attributes.getValue("", PLUGIN_REQUIRES_MATCH); //$NON-NLS-1$
+ manifestInfo.requires.add(new Prerequisite(plugin, version, "true".equalsIgnoreCase(optional) ? true : false, "true".equalsIgnoreCase(export) ? true : false, match)); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public void parseRequiresAttributes(Attributes attributes) {
+ //Nothing to do.
+ }
+
+ static String replace(String s, String from, String to) {
+ String str = s;
+ int fromLen = from.length();
+ int toLen = to.length();
+ int ix = str.indexOf(from);
+ while (ix != -1) {
+ str = str.substring(0, ix) + to + str.substring(ix + fromLen);
+ ix = str.indexOf(from, ix + toLen);
+ }
+ return str;
+ }
+
+ public void startDocument() {
+ stateStack.push(new Integer(INITIAL_STATE));
+ }
+
+ public void startElement(String uri, String elementName, String qName, Attributes attributes) {
+ switch (stateStack.peek().intValue()) {
+ case INITIAL_STATE :
+ handleInitialState(elementName, attributes);
+ break;
+ case FRAGMENT_STATE :
+ case PLUGIN_STATE :
+ handlePluginState(elementName, attributes);
+ break;
+ case PLUGIN_RUNTIME_STATE :
+ handleRuntimeState(elementName, attributes);
+ break;
+ case PLUGIN_REQUIRES_STATE :
+ handleRequiresState(elementName, attributes);
+ break;
+ case PLUGIN_EXTENSION_POINT_STATE :
+ handleExtensionPointState(elementName, attributes);
+ break;
+ case PLUGIN_EXTENSION_STATE :
+ handleExtensionState(elementName, attributes);
+ break;
+ case RUNTIME_LIBRARY_STATE :
+ handleLibraryState(elementName, attributes);
+ break;
+ case LIBRARY_EXPORT_STATE :
+ handleLibraryExportState(elementName, attributes);
+ break;
+ case PLUGIN_REQUIRES_IMPORT_STATE :
+ handleRequiresImportState(elementName, attributes);
+ break;
+ default :
+ stateStack.push(new Integer(IGNORED_ELEMENT_STATE));
+ }
+ }
+
+ public void warning(SAXParseException ex) {
+ logStatus(ex);
+ }
+
+ private void internalError(String elementName) {
+ FrameworkLogEntry error;
+ String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_CONVERTER_PARSE_UNKNOWNTOP_ELEMENT, elementName);
+ error = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, (manifestInfo.pluginId == null ? message : "Plug-in : " + manifestInfo.pluginId + ", " + message), 0, null, null); //$NON-NLS-1$ //$NON-NLS-2$
+ adaptor.getFrameworkLog().log(error);
+ }
+
+ /**
+ * @throws SAXException
+ */
+ public void processingInstruction(String instructionTarget, String data) throws SAXException {
+ // Since 3.0, a processing instruction of the form <?eclipse version="3.0"?> at
+ // the start of the manifest file is used to indicate the plug-in manifest
+ // schema version in effect. Pre-3.0 (i.e., 2.1) plug-in manifest files do not
+ // have one of these, and this is how we can distinguish the manifest of a
+ // pre-3.0 plug-in from a post-3.0 one (for compatibility tranformations).
+ if (instructionTarget.equalsIgnoreCase("eclipse")) { //$NON-NLS-1$
+ // just the presence of this processing instruction indicates that this
+ // plug-in is at least 3.0
+ manifestInfo.schemaVersion = "3.0"; //$NON-NLS-1$
+ StringTokenizer tokenizer = new StringTokenizer(data, "=\""); //$NON-NLS-1$
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+ if (token.equalsIgnoreCase("version")) { //$NON-NLS-1$
+ if (!tokenizer.hasMoreTokens()) {
+ break;
+ }
+ manifestInfo.schemaVersion = tokenizer.nextToken();
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/Semaphore.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/Semaphore.java
new file mode 100644
index 000000000..0ef4bffcf
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/Semaphore.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+/**
+ * Internal class.
+ */
+public class Semaphore {
+ protected long notifications;
+
+ public Semaphore(int count) {
+ notifications = count;
+ }
+
+ /**
+ * Attempts to acquire this semaphore. Returns only when the semaphore has been acquired.
+ */
+ public synchronized void acquire() {
+ while (true) {
+ if (notifications > 0) {
+ notifications--;
+ return;
+ }
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ //Ignore
+ }
+ }
+ }
+
+ /**
+ * Attempts to acquire this semaphore. Returns true if it was successfully acquired,
+ * and false otherwise.
+ */
+ public synchronized boolean acquire(long delay) {
+ long start = System.currentTimeMillis();
+ long timeLeft = delay;
+ while (true) {
+ if (notifications > 0) {
+ notifications--;
+ return true;
+ }
+ if (timeLeft <= 0)
+ return false;
+ try {
+ wait(timeLeft);
+ } catch (InterruptedException e) {
+ //Ignore
+ }
+ timeLeft = start + delay - System.currentTimeMillis();
+ }
+ }
+
+ public synchronized void release() {
+ notifications++;
+ notifyAll();
+ }
+
+ // for debug only
+ public String toString() {
+ return "Semaphore(" + notifications + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/URLConverterImpl.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/URLConverterImpl.java
new file mode 100644
index 000000000..c197861e9
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/adaptor/URLConverterImpl.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.adaptor;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import org.eclipse.osgi.framework.internal.core.BundleURLConnection;
+import org.eclipse.osgi.service.urlconversion.URLConverter;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * The service implementation that allows bundleresource or bundleentry
+ * URLs to be converted to native file URLs on the local file system.
+ *
+ * <p>Internal class.</p>
+ */
+public class URLConverterImpl implements URLConverter {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.osgi.service.urlconversion.URLConverter#toFileURL(java.net.URL)
+ */
+ public URL toFileURL(URL url) throws IOException {
+ URLConnection connection = url.openConnection();
+ if (connection instanceof BundleURLConnection) {
+ URL result = ((BundleURLConnection) connection).getFileURL();
+ /* If we got a connection then we know the resource exists in
+ * the bundle but if connection.getFileURL returned null then there
+ * was a problem extracting the file to disk. See bug 259241.
+ **/
+ if (result == null)
+ throw new IOException(NLS.bind(EclipseAdaptorMsg.ECLIPSE_PLUGIN_EXTRACTION_PROBLEM, url));
+ return result;
+ }
+ return url;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.osgi.service.urlconversion.URLConverter#resolve(java.net.URL)
+ */
+ public URL resolve(URL url) throws IOException {
+ URLConnection connection = url.openConnection();
+ if (connection instanceof BundleURLConnection)
+ return ((BundleURLConnection) connection).getLocalURL();
+ return url;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/BundleStats.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/BundleStats.java
new file mode 100644
index 000000000..f021dfc9b
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/BundleStats.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.stats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains information about activated bundles and acts as the main
+ * entry point for logging bundle activity.
+ */
+
+public class BundleStats {
+ public String symbolicName;
+ public long id;
+ public int activationOrder;
+ private long timestamp; //timeStamp at which this bundle has been activated
+ private boolean duringStartup; // indicate if the bundle has been activated during startup
+ private long startupTime; // the time took by the bundle to startup
+ private long startupMethodTime; // the time took to run the startup method
+
+ // Indicate the position of the activation trace in the file
+ private long traceStart = -1;
+ private long traceEnd = -1;
+
+ //To keep bundle parentage
+ private List<BundleStats> bundlesActivated = new ArrayList<BundleStats>(3); // TODO create lazily
+ private BundleStats activatedBy = null;
+
+ public BundleStats(String name, long id) {
+ this.symbolicName = name;
+ this.id = id;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public int getActivationOrder() {
+ return activationOrder;
+ }
+
+ protected void activated(BundleStats info) {
+ bundlesActivated.add(info);
+ }
+
+ public BundleStats getActivatedBy() {
+ return activatedBy;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public String getSymbolicName() {
+ return symbolicName;
+ }
+
+ public long getStartupTime() {
+ return startupTime;
+ }
+
+ public long getStartupMethodTime() {
+ return startupMethodTime;
+ }
+
+ public boolean isStartupBundle() {
+ return duringStartup;
+ }
+
+ public int getClassLoadCount() {
+ if (!StatsManager.MONITOR_CLASSES)
+ return 0;
+ ClassloaderStats loader = ClassloaderStats.getLoader(symbolicName);
+ return loader == null ? 0 : loader.getClassLoadCount();
+ }
+
+ public long getClassLoadTime() {
+ if (!StatsManager.MONITOR_CLASSES)
+ return 0;
+ ClassloaderStats loader = ClassloaderStats.getLoader(symbolicName);
+ return loader == null ? 0 : loader.getClassLoadTime();
+ }
+
+ public List<BundleStats> getBundlesActivated() {
+ return bundlesActivated;
+ }
+
+ public long getTraceStart() {
+ return traceStart;
+ }
+
+ public long getTraceEnd() {
+ return traceEnd;
+ }
+
+ protected void setTimestamp(long value) {
+ timestamp = value;
+ }
+
+ protected void setActivationOrder(int value) {
+ activationOrder = value;
+ }
+
+ protected void setTraceStart(long time) {
+ traceStart = time;
+ }
+
+ protected void setDuringStartup(boolean value) {
+ duringStartup = value;
+ }
+
+ protected void endActivation() {
+ startupTime = System.currentTimeMillis() - timestamp;
+ }
+
+ protected void setTraceEnd(long position) {
+ traceEnd = position;
+ }
+
+ protected void setActivatedBy(BundleStats value) {
+ activatedBy = value;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ClassStats.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ClassStats.java
new file mode 100644
index 000000000..092dd156e
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ClassStats.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.stats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Maintain statistics about a loaded class.
+ */
+public class ClassStats {
+ private String className; // fully qualified name of this class
+ private ClassloaderStats classloader; // the classloader that loaded this class
+ private int loadOrder = -1;
+
+ private long timestamp; // time at which this class was loaded
+ private long timeLoading; // time to load the class
+ private long timeLoadingOthers = 0; // time spent loading classes which has been triggered by this class
+
+ // parentage of classes loaded
+ private ClassStats loadedBy = null; // a reference to the class that loaded this class
+ private List<ClassStats> loaded = new ArrayList<ClassStats>(2); // a reference to the classes that this class loaded
+
+ private boolean duringStartup; // indicate if the class was loaded during platform startup
+
+ //information to retrieve the stacktrace from the file
+ private long traceStart = -1;
+ private long traceEnd = -1;
+
+ public ClassStats(String name, ClassloaderStats classloader) {
+ className = name;
+ timestamp = System.currentTimeMillis();
+ duringStartup = StatsManager.isBooting();
+ this.classloader = classloader;
+ }
+
+ public void setLoadOrder(int order) {
+ loadOrder = order;
+ }
+
+ public void loadingDone() {
+ timeLoading = System.currentTimeMillis() - timestamp;
+ }
+
+ public long getTimeLoading() {
+ return timeLoading;
+ }
+
+ public long getLocalTimeLoading() {
+ return timeLoading - timeLoadingOthers;
+ }
+
+ public void addTimeLoadingOthers(long time) {
+ timeLoadingOthers = timeLoadingOthers + time;
+ }
+
+ public long getTraceStart() {
+ return traceStart;
+ }
+
+ public long getTraceEnd() {
+ return traceEnd;
+ }
+
+ public void setTraceStart(long position) {
+ traceStart = position;
+ }
+
+ public void setTraceEnd(long position) {
+ traceEnd = position;
+ }
+
+ public void loaded(ClassStats child) {
+ loaded.add(child);
+ }
+
+ public void setLoadedBy(ClassStats parent) {
+ loadedBy = parent;
+ }
+
+ public ClassStats getLoadedBy() {
+ return loadedBy;
+ }
+
+ public List<ClassStats> getLoadedClasses() {
+ return loaded;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public boolean isStartupClass() {
+ return duringStartup;
+ }
+
+ public ClassloaderStats getClassloader() {
+ return classloader;
+ }
+
+ public int getLoadOrder() {
+ return loadOrder;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void toBaseClass() {
+ duringStartup = true;
+ loadOrder = -2;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ClassloaderStats.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ClassloaderStats.java
new file mode 100644
index 000000000..f34da3da3
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ClassloaderStats.java
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.stats;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Contains information about the classes and the bundles loaded by a given classloader. Typically there is one classloader per plugin so at levels above boot, this equates to information about
+ * classes and bundles in a plugin.
+ */
+public class ClassloaderStats {
+ private String id;
+ private long loadingTime; // time spent loading classes
+ /**
+ * classes loaded by the plugin (key: class name, value: ClassStats)
+ */
+ private Map<String, ClassStats> classes = Collections.synchronizedMap(new HashMap<String, ClassStats>(20));
+ private List<ResourceBundleStats> bundles = new ArrayList<ResourceBundleStats>(2); // bundles loaded
+
+ private boolean keepTraces = false; // indicate whether or not the traces of classes loaded are kept
+
+ // filters to indicate which classes we want to keep the traces
+ private static List<String> packageFilters = new ArrayList<String>(4); // filters on a package basis
+ private static Set<String> pluginFilters = new HashSet<String>(5); // filters on a plugin basis
+
+ private static Hashtable<Thread, Stack<ClassStats>> classStacks = new Hashtable<Thread, Stack<ClassStats>>(); // represents the classes that are currently being loaded
+ /**
+ * a dictionary of the classloaderStats (key: pluginId, value: ClassloaderStats)
+ */
+ private static Map<String, ClassloaderStats> loaders = Collections.synchronizedMap(new HashMap<String, ClassloaderStats>(20));
+ public static File traceFile;
+
+ static {
+ if (StatsManager.TRACE_CLASSES || StatsManager.TRACE_BUNDLES)
+ initializeTraceOptions();
+ }
+
+ private static void initializeTraceOptions() {
+ // create the trace file
+ String filename = StatsManager.TRACE_FILENAME;
+ traceFile = new File(filename);
+ traceFile.delete();
+
+ //load the filters
+ if (!StatsManager.TRACE_CLASSES)
+ return;
+ filename = StatsManager.TRACE_FILTERS;
+ if (filename == null || filename.length() == 0)
+ return;
+ try {
+ File filterFile = new File(filename);
+ System.out.print("Runtime tracing elements defined in: " + filterFile.getAbsolutePath() + "..."); //$NON-NLS-1$ //$NON-NLS-2$
+ InputStream input = new FileInputStream(filterFile);
+ System.out.println(" Loaded."); //$NON-NLS-1$
+ Properties filters = new Properties() {
+ private static final long serialVersionUID = 3546359543853365296L;
+
+ public synchronized Object put(Object key, Object value) {
+ addFilters((String) key, (String) value);
+ return null;
+ }
+ };
+ try {
+ filters.load(input);
+ } finally {
+ input.close();
+ }
+ } catch (IOException e) {
+ System.out.println(" No trace filters loaded."); //$NON-NLS-1$
+ }
+ }
+
+ protected static void addFilters(String key, String value) {
+ String[] filters = StatsManager.getArrayFromList(value);
+ if ("plugins".equals(key)) //$NON-NLS-1$
+ pluginFilters.addAll(Arrays.asList(filters));
+ if ("packages".equals(key)) //$NON-NLS-1$
+ packageFilters.addAll(Arrays.asList(filters));
+ }
+
+ public static void startLoadingClass(String id, String className) {
+ findLoader(id).startLoadClass(className);
+ }
+
+ // get and create if does not exist
+ private static ClassloaderStats findLoader(String id) {
+ synchronized (loaders) {
+ ClassloaderStats result = loaders.get(id);
+ if (result == null) {
+ result = new ClassloaderStats(id);
+ loaders.put(id, result);
+ }
+ return result;
+ }
+ }
+
+ public static synchronized Stack<ClassStats> getClassStack() {
+ Stack<ClassStats> result = classStacks.get(Thread.currentThread());
+ if (result == null) {
+ result = new Stack<ClassStats>();
+ classStacks.put(Thread.currentThread(), result);
+ }
+ return result;
+ }
+
+ public static ClassloaderStats[] getLoaders() {
+ //the parameter to toArray is of size zero for thread safety, otherwise this
+ //could return an array with null entries if the map shrinks concurrently
+ return loaders.values().toArray(new ClassloaderStats[0]);
+ }
+
+ public static void endLoadingClass(String id, String className, boolean success) {
+ findLoader(id).endLoadClass(className, success);
+ }
+
+ public static void loadedBundle(String id, ResourceBundleStats info) {
+ findLoader(id).loadedBundle(info);
+ }
+
+ public static ClassloaderStats getLoader(String id) {
+ return loaders.get(id);
+ }
+
+ public ClassloaderStats(String id) {
+ this.id = id;
+ keepTraces = pluginFilters.contains(id);
+ }
+
+ public void addBaseClasses(String[] baseClasses) {
+ for (int i = 0; i < baseClasses.length; i++) {
+ String name = baseClasses[i];
+ if (classes.get(name) == null) {
+ ClassStats value = new ClassStats(name, this);
+ value.toBaseClass();
+ classes.put(name, value);
+ }
+ }
+ }
+
+ private void loadedBundle(ResourceBundleStats bundle) {
+ bundles.add(bundle);
+ }
+
+ public List<ResourceBundleStats> getBundles() {
+ return bundles;
+ }
+
+ private synchronized void startLoadClass(String name) {
+ getClassStack().push(findClass(name));
+ }
+
+ // internal method that return the existing classStats or creates one
+ private ClassStats findClass(String name) {
+ ClassStats result = classes.get(name);
+ return result == null ? new ClassStats(name, this) : result;
+ }
+
+ private synchronized void endLoadClass(String name, boolean success) {
+ ClassStats current = getClassStack().pop();
+ if (!success)
+ return;
+ if (current.getLoadOrder() >= 0)
+ return;
+
+ classes.put(name, current);
+ current.setLoadOrder(classes.size());
+ current.loadingDone();
+ traceLoad(name, current);
+
+ // is there something on the load stack. if so, link them together...
+ Stack<ClassStats> classStack = getClassStack();
+ if (classStack.size() != 0) {
+ // get the time spent loading cli and subtract its load time from the class that requires loading
+ ClassStats previous = classStack.peek();
+ previous.addTimeLoadingOthers(current.getTimeLoading());
+ current.setLoadedBy(previous);
+ previous.loaded(current);
+ } else {
+ loadingTime = loadingTime + current.getTimeLoading();
+ }
+ }
+
+ private void traceLoad(String name, ClassStats target) {
+ // Stack trace code
+ if (!keepTraces) {
+ boolean found = false;
+ for (int i = 0; !found && i < packageFilters.size(); i++)
+ if (name.startsWith(packageFilters.get(i)))
+ found = true;
+ if (!found)
+ return;
+ }
+
+ // Write the stack trace. The position in the file are set to the corresponding classStat object
+ try {
+ target.setTraceStart(traceFile.length());
+ PrintWriter output = new PrintWriter(new FileOutputStream(traceFile.getAbsolutePath(), true));
+ try {
+ output.println("Loading class: " + name); //$NON-NLS-1$
+ output.println("Class loading stack:"); //$NON-NLS-1$
+ output.println("\t" + name); //$NON-NLS-1$
+ Stack<ClassStats> classStack = getClassStack();
+ for (int i = classStack.size() - 1; i >= 0; i--)
+ output.println("\t" + classStack.get(i).getClassName()); //$NON-NLS-1$
+ output.println("Stack trace:"); //$NON-NLS-1$
+ new Throwable().printStackTrace(output);
+ } finally {
+ output.close();
+ }
+ target.setTraceEnd(traceFile.length());
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public int getClassLoadCount() {
+ return classes.size();
+ }
+
+ public long getClassLoadTime() {
+ return loadingTime;
+ }
+
+ public ClassStats[] getClasses() {
+ //the parameter to toArray is of size zero for thread safety, otherwise this
+ //could return an array with null entries if the map shrinks concurrently
+ return classes.values().toArray(new ClassStats[0]);
+ }
+
+ public String getId() {
+ return id;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ResourceBundleStats.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ResourceBundleStats.java
new file mode 100644
index 000000000..2c072f91c
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/ResourceBundleStats.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.stats;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * BundleStats is used to represent information about loaded bundle. A
+ * bundlestats instance represents only one bundle.
+ */
+
+public class ResourceBundleStats {
+ private String pluginId; // the plugin loading this bundle
+ private String fileName; // the filename of the bundle
+ private int keyCount = 0; // number of keys in the bundle
+ private int keySize = 0; // size of the keys in the bundle
+ private int valueSize = 0; // size of the values in the bundle
+ private long hashSize = 0; // size of the hashtable
+ private long fileSize = 0;
+
+ private static int sizeOf(String value) {
+ return 44 + (2 * value.length());
+ }
+
+ private static int sizeOf(Properties value) {
+ return (int) Math.round(44 + (16 + (value.size() * 1.25 * 4)) + (24 * value.size()));
+ }
+
+ public ResourceBundleStats(String pluginId, String fileName, URL input) {
+ this.pluginId = pluginId;
+ this.fileName = fileName;
+ initialize(input);
+ }
+
+ public ResourceBundleStats(String pluginId, String fileName, ResourceBundle bundle) {
+ this.pluginId = pluginId;
+ this.fileName = fileName;
+ initialize(bundle);
+ }
+
+ /**
+ * Compute the size of bundle
+ */
+ private void initialize(ResourceBundle bundle) {
+ for (Enumeration<String> keys = bundle.getKeys(); keys.hasMoreElements();) {
+ String key = keys.nextElement();
+ keySize += sizeOf(key);
+ valueSize += sizeOf(bundle.getString(key));
+ keyCount++;
+ }
+ }
+
+ /**
+ * Compute the size of stream which represents a property file
+ */
+ private void initialize(URL url) {
+ InputStream stream = null;
+ Properties props = new Properties();
+ try {
+ try {
+ stream = url.openStream();
+ fileSize = stream.available();
+ props.load(stream);
+ for (Iterator<Object> iter = props.keySet().iterator(); iter.hasNext();) {
+ String key = (String) iter.next();
+ keySize += sizeOf(key);
+ valueSize += sizeOf(props.getProperty(key));
+ keyCount++;
+ }
+ hashSize = sizeOf(props);
+ } finally {
+ if (stream != null)
+ stream.close();
+ }
+ } catch (IOException e) {
+ // ignore exceptions as they will be handled when the stream
+ // is loaded for real. See callers.
+ }
+ }
+
+ public long getHashSize() {
+ return hashSize;
+ }
+
+ public int getKeyCount() {
+ return keyCount;
+ }
+
+ public String getPluginId() {
+ return pluginId;
+ }
+
+ public int getKeySize() {
+ return keySize;
+ }
+
+ public int getValueSize() {
+ return valueSize;
+ }
+
+ public long getTotalSize() {
+ return keySize + valueSize + hashSize;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public long getFileSize() {
+ return fileSize;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/StatsManager.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/StatsManager.java
new file mode 100644
index 000000000..8356d808e
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/core/runtime/internal/stats/StatsManager.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.runtime.internal.stats;
+
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.osgi.baseadaptor.HookConfigurator;
+import org.eclipse.osgi.baseadaptor.HookRegistry;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingStatsHook;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathEntry;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathManager;
+import org.eclipse.osgi.framework.adaptor.BundleWatcher;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.debug.FrameworkDebugOptions;
+import org.eclipse.osgi.util.ManifestElement;
+import org.osgi.framework.Bundle;
+
+public class StatsManager implements BundleWatcher, HookConfigurator, ClassLoadingStatsHook {
+ // This connect bundles and their info, and so allows to access the info without running through
+ // the bundle registry. This map only contains activated bundles. The key is the bundle Id
+ private Hashtable<Long, BundleStats> bundles = new Hashtable<Long, BundleStats>(20);
+ private Map<Thread, Stack<BundleStats>> activationStacks = new HashMap<Thread, Stack<BundleStats>>(5);
+ private static boolean booting = true; // the state of the platform. This value is changed by the InternalPlatform itself.
+
+ private static StatsManager defaultInstance;
+
+ public static boolean MONITOR_ACTIVATION = false;
+ public static boolean MONITOR_CLASSES = false;
+ public static boolean MONITOR_RESOURCES = false;
+ public static String TRACE_FILENAME = "runtime.traces"; //$NON-NLS-1$
+ public static String TRACE_FILTERS = "trace.properties"; //$NON-NLS-1$
+ public static boolean TRACE_CLASSES = false;
+ public static boolean TRACE_BUNDLES = false;
+ public static final String FRAMEWORK_SYMBOLICNAME = "org.eclipse.osgi"; //$NON-NLS-1$
+
+ //Option names for spies
+ private static final String OPTION_MONITOR_ACTIVATION = FRAMEWORK_SYMBOLICNAME + "/monitor/activation"; //$NON-NLS-1$
+ private static final String OPTION_MONITOR_CLASSES = FRAMEWORK_SYMBOLICNAME + "/monitor/classes"; //$NON-NLS-1$
+ private static final String OPTION_MONITOR_RESOURCES = FRAMEWORK_SYMBOLICNAME + "/monitor/resources"; //$NON-NLS-1$
+ private static final String OPTION_TRACE_BUNDLES = FRAMEWORK_SYMBOLICNAME + "/trace/activation"; //$NON-NLS-1$
+ private static final String OPTION_TRACE_CLASSES = FRAMEWORK_SYMBOLICNAME + "/trace/classLoading"; //$NON-NLS-1$
+ private static final String OPTION_TRACE_FILENAME = FRAMEWORK_SYMBOLICNAME + "/trace/filename"; //$NON-NLS-1$
+ private static final String OPTION_TRACE_FILTERS = FRAMEWORK_SYMBOLICNAME + "/trace/filters"; //$NON-NLS-1$
+
+ static {
+ setDebugOptions();
+ }
+
+ public static StatsManager getDefault() {
+ if (defaultInstance == null) {
+ defaultInstance = new StatsManager();
+ defaultInstance.initialize();
+ }
+ return defaultInstance;
+ }
+
+ public static void setDebugOptions() {
+ FrameworkDebugOptions options = FrameworkDebugOptions.getDefault();
+ // may be null if debugging is not enabled
+ if (options == null)
+ return;
+ MONITOR_ACTIVATION = options.getBooleanOption(OPTION_MONITOR_ACTIVATION, false);
+ MONITOR_CLASSES = options.getBooleanOption(OPTION_MONITOR_CLASSES, false);
+ MONITOR_RESOURCES = options.getBooleanOption(OPTION_MONITOR_RESOURCES, false);
+ TRACE_CLASSES = options.getBooleanOption(OPTION_TRACE_CLASSES, false);
+ TRACE_BUNDLES = options.getBooleanOption(OPTION_TRACE_BUNDLES, false);
+ TRACE_FILENAME = options.getOption(OPTION_TRACE_FILENAME, TRACE_FILENAME);
+ TRACE_FILTERS = options.getOption(OPTION_TRACE_FILTERS, TRACE_FILTERS);
+ }
+
+ public static void doneBooting() {
+ booting = false;
+ }
+
+ public static boolean isBooting() {
+ return booting;
+ }
+
+ /**
+ * 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
+ */
+ public static String[] getArrayFromList(String prop) {
+ return ManifestElement.getArrayFromList(prop, ","); //$NON-NLS-1$
+ }
+
+ private void initialize() {
+ // add the system bundle
+ BundleStats bundle = findBundle(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, 0);
+ bundle.setTimestamp(System.currentTimeMillis());
+ bundle.setActivationOrder(bundles.size());
+ bundle.setDuringStartup(booting);
+ }
+
+ public void watchBundle(Bundle bundle, int type) {
+ switch (type) {
+ case BundleWatcher.START_ACTIVATION :
+ startActivation(bundle);
+ break;
+ case BundleWatcher.END_ACTIVATION :
+ endActivation(bundle);
+ break;
+ }
+ }
+
+ public void startActivation(Bundle bundle) {
+ // should be called from a synchronized location to protect against concurrent updates
+ BundleStats info = findBundle(bundle.getSymbolicName(), bundle.getBundleId());
+ info.setTimestamp(System.currentTimeMillis());
+ info.setActivationOrder(bundles.size());
+ info.setDuringStartup(booting);
+
+ Stack<BundleStats> activationStack = activationStacks.get(Thread.currentThread());
+ if (activationStack == null) {
+ activationStack = new Stack<BundleStats>();
+ activationStacks.put(Thread.currentThread(), activationStack);
+ }
+
+ // set the parentage of activation
+ if (activationStack.size() != 0) {
+ BundleStats activatedBy = activationStack.peek();
+ activatedBy.activated(info);
+ info.setActivatedBy(activatedBy);
+ }
+ activationStack.push(info);
+
+ if (TRACE_BUNDLES == true) {
+ traceActivate(bundle, info);
+ }
+ }
+
+ public void endActivation(Bundle symbolicName) {
+ Stack<BundleStats> activationStack = activationStacks.get(Thread.currentThread());
+ // should be called from a synchronized location to protect against concurrent updates
+ BundleStats info = activationStack.pop();
+ info.endActivation();
+ }
+
+ private void traceActivate(Bundle bundle, BundleStats info) {
+ try {
+ PrintWriter output = new PrintWriter(new FileOutputStream(ClassloaderStats.traceFile.getAbsolutePath(), true));
+ try {
+ long startPosition = ClassloaderStats.traceFile.length();
+ output.println("Activating bundle: " + bundle.getSymbolicName()); //$NON-NLS-1$
+ output.println("Bundle activation stack:"); //$NON-NLS-1$
+ Stack<BundleStats> activationStack = activationStacks.get(Thread.currentThread());
+ for (int i = activationStack.size() - 1; i >= 0; i--)
+ output.println("\t" + activationStack.get(i).getSymbolicName()); //$NON-NLS-1$
+ output.println("Class loading stack:"); //$NON-NLS-1$
+ Stack<ClassStats> classStack = ClassloaderStats.getClassStack();
+ for (int i = classStack.size() - 1; i >= 0; i--)
+ output.println("\t" + classStack.get(i).getClassName()); //$NON-NLS-1$
+ output.println("Stack trace:"); //$NON-NLS-1$
+ new Throwable().printStackTrace(output);
+ info.setTraceStart(startPosition);
+ } finally {
+ output.close();
+ info.setTraceEnd(ClassloaderStats.traceFile.length());
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public BundleStats findBundle(String symbolicName, long id) {
+ BundleStats result = bundles.get(new Long(id));
+ try {
+ if (result == null) {
+ result = new BundleStats(symbolicName, id);
+ bundles.put(new Long(id), result);
+ }
+ } catch (IllegalAccessError e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ public BundleStats[] getBundles() {
+ return bundles.values().toArray(new BundleStats[bundles.size()]);
+ }
+
+ public BundleStats getBundle(long id) {
+ return bundles.get(new Long(id));
+ }
+
+ /**
+ * @throws ClassNotFoundException
+ */
+ public void preFindLocalClass(String name, ClasspathManager manager) throws ClassNotFoundException {
+ if (StatsManager.MONITOR_CLASSES) //Support for performance analysis
+ ClassloaderStats.startLoadingClass(getClassloaderId(manager), name);
+ }
+
+ public void postFindLocalClass(String name, Class<?> clazz, ClasspathManager manager) {
+ if (StatsManager.MONITOR_CLASSES)
+ ClassloaderStats.endLoadingClass(getClassloaderId(manager), name, clazz != null);
+ }
+
+ public void preFindLocalResource(String name, ClasspathManager manager) {
+ // do nothing
+ }
+
+ public void postFindLocalResource(String name, URL resource, ClasspathManager manager) {
+ if (StatsManager.MONITOR_RESOURCES)
+ if (resource != null && name.endsWith(".properties")) //$NON-NLS-1$
+ ClassloaderStats.loadedBundle(getClassloaderId(manager), new ResourceBundleStats(getClassloaderId(manager), name, resource));
+ return;
+ }
+
+ public void recordClassDefine(String name, Class<?> clazz, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry, ClasspathManager manager) {
+ // do nothing
+ }
+
+ private String getClassloaderId(ClasspathManager loader) {
+ return loader.getBaseData().getSymbolicName();
+ }
+
+ public void addHooks(HookRegistry hookRegistry) {
+ if (Debug.MONITOR_ACTIVATION)
+ hookRegistry.addWatcher(StatsManager.getDefault());
+ if (StatsManager.MONITOR_CLASSES || StatsManager.MONITOR_RESOURCES)
+ hookRegistry.addClassLoadingStatsHook(StatsManager.getDefault());
+ }
+}

Back to the top