Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Niefer2006-12-05 22:10:36 +0000
committerAndrew Niefer2006-12-05 22:10:36 +0000
commitf39c77a020b5dc7484945ecd8bc718b0d61ced21 (patch)
tree2234e836dec32975e18394c26647f3e79be52cd4 /bundles
parentbbfad4091158b2b32677ff1e8b66130e5f96fa8a (diff)
downloadrt.equinox.framework-f39c77a020b5dc7484945ecd8bc718b0d61ced21.tar.gz
rt.equinox.framework-f39c77a020b5dc7484945ecd8bc718b0d61ced21.tar.xz
rt.equinox.framework-f39c77a020b5dc7484945ecd8bc718b0d61ced21.zip
startup.jar code moving from equinox-incubator/org.eclipse.equinox.startup
Diffstat (limited to 'bundles')
-rw-r--r--bundles/org.eclipse.equinox.launcher/.classpath7
-rw-r--r--bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.cdt.core.prefs3
-rw-r--r--bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.jdt.core.prefs12
-rw-r--r--bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.pde.core.prefs4
-rw-r--r--bundles/org.eclipse.equinox.launcher/META-INF/MANIFEST.MF9
-rw-r--r--bundles/org.eclipse.equinox.launcher/build.properties4
-rw-r--r--bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/JNIBridge.java105
-rw-r--r--bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/Main.java2229
-rw-r--r--bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/WebStartMain.java295
-rw-r--r--bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/package.html18
10 files changed, 2683 insertions, 3 deletions
diff --git a/bundles/org.eclipse.equinox.launcher/.classpath b/bundles/org.eclipse.equinox.launcher/.classpath
new file mode 100644
index 000000000..751c8f2e5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.launcher/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.cdt.core.prefs b/bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.cdt.core.prefs
deleted file mode 100644
index a44ad7e23..000000000
--- a/bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.cdt.core.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Mon Nov 13 14:48:49 EST 2006
-eclipse.preferences.version=1
-indexerId=org.eclipse.cdt.core.fastIndexer
diff --git a/bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..9db76a606
--- /dev/null
+++ b/bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Fri Nov 24 13:39:42 EST 2006
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
diff --git a/bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.pde.core.prefs b/bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 000000000..fe921f9c4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.launcher/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,4 @@
+#Mon Nov 13 11:00:38 EST 2006
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/bundles/org.eclipse.equinox.launcher/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.launcher/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..e0cec4b99
--- /dev/null
+++ b/bundles/org.eclipse.equinox.launcher/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Startup Plug-in
+Bundle-SymbolicName: org.eclipse.equinox.launcher
+Bundle-Version: 1.0.0.qualifier
+Main-Class: org.eclipse.core.launcher.Main
+Bundle-RequiredExecutionEnvironment: J2SE-1.4,
+ CDC-1.0/Foundation-1.0,
+ J2SE-1.3
diff --git a/bundles/org.eclipse.equinox.launcher/build.properties b/bundles/org.eclipse.equinox.launcher/build.properties
new file mode 100644
index 000000000..34d2e4d2d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.launcher/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/JNIBridge.java b/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/JNIBridge.java
new file mode 100644
index 000000000..9d923c924
--- /dev/null
+++ b/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/JNIBridge.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 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.equinox.launcher;
+
+
+/**
+ * @author aniefer
+ *
+ */
+public class JNIBridge {
+ private native void _set_exit_data(String data);
+ private native void _update_splash();
+ private native long _get_splash_handle();
+ private native void _show_splash(String bitmap);
+ private native void _takedown_splash();
+
+ private String library;
+ private boolean libraryLoaded = false;
+ public JNIBridge(String library) {
+ this.library = library;
+ }
+
+ private void loadLibrary() {
+ if(library != null) {
+ try {
+ System.load(library);
+ } catch (UnsatisfiedLinkError e ) {
+ //failed
+ }
+ }
+ libraryLoaded = true;
+ }
+
+ public boolean setExitData(String data) {
+ try {
+ _set_exit_data(data);
+ return true;
+ } catch (UnsatisfiedLinkError e) {
+ if(!libraryLoaded){
+ loadLibrary();
+ return setExitData(data);
+ }
+ return false;
+ }
+ }
+
+ public boolean showSplash(String bitmap) {
+ try {
+ _show_splash(bitmap);
+ return true;
+ } catch (UnsatisfiedLinkError e) {
+ if(!libraryLoaded){
+ loadLibrary();
+ return showSplash(bitmap);
+ }
+ return false;
+ }
+ }
+
+ public boolean updateSplash() {
+ try {
+ _update_splash();
+ return true;
+ } catch (UnsatisfiedLinkError e) {
+ if(!libraryLoaded){
+ loadLibrary();
+ return updateSplash();
+ }
+ return false;
+ }
+ }
+
+ public long getSplashHandle() {
+ try {
+ return _get_splash_handle();
+ } catch (UnsatisfiedLinkError e) {
+ if(!libraryLoaded){
+ loadLibrary();
+ return getSplashHandle();
+ }
+ return -1;
+ }
+ }
+
+ public boolean takeDownSplash() {
+ try {
+ _takedown_splash();
+ return true;
+ } catch (UnsatisfiedLinkError e) {
+ if(!libraryLoaded){
+ loadLibrary();
+ return takeDownSplash();
+ }
+ return false;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/Main.java b/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/Main.java
new file mode 100644
index 000000000..90b759aca
--- /dev/null
+++ b/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/Main.java
@@ -0,0 +1,2229 @@
+/*******************************************************************************
+ * 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.equinox.launcher;
+
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.*;
+import java.security.*;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * The launcher for Eclipse.
+ */
+public class Main {
+ /**
+ * Indicates whether this instance is running in debug mode.
+ */
+ protected boolean debug = false;
+
+ /**
+ * The location of the launcher to run.
+ */
+ protected String bootLocation = null;
+
+ /**
+ * The location of the install root
+ */
+ protected URL installLocation = null;
+
+ /**
+ * The location of the configuration information for this instance
+ */
+ protected URL configurationLocation = null;
+
+ /**
+ * The location of the configuration information in the install root
+ */
+ protected String parentConfigurationLocation = null;
+
+ /**
+ * The id of the bundle that will contain the framework to run. Defaults to org.eclipse.osgi.
+ */
+ protected String framework = OSGI;
+
+ /**
+ * The extra development time class path entries for the framework.
+ */
+ protected String devClassPath = null;
+
+ /*
+ * The extra development time class path entries for all bundles.
+ */
+ private Properties devClassPathProps = null;
+
+ /**
+ * Indicates whether this instance is running in development mode.
+ */
+ protected boolean inDevelopmentMode = false;
+
+ /**
+ * Indicates which OS was passed in with -os
+ */
+ protected String os = null;
+
+ private String name = null; // The name to brand the launcher
+ private String launcher = null; // The full path to the launcher
+
+ private String vm = null;
+ private String[] vmargs = null;
+ private String[] commands = null;
+ private String[] extensionPaths = null;
+
+ private JNIBridge bridge = null;
+
+ // splash handling
+ private boolean showSplash = false;
+ private String endSplash = null;
+ private boolean initialize = false;
+ private boolean splashDown = false;
+ public final class SplashHandler extends Thread {
+ public void run() {
+ takeDownSplash();
+ }
+ public void updateSplash() {
+ if(bridge != null) {
+ bridge.updateSplash();
+ }
+ }
+ }
+ private final Thread splashHandler = new SplashHandler();
+
+ // command line args
+ private static final String FRAMEWORK = "-framework"; //$NON-NLS-1$
+ private static final String INSTALL = "-install"; //$NON-NLS-1$
+ private static final String INITIALIZE = "-initialize"; //$NON-NLS-1$
+ private static final String VM = "-vm"; //$NON-NLS-1$
+ private static final String VMARGS = "-vmargs"; //$NON-NLS-1$
+ private static final String DEBUG = "-debug"; //$NON-NLS-1$
+ private static final String DEV = "-dev"; //$NON-NLS-1$
+ private static final String CONFIGURATION = "-configuration"; //$NON-NLS-1$
+ private static final String NOSPLASH = "-nosplash"; //$NON-NLS-1$
+ private static final String SHOWSPLASH = "-showsplash"; //$NON-NLS-1$
+ private static final String NAME = "-name"; //$NON-NLS-1$
+ private static final String LAUNCHER = "-launcher"; //$NON-NLS-1$
+ private static final String NL = "-nl"; //$NON-NLS-1$
+ private static final String ENDSPLASH = "-endsplash"; //$NON-NLS-1$
+ private static final String SPLASH_IMAGE = "splash.bmp"; //$NON-NLS-1$
+ private static final String CLEAN = "-clean"; //$NON-NLS-1$
+ private static final String NOEXIT = "-noExit"; //$NON-NLS-1$
+ private static final String OS = "-os"; //$NON-NLS-1$
+
+ private static final String OSGI = "org.eclipse.osgi"; //$NON-NLS-1$
+ private static final String STARTER = "org.eclipse.core.runtime.adaptor.EclipseStarter"; //$NON-NLS-1$
+ private static final String PLATFORM_URL = "platform:/base/"; //$NON-NLS-1$
+ private static final String ECLIPSE_PROPERTIES = "eclipse.properties"; //$NON-NLS-1$
+ private static final String FILE_SCHEME = "file:"; //$NON-NLS-1$
+ protected static final String REFERENCE_SCHEME = "reference:"; //$NON-NLS-1$
+ protected static final String JAR_SCHEME = "jar:"; //$NON-NLS-1$
+
+ private static final String DEFAULT_JRE_REQUIRED = "1.4.1"; //$NON-NLS-1$
+
+ // constants: configuration file location
+ private static final String CONFIG_DIR = "configuration/"; //$NON-NLS-1$
+ private static final String CONFIG_FILE = "config.ini"; //$NON-NLS-1$
+ private static final String CONFIG_FILE_TEMP_SUFFIX = ".tmp"; //$NON-NLS-1$
+ private static final String CONFIG_FILE_BAK_SUFFIX = ".bak"; //$NON-NLS-1$
+ 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$
+
+ // constants: System property keys and/or configuration file elements
+ private static final String PROP_USER_HOME = "user.home"; //$NON-NLS-1$
+ private static final String PROP_USER_DIR = "user.dir"; //$NON-NLS-1$
+ private static final String PROP_INSTALL_AREA = "osgi.install.area"; //$NON-NLS-1$
+ private static final String PROP_CONFIG_AREA = "osgi.configuration.area"; //$NON-NLS-1$
+ private static final String PROP_CONFIG_AREA_DEFAULT = "osgi.configuration.area.default"; //$NON-NLS-1$
+ private static final String PROP_BASE_CONFIG_AREA = "osgi.baseConfiguration.area"; //$NON-NLS-1$
+ private static final String PROP_SHARED_CONFIG_AREA = "osgi.sharedConfiguration.area"; //$NON-NLS-1$
+ private static final String PROP_CONFIG_CASCADED = "osgi.configuration.cascaded"; //$NON-NLS-1$
+ protected static final String PROP_FRAMEWORK = "osgi.framework"; //$NON-NLS-1$
+ private static final String PROP_SPLASHPATH = "osgi.splashPath"; //$NON-NLS-1$
+ private static final String PROP_SPLASHLOCATION = "osgi.splashLocation"; //$NON-NLS-1$
+ private static final String PROP_CLASSPATH = "osgi.frameworkClassPath"; //$NON-NLS-1$
+ private static final String PROP_EXTENSIONS = "osgi.framework.extensions"; //$NON-NLS-1$
+ private static final String PROP_FRAMEWORK_SYSPATH = "osgi.syspath"; //$NON-NLS-1$
+ private static final String PROP_FRAMEWORK_SHAPE = "osgi.framework.shape"; //$NON-NLS-1$
+ private static final String PROP_LOGFILE = "osgi.logfile"; //$NON-NLS-1$
+ private static final String PROP_REQUIRED_JAVA_VERSION = "osgi.requiredJavaVersion"; //$NON-NLS-1$
+ private static final String PROP_PARENT_CLASSLOADER = "osgi.parentClassloader"; //$NON-NLS-1$
+ private static final String PROP_NL = "osgi.nl"; //$NON-NLS-1$
+ private static final String PROP_NOSHUTDOWN = "osgi.noShutdown"; //$NON-NLS-1$
+ private static final String PROP_DEBUG = "osgi.debug"; //$NON-NLS-1$
+
+ private static final String PROP_EXITCODE = "eclipse.exitcode"; //$NON-NLS-1$
+ private static final String PROP_EXITDATA = "eclipse.exitdata"; //$NON-NLS-1$
+
+ private static final String PROP_VM = "eclipse.vm"; //$NON-NLS-1$
+ private static final String PROP_VMARGS = "eclipse.vmargs"; //$NON-NLS-1$
+ private static final String PROP_COMMANDS = "eclipse.commands"; //$NON-NLS-1$
+ private static final String PROP_ECLIPSESECURITY = "eclipse.security"; //$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$
+
+ // types of parent classloaders the framework can have
+ private static final String PARENT_CLASSLOADER_APP = "app"; //$NON-NLS-1$
+ private static final String PARENT_CLASSLOADER_EXT = "ext"; //$NON-NLS-1$
+ private static final String PARENT_CLASSLOADER_BOOT = "boot"; //$NON-NLS-1$
+
+ // log file handling
+ protected static final String SESSION = "!SESSION"; //$NON-NLS-1$
+ protected static final String ENTRY = "!ENTRY"; //$NON-NLS-1$
+ protected static final String MESSAGE = "!MESSAGE"; //$NON-NLS-1$
+ protected static final String STACK = "!STACK"; //$NON-NLS-1$
+ protected static final int ERROR = 4;
+ protected static final String PLUGIN_ID = "org.eclipse.equinox.launcher"; //$NON-NLS-1$
+ protected File logFile = null;
+ protected BufferedWriter log = null;
+ protected boolean newSession = true;
+
+ /**
+ * A structured form for a version identifier.
+ *
+ * @see http://java.sun.com/j2se/versioning_naming.html for information on valid version strings
+ */
+ static class Identifier {
+ private static final String DELIM = ". _-"; //$NON-NLS-1$
+ private int major, minor, service;
+ Identifier(int major, int minor, int service) {
+ super();
+ this.major = major;
+ this.minor = minor;
+ this.service = service;
+ }
+ /**
+ * @throws NumberFormatException if cannot parse the major and minor version components
+ */
+ Identifier(String versionString) {
+ super();
+ StringTokenizer tokenizer = new StringTokenizer(versionString, DELIM);
+
+ // major
+ if (tokenizer.hasMoreTokens())
+ major = Integer.parseInt(tokenizer.nextToken());
+
+ // minor
+ if (tokenizer.hasMoreTokens())
+ minor = Integer.parseInt(tokenizer.nextToken());
+
+ try {
+ // service
+ if (tokenizer.hasMoreTokens())
+ service = Integer.parseInt(tokenizer.nextToken());
+ } catch (NumberFormatException nfe) {
+ // ignore the service qualifier in that case and default to 0
+ // this will allow us to tolerate other non-conventional version numbers
+ }
+ }
+ /**
+ * Returns true if this id is considered to be greater than or equal to the given baseline.
+ * e.g.
+ * 1.2.9 >= 1.3.1 -> false
+ * 1.3.0 >= 1.3.1 -> false
+ * 1.3.1 >= 1.3.1 -> true
+ * 1.3.2 >= 1.3.1 -> true
+ * 2.0.0 >= 1.3.1 -> true
+ */
+ boolean isGreaterEqualTo(Identifier minimum) {
+ if (major < minimum.major)
+ return false;
+ if (major > minimum.major)
+ return true;
+ // major numbers are equivalent so check minor
+ if (minor < minimum.minor)
+ return false;
+ if (minor > minimum.minor)
+ return true;
+ // minor numbers are equivalent so check service
+ return service >= minimum.service;
+ }
+ }
+
+ /**
+ * Sets up the JNI bridge to native calls
+ */
+ protected void setupJNI() {
+ URL install = getInstallLocation();
+ String location = install.getFile();
+
+ //Guess a library suffix
+ String suffix = null;
+ String osName = os;
+ if(osName == null) {
+ osName = System.getProperty("os.name"); //$NON-NLS-1$
+ }
+ //windows is dll, others are so
+ if(osName.regionMatches(true, 0, "win", 0, 3)) //$NON-NLS-1$
+ suffix = ".dll"; //$NON-NLS-1$
+ else if(osName.regionMatches(true, 0, "mac", 0, 3)) {
+ location += name + ".app/Contents/MacOS";
+ suffix = ".so";
+
+ } else
+ suffix = ".so"; //$NON-NLS-1$
+
+ String library = searchFor("eclipse", suffix, location); //$NON-NLS-1$
+ bridge = new JNIBridge(library);
+ }
+
+ /**
+ * Executes the launch.
+ *
+ * @param args command-line arguments
+ * @exception Exception thrown if a problem occurs during the launch
+ */
+ protected void basicRun(String[] args) throws Exception {
+ System.getProperties().put("eclipse.startTime", Long.toString(System.currentTimeMillis())); //$NON-NLS-1$
+ commands = args;
+ String[] passThruArgs = processCommandLine(args);
+
+ if (!debug)
+ // debug can be specified as system property as well
+ debug = System.getProperty(PROP_DEBUG) != null;
+ setupVMProperties();
+ processConfiguration();
+
+ //ensure minimum Java version
+ if (!checkVersion(System.getProperty("java.version"), System.getProperty(PROP_REQUIRED_JAVA_VERSION, DEFAULT_JRE_REQUIRED))) //$NON-NLS-1$
+ return;
+
+ // need to ensure that getInstallLocation is called at least once to initialize the value.
+ // Do this AFTER processing the configuration to allow the configuration to set
+ // the install location.
+ getInstallLocation();
+
+ //Set up the JNI bridge. We need to know the install location to find the shared library
+ setupJNI();
+
+ // locate boot plugin (may return -dev mode variations)
+ URL[] bootPath = getBootPath(bootLocation);
+ setSecurityPolicy(bootPath);
+ // splash handling is done here, because the default case needs to know
+ // the location of the boot plugin we are going to use
+ handleSplash(bootPath);
+
+ invokeFramework(passThruArgs, bootPath);
+ }
+
+ protected void setSecurityPolicy(URL[] bootPath) {
+ String eclipseSecurity = System.getProperty(PROP_ECLIPSESECURITY);
+ if (eclipseSecurity != null) {
+ SecurityManager sm = System.getSecurityManager();
+ boolean setSM = false;
+ if (sm == null) {
+ if (eclipseSecurity.length() < 1) {
+ eclipseSecurity = "java.lang.SecurityManager"; //$NON-NLS-1$
+ }
+ try {
+ Class clazz = Class.forName(eclipseSecurity);
+ sm = (SecurityManager) clazz.newInstance();
+ setSM = true;
+ }
+ catch (Throwable t) {
+ System.getProperties().put("java.security.manager", eclipseSecurity); // let the framework try to load it later. //$NON-NLS-1$
+ }
+ }
+
+ ProtectionDomain domain = Main.class.getProtectionDomain();
+ CodeSource source = null;
+ if (domain != null)
+ source = Main.class.getProtectionDomain().getCodeSource();
+ if (domain == null || source == null) {
+ log("Can not automatically set the security manager. Please use a policy file."); //$NON-NLS-1$
+ return;
+ }
+ // get the list of codesource URLs to grant AllPermission to
+ URL[] rootURLs = new URL[bootPath.length + 1];
+ rootURLs[0] = source.getLocation();
+ System.arraycopy(bootPath, 0, rootURLs, 1, bootPath.length);
+ // replace the security policy
+ Policy eclipsePolicy = new EclipsePolicy(Policy.getPolicy(), rootURLs);
+ Policy.setPolicy(eclipsePolicy);
+ if (setSM)
+ System.setSecurityManager(sm);
+ }
+ }
+
+ private void invokeFramework(String[] passThruArgs, URL[] bootPath) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, Error, Exception, InvocationTargetException {
+ String type = System.getProperty(PROP_PARENT_CLASSLOADER, PARENT_CLASSLOADER_BOOT);
+ ClassLoader parent = null;
+ if (PARENT_CLASSLOADER_APP.equalsIgnoreCase(type))
+ parent = ClassLoader.getSystemClassLoader();
+ else if (PARENT_CLASSLOADER_EXT.equalsIgnoreCase(type)) {
+ ClassLoader appCL = ClassLoader.getSystemClassLoader();
+ if (appCL != null)
+ parent = appCL.getParent();
+ }
+ URLClassLoader loader = new StartupClassLoader(bootPath, parent);
+ Class clazz = loader.loadClass(STARTER);
+ Method method = clazz.getDeclaredMethod("run", new Class[] {String[].class, Runnable.class}); //$NON-NLS-1$
+ try {
+ method.invoke(clazz, new Object[] {passThruArgs, splashHandler});
+ } catch (InvocationTargetException e) {
+ if (e.getTargetException() instanceof Error)
+ throw (Error) e.getTargetException();
+ else if (e.getTargetException() instanceof Exception)
+ throw (Exception) e.getTargetException();
+ else
+ //could be a subclass of Throwable!
+ throw e;
+ }
+ }
+
+ /**
+ * Checks whether the given available version is greater or equal to the
+ * given required version.
+ * <p>Will set PROP_EXITCODE/PROP_EXITDATA accordingly if check fails.</p>
+ *
+ * @return a boolean indicating whether the checking passed
+ */
+ private boolean checkVersion(String availableVersion, String requiredVersion) {
+ if (requiredVersion == null || availableVersion == null)
+ return true;
+ try {
+ Identifier required = new Identifier(requiredVersion);
+ Identifier available = new Identifier(availableVersion);
+ boolean compatible = available.isGreaterEqualTo(required);
+ if (!compatible) {
+ // any non-zero value should do it - 14 used to be used for version incompatibility in Eclipse 2.1
+ System.getProperties().put(PROP_EXITCODE, "14"); //$NON-NLS-1$
+ System.getProperties().put(PROP_EXITDATA, "<title>Incompatible JVM</title>Version " + availableVersion + " of the JVM is not suitable for this product. Version: "+ requiredVersion + " or greater is required."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return compatible;
+ } catch (SecurityException e) {
+ // If the security manager won't allow us to get the system property, continue for
+ // now and let things fail later on their own if necessary.
+ return true;
+ } catch (NumberFormatException e) {
+ // If the version string was in a format that we don't understand, continue and
+ // let things fail later on their own if necessary.
+ return true;
+ }
+ }
+
+ /**
+ * Returns a string representation of the given URL String. This converts
+ * escaped sequences (%..) in the URL into the appropriate characters.
+ * NOTE: due to class visibility there is a copy of this method
+ * in InternalBootLoader
+ */
+ protected String decode(String urlString) {
+ //try to use Java 1.4 method if available
+ try {
+ Class clazz = URLDecoder.class;
+ Method method = clazz.getDeclaredMethod("decode", new Class[] {String.class, String.class}); //$NON-NLS-1$
+ //first encode '+' characters, because URLDecoder incorrectly converts
+ //them to spaces on certain class library implementations.
+ if (urlString.indexOf('+') >= 0) {
+ int len = urlString.length();
+ StringBuffer buf = new StringBuffer(len);
+ for (int i = 0; i < len; i++) {
+ char c = urlString.charAt(i);
+ if (c == '+')
+ buf.append("%2B"); //$NON-NLS-1$
+ else
+ buf.append(c);
+ }
+ urlString = buf.toString();
+ }
+ Object result = method.invoke(null, new Object[] {urlString, "UTF-8"}); //$NON-NLS-1$
+ if (result != null)
+ return (String) result;
+ } catch (Exception e) {
+ //JDK 1.4 method not found -- fall through and decode by hand
+ }
+ //decode URL by hand
+ boolean replaced = false;
+ byte[] encodedBytes = urlString.getBytes();
+ int encodedLength = encodedBytes.length;
+ byte[] decodedBytes = new byte[encodedLength];
+ int decodedLength = 0;
+ for (int i = 0; i < encodedLength; i++) {
+ byte b = encodedBytes[i];
+ if (b == '%') {
+ if(i+2 >= encodedLength)
+ throw new IllegalArgumentException("Malformed URL (\""+urlString+"\"): % must be followed by 2 digits."); //$NON-NLS-1$//$NON-NLS-2$
+ byte enc1 = encodedBytes[++i];
+ byte enc2 = encodedBytes[++i];
+ b = (byte) ((hexToByte(enc1) << 4) + hexToByte(enc2));
+ replaced = true;
+ }
+ decodedBytes[decodedLength++] = b;
+ }
+ if (!replaced)
+ return urlString;
+ try {
+ return new String(decodedBytes, 0, decodedLength, "UTF-8"); //$NON-NLS-1$
+ } catch (UnsupportedEncodingException e) {
+ //use default encoding
+ return new String(decodedBytes, 0, decodedLength);
+ }
+ }
+
+ /**
+ * 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
+ */
+ protected String[] getArrayFromList(String prop) {
+ if (prop == null || prop.trim().equals("")) //$NON-NLS-1$
+ return new String[0];
+ Vector list = new Vector();
+ StringTokenizer tokens = new StringTokenizer(prop, ","); //$NON-NLS-1$
+ while (tokens.hasMoreTokens()) {
+ String token = tokens.nextToken().trim();
+ if (!token.equals("")) //$NON-NLS-1$
+ list.addElement(token);
+ }
+ return list.isEmpty() ? new String[0] : (String[]) list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * Returns the <code>URL</code>-based class path describing where the boot classes
+ * are located when running in development mode.
+ *
+ * @return the url-based class path
+ * @param base the base location
+ * @exception MalformedURLException if a problem occurs computing the class path
+ */
+ private URL[] getDevPath(URL base) throws IOException {
+ ArrayList result = new ArrayList(5);
+ if (inDevelopmentMode)
+ addDevEntries(base, result, OSGI);
+ //The jars from the base always need to be added, even when running in dev mode (bug 46772)
+ addBaseJars(base, result);
+ return (URL[]) result.toArray(new URL[result.size()]);
+ }
+
+ URL constructURL(URL url, String name) {
+ //Recognize the following URLs
+ //url: file:foo/dir/
+ //url: file:foo/file.jar
+
+ String externalForm = url.toExternalForm();
+ if (externalForm.endsWith(".jar")) { //$NON-NLS-1$
+ try {
+ return new URL(JAR_SCHEME + url + "!/" + name); //$NON-NLS-1$
+ } catch (MalformedURLException e) {
+ //Ignore
+ }
+ }
+
+ try {
+ return new URL(url, name);
+ } catch (MalformedURLException e) {
+ //Ignore
+ return null;
+ }
+ }
+ private void readFrameworkExtensions(URL base, ArrayList result) throws IOException {
+ String[] extensions = getArrayFromList(System.getProperties().getProperty(PROP_EXTENSIONS));
+ String parent = new File(base.getFile()).getParent().toString();
+ ArrayList extensionResults = new ArrayList(extensions.length);
+ for (int i = 0; i < extensions.length; i++) {
+ //Search the extension relatively to the osgi plugin
+ String path = searchFor(extensions[i], parent);
+ if (path == null) {
+ log("Could not find extension: " + extensions[i]); //$NON-NLS-1$
+ continue;
+ }
+ if (debug)
+ System.out.println("Loading extension: " + extensions[i]); //$NON-NLS-1$
+
+ URL extensionURL = null;
+ if (installLocation.getProtocol().equals("file")) { //$NON-NLS-1$
+ extensionResults.add(path);
+ extensionURL = new File(path).toURL();
+ } else
+ extensionURL = new URL(installLocation.getProtocol(), installLocation.getHost(), installLocation.getPort(), path);
+
+ //Load a property file of the extension, merge its content, and in case of dev mode add the bin entries
+ Properties extensionProperties = null;
+ try {
+ extensionProperties = loadProperties(constructURL(extensionURL, ECLIPSE_PROPERTIES));
+ } catch (IOException e) {
+ if (debug)
+ System.out.println("\t" + ECLIPSE_PROPERTIES + " not found"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ String extensionClassPath = null;
+ if (extensionProperties != null)
+ extensionClassPath = extensionProperties.getProperty(PROP_CLASSPATH);
+ else // this is a "normal" RFC 101 framework extension bundle just put the base path on the classpath
+ extensionProperties = new Properties();
+ String[] entries = extensionClassPath == null || extensionClassPath.length() == 0 ? new String[] {""} : getArrayFromList(extensionClassPath); //$NON-NLS-1$
+ String qualifiedPath;
+ if (System.getProperty(PROP_CLASSPATH)==null)
+ qualifiedPath = "."; //$NON-NLS-1$
+ else
+ qualifiedPath = ""; //$NON-NLS-1$
+ for (int j = 0; j < entries.length; j++)
+ qualifiedPath += ", " + FILE_SCHEME + path + entries[j]; //$NON-NLS-1$
+ extensionProperties.put(PROP_CLASSPATH, qualifiedPath);
+ mergeProperties(System.getProperties(), extensionProperties);
+ if (inDevelopmentMode)
+ addDevEntries(extensionURL, result, extensions[i]);
+ }
+ extensionPaths = (String[]) extensionResults.toArray(new String[extensionResults.size()]);
+ }
+
+ private void addBaseJars(URL base, ArrayList result) throws IOException {
+ String baseJarList = System.getProperty(PROP_CLASSPATH);
+ if (baseJarList == null) {
+ readFrameworkExtensions(base, result);
+ baseJarList = System.getProperties().getProperty(PROP_CLASSPATH);
+ }
+
+ File fwkFile = new File(base.getFile());
+ boolean fwkIsDirectory = fwkFile.isDirectory();
+ //We found where the fwk is, remember it and its shape
+ if (fwkIsDirectory) {
+ System.getProperties().put(PROP_FRAMEWORK_SHAPE, "folder");//$NON-NLS-1$
+ } else {
+ System.getProperties().put(PROP_FRAMEWORK_SHAPE, "jar");//$NON-NLS-1$
+ }
+ String fwkPath = new File(new File(base.getFile()).getParent()).getAbsolutePath();
+ if (Character.isUpperCase(fwkPath.charAt(0))) {
+ char[] chars = fwkPath.toCharArray();
+ chars[0] = Character.toLowerCase(chars[0]);
+ fwkPath = new String(chars);
+ }
+ System.getProperties().put(PROP_FRAMEWORK_SYSPATH, fwkPath);
+
+ String[] baseJars = getArrayFromList(baseJarList);
+ if (baseJars.length == 0) {
+ if (!inDevelopmentMode && new File(base.getFile()).isDirectory())
+ throw new IOException("Unable to initialize " + PROP_CLASSPATH); //$NON-NLS-1$
+ addEntry(base, result);
+ return;
+ }
+ for (int i = 0; i < baseJars.length; i++) {
+ String string = baseJars[i];
+ try {
+ // if the string is a file: URL then *carefully* construct the
+ // URL. Otherwisejust try to build a URL. In either case, if we fail, use
+ // string as something to tack on the end of the base.
+
+ if (string.equals(".")) { //$NON-NLS-1$
+ addEntry(base, result);
+ }
+ URL url = null;
+ if (string.startsWith(FILE_SCHEME))
+ url = new File(string.substring(5)).toURL();
+ else
+ url = new URL(string);
+ addEntry(url, result);
+ } catch (MalformedURLException e) {
+ addEntry(new URL(base, string), result);
+ }
+ }
+ }
+
+ protected void addEntry(URL url, List result) {
+ if (new File(url.getFile()).exists())
+ result.add(url);
+ }
+
+ private void addDevEntries(URL base, List result, String symbolicName) throws MalformedURLException {
+ if (devClassPathProps == null)
+ return; // do nothing
+ String devPathList = devClassPathProps.getProperty(symbolicName);
+ if (devPathList == null)
+ devPathList = devClassPathProps.getProperty("*"); //$NON-NLS-1$
+ String[] locations = getArrayFromList(devPathList);
+ for (int i = 0; i < locations.length; i++) {
+ String location = locations[i];
+ File path = new File(location);
+ URL url;
+ if (path.isAbsolute())
+ url = path.toURL();
+ else {
+ // dev path is relative, combine with base location
+ char lastChar = location.charAt(location.length() - 1);
+ if ((location.endsWith(".jar") || (lastChar == '/' || lastChar == '\\'))) //$NON-NLS-1$
+ url = new URL(base, location);
+ else
+ url = new URL(base, location + "/"); //$NON-NLS-1$
+ }
+ addEntry(url, result);
+ }
+ }
+
+ /**
+ * Returns the <code>URL</code>-based class path describing where the boot classes are located.
+ *
+ * @return the url-based class path
+ * @param base the base location
+ * @exception MalformedURLException if a problem occurs computing the class path
+ */
+ protected URL[] getBootPath(String base) throws IOException {
+ URL url = null;
+ if (base != null) {
+ url = buildURL(base, true);
+ } else {
+ // search in the root location
+ url = getInstallLocation();
+ String path = new File(url.getFile(), "plugins").toString(); //$NON-NLS-1$
+ path = searchFor(framework, path);
+ if (path == null)
+ throw new RuntimeException("Could not find framework"); //$NON-NLS-1$
+ if (url.getProtocol().equals("file")) //$NON-NLS-1$
+ url = new File(path).toURL();
+ else
+ url = new URL(url.getProtocol(), url.getHost(), url.getPort(), path);
+ }
+ if (System.getProperty(PROP_FRAMEWORK) == null)
+ System.getProperties().put(PROP_FRAMEWORK, url.toExternalForm());
+ if (debug)
+ System.out.println("Framework located:\n " + url.toExternalForm()); //$NON-NLS-1$
+ // add on any dev path elements
+ URL[] result = getDevPath(url);
+ if (debug) {
+ System.out.println("Framework classpath:"); //$NON-NLS-1$
+ for (int i = 0; i < result.length; i++)
+ System.out.println(" " + result[i].toExternalForm()); //$NON-NLS-1$
+ }
+ return result;
+ }
+
+ /**
+ * Searches for the given target directory starting in the "plugins" subdirectory
+ * of the given location. If one is found then this location is returned;
+ * otherwise an exception is thrown.
+ *
+ * @return the location where target directory was found
+ * @param start the location to begin searching
+ */
+ protected String searchFor(final String target, String start) {
+ return searchFor(target, null, start);
+ }
+ protected String searchFor(final String target, final String targetSuffix, String start) {
+ // Note that File.list only gives you file names not the complete path from start
+ String[] candidates = new File(start).list();
+ if (candidates == null)
+ return null;
+
+ ArrayList matches = new ArrayList(2);
+ for (int i = 0; i < candidates.length; i++)
+ if (candidates[i].equals(target) || candidates[i].startsWith(target + "_")) //$NON-NLS-1$
+ matches.add(candidates[i]);
+ String[] names = (String[]) matches.toArray(new String[matches.size()]);
+ int result = findMax(names);
+ if (result == -1)
+ return null;
+ File candidate = new File(start, names[result]);
+ return candidate.getAbsolutePath().replace(File.separatorChar, '/') + (candidate.isDirectory() ? "/" : ""); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ protected int findMax(String[] candidates) {
+ int result = -1;
+ Object maxVersion = null;
+ for (int i = 0; i < candidates.length; i++) {
+ String name = candidates[i];
+ String version = ""; //$NON-NLS-1$ // Note: directory with version suffix is always > than directory without version suffix
+ int index = name.indexOf('_');
+ if (index != -1)
+ version = name.substring(index + 1);
+ Object currentVersion = getVersionElements(version);
+ if (maxVersion == null) {
+ result = i;
+ maxVersion = currentVersion;
+ } else {
+ if (compareVersion((Object[]) maxVersion, (Object[]) currentVersion) < 0) {
+ result = i;
+ maxVersion = currentVersion;
+ }
+ }
+ }
+ 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 int compareVersion(Object[] left, Object[] right) {
+
+ int result = ((Integer) left[0]).compareTo((Integer) right[0]); // compare major
+ if (result != 0)
+ return result;
+
+ result = ((Integer) left[1]).compareTo((Integer) right[1]); // compare minor
+ if (result != 0)
+ return result;
+
+ result = ((Integer) left[2]).compareTo((Integer) right[2]); // compare service
+ if (result != 0)
+ return result;
+
+ return ((String) left[3]).compareTo((String) right[3]); // compare qualifier
+ }
+
+ /**
+ * Do a quick parse of version identifier so its elements can be correctly compared.
+ * If we are unable to parse the full version, remaining elements are initialized
+ * with suitable defaults.
+ * @return an array of size 4; first three elements are of type Integer (representing
+ * major, minor and service) and the fourth element is of type String (representing
+ * qualifier). Note, that returning anything else will cause exceptions in the caller.
+ */
+ private Object[] getVersionElements(String version) {
+ if (version.endsWith(".jar")) //$NON-NLS-1$
+ version = version.substring(0, version.length() - 4);
+ Object[] result = {new Integer(0), new Integer(0), new Integer(0), ""}; //$NON-NLS-1$
+ StringTokenizer t = new StringTokenizer(version, "."); //$NON-NLS-1$
+ String token;
+ int i = 0;
+ while (t.hasMoreTokens() && i < 4) {
+ token = t.nextToken();
+ if (i < 3) {
+ // major, minor or service ... numeric values
+ try {
+ result[i++] = new Integer(token);
+ } catch (Exception e) {
+ // invalid number format - use default numbers (0) for the rest
+ break;
+ }
+ } else {
+ // qualifier ... string value
+ result[i++] = token;
+ }
+ }
+ return result;
+ }
+
+ private static URL buildURL(String spec, boolean trailingSlash) {
+ if (spec == null)
+ return null;
+ boolean isFile = spec.startsWith(FILE_SCHEME);
+ try {
+ if (isFile) {
+ File toAdjust = new File(spec.substring(5));
+ if (toAdjust.isDirectory())
+ return adjustTrailingSlash(toAdjust.toURL(), trailingSlash);
+ else
+ return toAdjust.toURL();
+ } else
+ return new URL(spec);
+ } catch (MalformedURLException e) {
+ // if we failed and it is a file spec, there is nothing more we can do
+ // otherwise, try to make the spec into a file URL.
+ if (isFile)
+ return null;
+ try {
+ File toAdjust = new File(spec);
+ if (toAdjust.isDirectory())
+ return adjustTrailingSlash(toAdjust.toURL(), trailingSlash);
+ else
+ return toAdjust.toURL();
+ } catch (MalformedURLException e1) {
+ return null;
+ }
+ }
+ }
+
+ private static URL adjustTrailingSlash(URL url, boolean trailingSlash) throws MalformedURLException {
+ String file = url.getFile();
+ if (trailingSlash == (file.endsWith("/"))) //$NON-NLS-1$
+ return url;
+ file = trailingSlash ? file + "/" : file.substring(0, file.length() - 1); //$NON-NLS-1$
+ return new URL(url.getProtocol(), url.getHost(), file);
+ }
+
+ private URL buildLocation(String property, URL defaultLocation, String userDefaultAppendage) {
+ URL result = null;
+ String location = System.getProperty(property);
+ System.getProperties().remove(property);
+ // if the instance location is not set, predict where the workspace will be and
+ // put the instance area inside the workspace meta area.
+ try {
+ if (location == null)
+ result = defaultLocation;
+ else if (location.equalsIgnoreCase(NONE))
+ return null;
+ else if (location.equalsIgnoreCase(NO_DEFAULT))
+ result = buildURL(location, true);
+ else {
+ if (location.startsWith(USER_HOME)) {
+ String base = substituteVar(location, USER_HOME, PROP_USER_HOME);
+ location = new File(base, userDefaultAppendage).getAbsolutePath();
+ } else if (location.startsWith(USER_DIR)) {
+ String base = substituteVar(location, USER_DIR, PROP_USER_DIR);
+ location = new File(base, userDefaultAppendage).getAbsolutePath();
+ }
+ result = buildURL(location, true);
+ }
+ } finally {
+ if (result != null)
+ System.getProperties().put(property, result.toExternalForm());
+ }
+ return result;
+ }
+
+ private String substituteVar(String source, String var, String prop) {
+ String value = System.getProperty(prop, ""); //$NON-NLS-1$
+ return value + source.substring(var.length());
+ }
+
+ /**
+ * Retuns the default file system path for the configuration location.
+ * By default the configuration information is in the installation directory
+ * if this is writeable. Otherwise it is located somewhere in the user.home
+ * area relative to the current product.
+ * @return the default file system path for the configuration information
+ */
+ private 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 install = getInstallLocation();
+ // TODO a little dangerous here. Basically we have to assume that it is a file URL.
+ if (install.getProtocol().equals("file")) { //$NON-NLS-1$
+ File installDir = new File(install.getFile());
+ if (canWrite(installDir))
+ return installDir.getAbsolutePath() + File.separator + CONFIG_DIR;
+ }
+ // 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(File installDir) {
+ if (installDir.canWrite() == false)
+ return false;
+
+ if (!installDir.isDirectory())
+ return false;
+
+ File fileTest = null;
+ try {
+ fileTest = File.createTempFile("writtableArea", null, installDir); //$NON-NLS-1$
+ } catch (IOException e) {
+ //If an exception occured while trying to create the file, it means that it is not writtable
+ return false;
+ } finally {
+ if (fileTest != null)
+ fileTest.delete();
+ }
+ return true;
+ }
+
+ /**
+ * Returns a files system path for an area in the user.home region related to the
+ * current product. The given appendage is added to this base location
+ * @param pathAppendage the path segments to add to computed base
+ * @return a file system location in the user.home area related the the current
+ * product and the given appendage
+ */
+ private 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.
+ URL installURL = getInstallLocation();
+ if (installURL == null)
+ return null;
+ File installDir = new File(installURL.getFile());
+ 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; //$NON-NLS-1$
+ } catch (IOException e) {
+ // Do nothing if we get an exception. We will default to a standard location
+ // in the user's home dir.
+ }
+ }
+ String userHome = System.getProperty(PROP_USER_HOME);
+ return new File(userHome, appName + "/" + pathAppendage).getAbsolutePath(); //$NON-NLS-1$
+ }
+
+ /**
+ * Runs this launcher with the arguments specified in the given string.
+ *
+ * @param argString the arguments string
+ */
+ public static void main(String argString) {
+ Vector list = new Vector(5);
+ for (StringTokenizer tokens = new StringTokenizer(argString, " "); tokens.hasMoreElements();) //$NON-NLS-1$
+ list.addElement(tokens.nextElement());
+ main((String[]) list.toArray(new String[list.size()]));
+ }
+
+ /**
+ * Runs the platform with the given arguments. The arguments must identify
+ * an application to run (e.g., <code>-application com.example.application</code>).
+ * After running the application <code>System.exit(N)</code> is executed.
+ * The value of N is derived from the value returned from running the application.
+ * If the application's return value is an <code>Integer</code>, N is this value.
+ * In all other cases, N = 0.
+ * <p>
+ * Clients wishing to run the platform without a following <code>System.exit</code>
+ * call should use <code>run()</code>.
+ * </p>
+ *
+ * @param args the command line arguments
+ * @see #run(String[])
+ */
+ public static void main(String[] args) {
+ int result = 0;
+ try {
+ result = new Main().run(args);
+ } catch (Throwable t) {
+ // This is *really* unlikely to happen - run() takes care of exceptional situations.
+ // In case something weird happens, just dump stack - logging is not available at this point
+ t.printStackTrace();
+ } finally {
+ if (!Boolean.getBoolean(PROP_NOSHUTDOWN))
+ // make sure we always terminate the VM
+ System.exit(result);
+ }
+ }
+
+
+ /**
+ * Runs the platform with the given arguments. The arguments must identify
+ * an application to run (e.g., <code>-application com.example.application</code>).
+ * Returns the value returned from running the application.
+ * If the application's return value is an <code>Integer</code>, N is this value.
+ * In all other cases, N = 0.
+ *
+ * @param args the command line arguments
+ */
+ public int run(String[] args) {
+ int result = 0;
+ try {
+ basicRun(args);
+ String exitCode = System.getProperty(PROP_EXITCODE);
+ try {
+ result = exitCode == null ? 0 : Integer.parseInt(exitCode);
+ } catch (NumberFormatException e) {
+ result = 17;
+ }
+ } catch (Throwable e) {
+ // only log the exceptions if they have not been caught by the
+ // EclipseStarter (i.e., if the exitCode is not 13)
+ if (!"13".equals(System.getProperty(PROP_EXITCODE))) { //$NON-NLS-1$
+ log("Exception launching the Eclipse Platform:"); //$NON-NLS-1$
+ log(e);
+ String message = "An error has occurred"; //$NON-NLS-1$
+ if (logFile == null)
+ message += " and could not be logged: \n" + e.getMessage(); //$NON-NLS-1$
+ else
+ message += ". See the log file\n" + logFile.getAbsolutePath(); //$NON-NLS-1$
+ System.getProperties().put(PROP_EXITDATA, message);
+ }
+ // Return "unlucky" 13 as the exit code. The executable will recognize
+ // this constant and display a message to the user telling them that
+ // there is information in their log file.
+ result = 13;
+ } finally {
+ // always try putting down the splash screen just in case the application failed to do so
+ takeDownSplash();
+ }
+ // Return an int exit code and ensure the system property is set.
+ System.getProperties().put(PROP_EXITCODE, Integer.toString(result));
+ setExitData();
+ return result;
+ }
+
+ private void setExitData() {
+ String data = System.getProperty(PROP_EXITDATA);
+ if (data == null)
+ return;
+ bridge.setExitData(data);
+ }
+
+ /**
+ * Processes the command line arguments. The general principle is to NOT
+ * consume the arguments and leave them to be processed by Eclipse proper.
+ * There are a few args which are directed towards main() and a few others
+ * which we need to know about. Very few should actually be consumed here.
+ *
+ * @return the arguments to pass through to the launched application
+ * @param args the command line arguments
+ */
+ protected String[] processCommandLine(String[] args) {
+ if (args.length == 0)
+ 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 (args[i].equalsIgnoreCase(DEBUG)) {
+ debug = true;
+ // passed thru this arg (i.e., do not set found = true)
+ continue;
+ }
+
+ // look for and consume the nosplash directive. This supercedes any
+ // -showsplash command that might be present.
+ if (args[i].equalsIgnoreCase(NOSPLASH)) {
+ splashDown = true;
+ found = true;
+ }
+
+ if (args[i].equalsIgnoreCase(NOEXIT)) {
+ System.getProperties().put(PROP_NOSHUTDOWN, "true"); //$NON-NLS-1$
+ found = true;
+ }
+
+ // look for the command to use to show the splash screen
+ if (args[i].equalsIgnoreCase(SHOWSPLASH)) {
+ showSplash = true;
+ found = true;
+ }
+
+ // check if this is initialization pass
+ if (args[i].equalsIgnoreCase(INITIALIZE)) {
+ initialize = true;
+ // passed thru this arg (i.e., do not set found = true)
+ continue;
+ }
+
+ // 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$
+ inDevelopmentMode = true;
+ // do not mark the arg as found so it will be passed through
+ continue;
+ }
+
+ // done checking for args. Remember where an arg was found
+ if (found) {
+ configArgs[configArgIndex++] = i;
+ continue;
+ }
+
+ // look for the VM args arg. We have to do that before looking to see
+ // if the next element is a -arg as the thing following -vmargs may in
+ // fact be another -arg.
+ if (args[i].equalsIgnoreCase(VMARGS)) {
+ // consume the -vmargs arg itself
+ args[i] = null;
+ i++;
+ vmargs = new String[args.length - i];
+ for (int j = 0; i < args.length; i++) {
+ vmargs[j++] = args[i];
+ args[i] = null;
+ }
+ 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 development mode and class path entries.
+ if (args[i - 1].equalsIgnoreCase(DEV)) {
+ inDevelopmentMode = true;
+ devClassPathProps = processDevArg(arg);
+ if (devClassPathProps != null) {
+ devClassPath = devClassPathProps.getProperty(OSGI);
+ if (devClassPath == null)
+ devClassPath = devClassPathProps.getProperty("*"); //$NON-NLS-1$
+ }
+ continue;
+ }
+
+ // look for the framework to run
+ if (args[i - 1].equalsIgnoreCase(FRAMEWORK)) {
+ framework = arg;
+ found = true;
+ }
+
+ if (args[i - 1].equalsIgnoreCase(OS)) {
+ os = arg;
+ // passed thru this arg
+ continue;
+ }
+
+ // look for explicitly set install root
+ // Consume the arg here to ensure that the launcher and Eclipse get the
+ // same value as each other.
+ if (args[i - 1].equalsIgnoreCase(INSTALL)) {
+ System.getProperties().put(PROP_INSTALL_AREA, arg);
+ found = true;
+ }
+
+ // look for the configuration to use.
+ // Consume the arg here to ensure that the launcher and Eclipse get the
+ // same value as each other.
+ if (args[i - 1].equalsIgnoreCase(CONFIGURATION)) {
+ System.getProperties().put(PROP_CONFIG_AREA, arg);
+ found = true;
+ }
+
+ // look for the name to use by the launcher
+ if (args[i - 1].equalsIgnoreCase(NAME)) {
+ name = arg;
+ found = true;
+ }
+
+ // look for the launcher location
+ if (args[i - 1].equalsIgnoreCase(LAUNCHER)) {
+ launcher = arg;
+ found = true;
+ }
+
+ // look for the command to use to end the splash screen
+ if (args[i - 1].equalsIgnoreCase(ENDSPLASH)) {
+ endSplash = arg;
+ found = true;
+ }
+
+ // look for the VM location arg
+ if (args[i - 1].equalsIgnoreCase(VM)) {
+ vm = arg;
+ found = true;
+ }
+
+ //look for the nl setting
+ if (args[i - 1].equalsIgnoreCase(NL)) {
+ System.getProperties().put(PROP_NL, arg);
+ found = true;
+ }
+
+ // done checking for args. Remember where an arg was found
+ if (found) {
+ configArgs[configArgIndex++] = i - 1;
+ configArgs[configArgIndex++] = i;
+ }
+ }
+ // remove all the arguments consumed by this argument parsing
+ String[] passThruArgs = new String[args.length - configArgIndex - (vmargs == null ? 0 : vmargs.length + 1)];
+ configArgIndex = 0;
+ int j = 0;
+ for (int i = 0; i < args.length; i++) {
+ if (i == configArgs[configArgIndex])
+ configArgIndex++;
+ else if (args[i] != null)
+ passThruArgs[j++] = args[i];
+ }
+ return passThruArgs;
+ }
+
+ private Properties processDevArg(String arg) {
+ if (arg == null)
+ return null;
+ try {
+ URL location = new URL(arg);
+ return load(location, null);
+ } catch (MalformedURLException e) {
+ // the arg was not a URL so use it as is.
+ Properties result = new Properties();
+ result.put("*", arg); //$NON-NLS-1$
+ return result;
+ } catch (IOException e) {
+ // TODO consider logging here
+ return null;
+ }
+ }
+
+ private URL getConfigurationLocation() {
+ if (configurationLocation != null)
+ return configurationLocation;
+ configurationLocation = buildLocation(PROP_CONFIG_AREA, null, ""); //$NON-NLS-1$
+ if (configurationLocation == null) {
+ configurationLocation = buildLocation(PROP_CONFIG_AREA_DEFAULT, null, ""); //$NON-NLS-1$
+ if (configurationLocation == null)
+ configurationLocation = buildURL(computeDefaultConfigurationLocation(), true);
+ }
+ if (configurationLocation != null)
+ System.getProperties().put(PROP_CONFIG_AREA, configurationLocation.toExternalForm());
+ if (debug)
+ System.out.println("Configuration location:\n " + configurationLocation); //$NON-NLS-1$
+ return configurationLocation;
+ }
+
+ private void processConfiguration() {
+ // if the configuration area is not already defined, discover the config area by
+ // trying to find a base config area. This is either defined in a system property or
+ // is computed relative to the install location.
+ // Note that the config info read here is only used to determine a value
+ // for the user configuration area
+ URL baseConfigurationLocation = null;
+ Properties baseConfiguration = null;
+ if (System.getProperty(PROP_CONFIG_AREA) == null) {
+ String baseLocation = System.getProperty(PROP_BASE_CONFIG_AREA);
+ if (baseLocation != null)
+ // here the base config cannot have any symbolic (e..g, @xxx) entries. It must just
+ // point to the config file.
+ baseConfigurationLocation = buildURL(baseLocation, true);
+ if (baseConfigurationLocation == null)
+ try {
+ // here we access the install location but this is very early. This case will only happen if
+ // the config area is not set and the base config area is not set (or is bogus).
+ // In this case we compute based on the install location.
+ baseConfigurationLocation = new URL(getInstallLocation(), CONFIG_DIR);
+ } catch (MalformedURLException e) {
+ // leave baseConfigurationLocation null
+ }
+ baseConfiguration = loadConfiguration(baseConfigurationLocation);
+ if (baseConfiguration != null) {
+ // if the base sets the install area then use that value if the property. We know the
+ // property is not already set.
+ String location = baseConfiguration.getProperty(PROP_CONFIG_AREA);
+ if (location != null)
+ System.getProperties().put(PROP_CONFIG_AREA, location);
+ // if the base sets the install area then use that value if the property is not already set.
+ // This helps in selfhosting cases where you cannot easily compute the install location
+ // from the code base.
+ location = baseConfiguration.getProperty(PROP_INSTALL_AREA);
+ if (location != null && System.getProperty(PROP_INSTALL_AREA) == null)
+ System.getProperties().put(PROP_INSTALL_AREA, location);
+ }
+ }
+
+ // Now we know where the base configuration is supposed to be. Go ahead and load
+ // it and merge into the System properties. Then, if cascaded, read the parent configuration
+ // Note that the parent may or may not be the same parent as we read above since the
+ // base can define its parent. The first parent we read was either defined by the user
+ // on the command line or was the one in the install dir.
+ // if the config or parent we are about to read is the same as the base config we read above,
+ // just reuse the base
+ Properties configuration = baseConfiguration;
+ if (configuration == null || !getConfigurationLocation().equals(baseConfigurationLocation))
+ configuration = loadConfiguration(getConfigurationLocation());
+ mergeProperties(System.getProperties(), configuration);
+ if ("false".equalsIgnoreCase(System.getProperty(PROP_CONFIG_CASCADED))) //$NON-NLS-1$
+ // if we are not cascaded then remove the parent property even if it was set.
+ System.getProperties().remove(PROP_SHARED_CONFIG_AREA);
+ else {
+ ensureAbsolute(PROP_SHARED_CONFIG_AREA);
+ URL sharedConfigURL = buildLocation(PROP_SHARED_CONFIG_AREA, null, ""); //$NON-NLS-1$
+ if (sharedConfigURL == null)
+ try {
+ // there is no shared config value so compute one
+ sharedConfigURL = new URL(getInstallLocation(), CONFIG_DIR);
+ } catch (MalformedURLException e) {
+ // leave sharedConfigurationLocation null
+ }
+ // if the parent location is different from the config location, read it too.
+ if (sharedConfigURL != null) {
+ if (sharedConfigURL.equals(getConfigurationLocation()))
+ // remove the property to show that we do not have a parent.
+ System.getProperties().remove(PROP_SHARED_CONFIG_AREA);
+ else {
+ // if the parent we are about to read is the same as the base config we read above,
+ // just reuse the base
+ configuration = baseConfiguration;
+ if (!sharedConfigURL.equals(baseConfigurationLocation))
+ configuration = loadConfiguration(sharedConfigURL);
+ mergeProperties(System.getProperties(), configuration);
+ System.getProperties().put(PROP_SHARED_CONFIG_AREA, sharedConfigURL.toExternalForm());
+ if (debug)
+ System.out.println("Shared configuration location:\n " + sharedConfigURL.toExternalForm()); //$NON-NLS-1$
+ }
+ }
+ }
+ // setup the path to the framework
+ String urlString = System.getProperty(PROP_FRAMEWORK, null);
+ if (urlString != null) {
+ URL url = buildURL(urlString, true);
+ System.getProperties().put(PROP_FRAMEWORK, url.toExternalForm());
+ bootLocation = resolve(urlString);
+ }
+ }
+
+ /**
+ * Ensures the value for a system property is an absolute URL. Relative URLs are translated to
+ * absolute URLs by taking the install URL as reference.
+ *
+ * @param locationProperty the key for a system property containing a URL
+ */
+ private void ensureAbsolute(String locationProperty) {
+ String propertyValue = System.getProperty(locationProperty);
+ if (propertyValue == null)
+ // property not defined
+ return;
+ URL locationURL = null;
+ try {
+ locationURL = new URL(propertyValue);
+ } catch (MalformedURLException e) {
+ // property is not a valid URL
+ return;
+ }
+ String locationPath = locationURL.getPath();
+ if (locationPath.startsWith("/")) //$NON-NLS-1$
+ // property value is absolute
+ return;
+ URL installURL = getInstallLocation();
+ if (!locationURL.getProtocol().equals(installURL.getProtocol()))
+ // not same protocol
+ return;
+ try {
+ URL absoluteURL = new URL(installURL, locationPath);
+ System.getProperties().put(locationProperty, absoluteURL.toExternalForm());
+ } catch (MalformedURLException e) {
+ // should not happen - the relative URL is known to be valid
+ }
+ }
+
+ /**
+ * Returns url of the location this class was loaded from
+ */
+ private URL getInstallLocation() {
+ if (installLocation != null)
+ return installLocation;
+
+ // value is not set so compute the default and set the value
+ String installArea = System.getProperty(PROP_INSTALL_AREA);
+ if (installArea != null) {
+ installLocation = buildURL(installArea, true);
+ if (installLocation == null)
+ throw new IllegalStateException("Install location is invalid: " + installArea); //$NON-NLS-1$
+ System.getProperties().put(PROP_INSTALL_AREA, installLocation.toExternalForm());
+ if (debug)
+ System.out.println("Install location:\n " + installLocation); //$NON-NLS-1$
+ return installLocation;
+ }
+
+ ProtectionDomain domain = Main.class.getProtectionDomain();
+ CodeSource source = null;
+ URL result = null;
+ if (domain != null)
+ source = domain.getCodeSource();
+ if (source == null || domain == null) {
+ if (debug)
+ System.out.println("CodeSource location is null. Defaulting the install location to file:startup.jar"); //$NON-NLS-1$
+ try {
+ result = new URL("file:startup.jar"); //$NON-NLS-1$
+ } catch (MalformedURLException e2) {
+ //Ignore
+ }
+ }
+ if (source != null)
+ result = source.getLocation();
+
+ String path = decode(result.getFile());
+ // normalize to not have leading / so we can check the form
+ File file = new File(path);
+ path = file.toString().replace('\\', '/');
+ // TODO need a better test for windows
+ // If on Windows then canonicalize the drive letter to be lowercase.
+ // remember that there may be UNC paths
+ if (File.separatorChar == '\\')
+ if (Character.isUpperCase(path.charAt(0))) {
+ char[] chars = path.toCharArray();
+ chars[0] = Character.toLowerCase(chars[0]);
+ path = new String(chars);
+ }
+ if (path.toLowerCase().endsWith(".jar")) //$NON-NLS-1$
+ path = path.substring(0, path.lastIndexOf("/") + 1); //$NON-NLS-1$
+ if (path.toLowerCase().endsWith("/plugins/")) //$NON-NLS-1$
+ path = path.substring(0, path.length() - "/plugins/".length());
+ try {
+ try {
+ // create a file URL (via File) to normalize the form (e.g., put
+ // the leading / on if necessary)
+ path = new File(path).toURL().getFile();
+ } catch (MalformedURLException e1) {
+ // will never happen. The path is straight from a URL.
+ }
+ installLocation = new URL(result.getProtocol(), result.getHost(), result.getPort(), path);
+ System.getProperties().put(PROP_INSTALL_AREA, installLocation.toExternalForm());
+ } catch (MalformedURLException e) {
+ // TODO Very unlikely case. log here.
+ }
+ if (debug)
+ System.out.println("Install location:\n " + installLocation); //$NON-NLS-1$
+ return installLocation;
+ }
+
+ /*
+ * Load the given configuration file
+ */
+ private Properties loadConfiguration(URL url) {
+ Properties result = null;
+ try {
+ url = new URL(url, CONFIG_FILE);
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ try {
+ if (debug)
+ System.out.print("Configuration file:\n " + url.toString()); //$NON-NLS-1$
+ result = loadProperties(url);
+ if (debug)
+ System.out.println(" loaded"); //$NON-NLS-1$
+ } catch (IOException e) {
+ if (debug)
+ System.out.println(" not found or not read"); //$NON-NLS-1$
+ }
+ return result;
+ }
+
+ private Properties loadProperties(URL url) throws IOException {
+ // try to load saved configuration file (watch for failed prior save())
+ if (url == null)
+ return null;
+ Properties result = null;
+ IOException originalException = null;
+ try {
+ result = load(url, null); // try to load config file
+ } catch (IOException e1) {
+ originalException = e1;
+ try {
+ result = load(url, CONFIG_FILE_TEMP_SUFFIX); // check for failures on save
+ } catch (IOException e2) {
+ try {
+ result = load(url, CONFIG_FILE_BAK_SUFFIX); // check for failures on save
+ } catch (IOException e3) {
+ throw originalException; // we tried, but no config here ...
+ }
+ }
+ }
+ return result;
+ }
+
+ /*
+ * Load the configuration
+ */
+ private Properties load(URL url, String suffix) throws IOException {
+ // figure out what we will be loading
+ if (suffix != null && !suffix.equals("")) //$NON-NLS-1$
+ url = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile() + suffix);
+
+ // try to load saved configuration file
+ Properties props = new Properties();
+ InputStream is = null;
+ try {
+ is = url.openStream();
+ props.load(is);
+ } finally {
+ if (is != null)
+ try {
+ is.close();
+ } catch (IOException e) {
+ //ignore failure to close
+ }
+ }
+ return props;
+ }
+
+ /*
+ * Handle splash screen.
+ * We support 2 startup scenarios:
+ *
+ * (1) the executable launcher put up the splash screen. In that
+ * scenario we are invoked with -endsplash command which is
+ * fully formed to take down the splash screen
+ *
+ * (2) the executable launcher did not put up the splash screen,
+ * but invokes Eclipse with partially formed -showsplash command.
+ * In this scenario we determine which splash to display (based on
+ * feature information) and then call -showsplash command.
+ *
+ * In both scenarios we pass a handler (Runnable) to the platform.
+ * The handler is called as a result of the launched application calling
+ * Platform.endSplash(). In the first scenario this results in the
+ * -endsplash command being executed. In the second scenario this
+ * results in the process created as a result of the -showsplash command
+ * being destroyed.
+ *
+ * @param defaultPath search path for the boot plugin
+ */
+ private void handleSplash(URL[] defaultPath) {
+ // run without splash if we are initializing or nosplash
+ // was specified (splashdown = true)
+ if (initialize || splashDown) {
+ showSplash = false;
+ endSplash = null;
+ return;
+ }
+
+ if (showSplash || endSplash != null) {
+ // Register the endSplashHandler to be run at VM shutdown. This hook will be
+ // removed once the splash screen has been taken down.
+ try {
+ Runtime.getRuntime().addShutdownHook(splashHandler);
+ } catch(Throwable ex) {
+ // Best effort to register the handler
+ }
+ }
+
+ // if -endsplash is specified, use it and ignore any -showsplash command
+ if (endSplash != null) {
+ showSplash = false;
+ return;
+ }
+
+ // check if we are running without a splash screen
+ if (!showSplash)
+ return;
+
+ // determine the splash location
+ String location = getSplashLocation(defaultPath);
+ if (debug)
+ System.out.println("Splash location:\n " + location); //$NON-NLS-1$
+ if (location == null)
+ return;
+
+ bridge.showSplash(location);
+ long handle = bridge.getSplashHandle();
+ if(handle != 0) {
+ System.setProperty("org.eclipse.splash.handle", String.valueOf(handle));
+ System.setProperty("org.eclipse.splash.bundle", location);
+ bridge.updateSplash();
+ }
+ }
+
+ /*
+ * Take down the splash screen.
+ */
+ protected void takeDownSplash() {
+ if (splashDown) // splash is already down
+ return;
+
+ splashDown = bridge.takeDownSplash();;
+
+ try {
+ Runtime.getRuntime().removeShutdownHook(splashHandler);
+ } catch (Throwable e) {
+ // OK to ignore this, happens when the VM is already shutting down
+ }
+ }
+
+ /*
+ * Return path of the splash image to use. First search the defined splash path.
+ * If that does not work, look for a default splash. Currently the splash must be in the file system
+ * so the return value here is the file system path.
+ */
+ private String getSplashLocation(URL[] bootPath) {
+ String result = System.getProperty(PROP_SPLASHLOCATION);
+ if (result != null)
+ return result;
+ String splashPath = System.getProperty(PROP_SPLASHPATH);
+ if (splashPath != null) {
+ String[] entries = getArrayFromList(splashPath);
+ ArrayList path = new ArrayList(entries.length);
+ for (int i = 0; i < entries.length; i++) {
+ String entry = resolve(entries[i]);
+ if (entry == null || entry.startsWith(FILE_SCHEME)) {
+ File entryFile = new File(entry.substring(5).replace('/', File.separatorChar));
+ entry = searchFor(entryFile.getName(), entryFile.getParent());
+ if (entry != null)
+ path.add(entry);
+ } else
+ log("Invalid splash path entry: " + entries[i]); //$NON-NLS-1$
+ }
+ // see if we can get a splash given the splash path
+ result = searchForSplash((String[]) path.toArray(new String[path.size()]));
+ if (result != null) {
+ System.getProperties().put(PROP_SPLASHLOCATION, result);
+ return result;
+ }
+ }
+
+ // can't find it on the splashPath so look for a default splash
+ String temp = bootPath[0].getFile(); // take the first path element
+ temp = temp.replace('/', File.separatorChar);
+ int ix = temp.lastIndexOf("plugins" + File.separator); //$NON-NLS-1$
+ if (ix != -1) {
+ int pix = temp.indexOf(File.separator, ix + 8);
+ if (pix != -1) {
+ temp = temp.substring(0, pix);
+ result = searchForSplash(new String[] {temp});
+ if (result != null)
+ System.getProperties().put(PROP_SPLASHLOCATION, result);
+ }
+ }
+ return result;
+ }
+
+ /*
+ * Do a locale-sensitive lookup of splash image
+ */
+ private String searchForSplash(String[] searchPath) {
+ if (searchPath == null)
+ return null;
+
+ // Get the splash screen for the specified locale
+ String locale = (String) System.getProperties().get(PROP_NL);
+ if (locale == null)
+ locale = Locale.getDefault().toString();
+ String[] nlVariants = buildNLVariants(locale);
+
+ for (int i=0; i<nlVariants.length; i++) {
+ for (int j=0; j<searchPath.length; j++) {
+ // do we have a JAR?
+ if (isJAR(searchPath[j])) {
+ String result = extractSplashFromJAR(searchPath[j], nlVariants[i]);
+ if (result != null)
+ return result;
+ } else {
+ // we have a file or a directory
+ String path = searchPath[j];
+ if (!path.endsWith(File.separator))
+ path += File.separator;
+ path += nlVariants[i];
+ File result = new File(path);
+ if (result.exists())
+ return result.getAbsolutePath(); // return the first match found [20063]
+ }
+ }
+ }
+
+ // sorry, could not find splash image
+ return null;
+ }
+
+ /**
+ * Transfers all available bytes from the given input stream to the given output stream.
+ * Regardless of failure, this method closes both streams.
+ */
+ private static void transferStreams(InputStream source, OutputStream destination) {
+ byte[] buffer = new byte[8096];
+ try {
+ while (true) {
+ int bytesRead = -1;
+ try {
+ bytesRead = source.read(buffer);
+ } catch (IOException e) {
+ return;
+ }
+ if (bytesRead == -1)
+ break;
+ try {
+ destination.write(buffer, 0, bytesRead);
+ } catch (IOException e) {
+ return;
+ }
+ }
+ } finally {
+ try {
+ source.close();
+ } catch (IOException e) {
+ // ignore
+ } finally {
+ //close destination in finally in case source.close fails
+ try {
+ destination.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ /*
+ * Look for the specified spash file in the given JAR and extract it to the config
+ * area for caching purposes.
+ */
+ private String extractSplashFromJAR(String jarPath, String splashPath) {
+ String configLocation = System.getProperty(PROP_CONFIG_AREA);
+ if (configLocation == null) {
+ log("Configuration area not set yet. Unable to extract splash from JAR'd plug-in: " + jarPath); //$NON-NLS-1$
+ return null;
+ }
+ URL configURL = buildURL(configLocation, false);
+ if (configURL == null)
+ return null;
+ // cache the splash in the OSGi sub-dir in the config area
+ File splash = new File(configURL.getPath(), OSGI);
+ splash = new File(splash, splashPath);
+ // if we have already extracted this file before, then return
+ if (splash.exists()) {
+ // if we are running with -clean then delete the cached splash file
+ boolean clean = false;
+ for (int i=0; i<commands.length; i++) {
+ if (CLEAN.equalsIgnoreCase(commands[i])) {
+ clean = true;
+ splash.delete();
+ break;
+ }
+ }
+ if (!clean)
+ return splash.getAbsolutePath();
+ }
+ ZipFile file;
+ try {
+ file = new ZipFile(jarPath);
+ } catch (IOException e) {
+ log("Exception looking for splash in JAR file: " + jarPath); //$NON-NLS-1$
+ log(e);
+ return null;
+ }
+ ZipEntry entry = file.getEntry(splashPath.replace(File.separatorChar, '/'));
+ if (entry == null)
+ return null;
+ InputStream input = null;
+ try {
+ input = file.getInputStream(entry);
+ } catch (IOException e) {
+ log("Exception opening splash: " + entry.getName() + " in JAR file: " + jarPath); //$NON-NLS-1$ //$NON-NLS-2$
+ log(e);
+ return null;
+ }
+ new File(splash.getParent()).mkdirs();
+ OutputStream output;
+ try {
+ output = new BufferedOutputStream(new FileOutputStream(splash));
+ } catch (FileNotFoundException e) {
+ try {
+ input.close();
+ } catch (IOException e1) {
+ // ignore
+ }
+ return null;
+ }
+ transferStreams(input, output);
+ return splash.exists() ? splash.getAbsolutePath() : null;
+ }
+
+ /*
+ * Return a boolean value indicating whether or not the given
+ * path represents a JAR file.
+ */
+ private boolean isJAR(String path) {
+ if (path.endsWith(File.separator))
+ return false;
+ int index = path.lastIndexOf('.');
+ if (index == -1)
+ return false;
+ index++;
+ // handle the case where we have a '.' at the end
+ if (index >= path.length())
+ return false;
+ return "JAR".equalsIgnoreCase(path.substring(index)); //$NON-NLS-1$
+ }
+
+ /*
+ * Build an array of path suffixes based on the given NL which is suitable
+ * for splash path searching. The returned array contains paths in order
+ * from most specific to most generic. So, in the FR_fr locale, it will return
+ * "nl/fr/FR/splash.bmp", then "nl/fr/splash.bmp", and finally "splash.bmp".
+ * (we always search the root)
+ */
+ private static String[] buildNLVariants(String locale) {
+ //build list of suffixes for loading resource bundles
+ String nl = locale;
+ ArrayList result = new ArrayList(4);
+ int lastSeparator;
+ while (true) {
+ result.add("nl" + File.separatorChar + nl.replace('_', File.separatorChar) + File.separatorChar + SPLASH_IMAGE); //$NON-NLS-1$
+ lastSeparator = nl.lastIndexOf('_');
+ if (lastSeparator == -1)
+ break;
+ nl = nl.substring(0, lastSeparator);
+ }
+ //add the empty suffix last (most general)
+ result.add(SPLASH_IMAGE);
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+ /*
+ * resolve platform:/base/ URLs
+ */
+ private String resolve(String urlString) {
+ // handle the case where people mistakenly spec a refererence: url.
+ if (urlString.startsWith(REFERENCE_SCHEME)) {
+ urlString = urlString.substring(10);
+ System.getProperties().put(PROP_FRAMEWORK, urlString);
+ }
+ if (urlString.startsWith(PLATFORM_URL)) {
+ String path = urlString.substring(PLATFORM_URL.length());
+ return getInstallLocation() + path;
+ } else
+ return urlString;
+ }
+
+ /*
+ * Entry point for logging.
+ */
+ protected synchronized void log(Object obj) {
+ if (obj == null)
+ return;
+ try {
+ openLogFile();
+ try {
+ if (newSession) {
+ log.write(SESSION);
+ log.write(' ');
+ String timestamp = new Date().toString();
+ log.write(timestamp);
+ log.write(' ');
+ for (int i = SESSION.length() + timestamp.length(); i < 78; i++)
+ log.write('-');
+ log.newLine();
+ newSession = false;
+ }
+ write(obj);
+ } finally {
+ if (logFile == null) {
+ if (log != null)
+ log.flush();
+ } else
+ closeLogFile();
+ }
+ } catch (Exception e) {
+ 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 {
+ log = logForStream(System.err);
+ write(obj);
+ log.flush();
+ } catch (Exception e2) {
+ System.err.println("An exception occurred while logging to the console:"); //$NON-NLS-1$
+ e2.printStackTrace(System.err);
+ }
+ } finally {
+ log = null;
+ }
+ }
+
+ /*
+ * This should only be called from #log()
+ */
+ private void write(Object obj) throws IOException {
+ if (obj == null)
+ return;
+ if (obj instanceof Throwable) {
+ log.write(STACK);
+ log.newLine();
+ ((Throwable) obj).printStackTrace(new PrintWriter(log));
+ } else {
+ log.write(ENTRY);
+ log.write(' ');
+ log.write(PLUGIN_ID);
+ log.write(' ');
+ log.write(String.valueOf(ERROR));
+ log.write(' ');
+ log.write(String.valueOf(0));
+ log.write(' ');
+ log.write(getDate(new Date()));
+ log.newLine();
+ log.write(MESSAGE);
+ log.write(' ');
+ log.write(String.valueOf(obj));
+ }
+ log.newLine();
+ }
+
+ protected 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;
+ }
+
+ private void computeLogFileLocation() {
+ String logFileProp = System.getProperty(PROP_LOGFILE);
+ if (logFileProp != null) {
+ if (logFile == null || !logFileProp.equals(logFile.getAbsolutePath())) {
+ logFile = new File(logFileProp);
+ new File(logFile.getParent()).mkdirs();
+ }
+ return;
+ }
+
+ // compute the base location and then append the name of the log file
+ URL base = buildURL(System.getProperty(PROP_CONFIG_AREA), false);
+ if (base == null)
+ return;
+ logFile = new File(base.getPath(), Long.toString(System.currentTimeMillis()) + ".log"); //$NON-NLS-1$
+ new File(logFile.getParent()).mkdirs();
+ System.getProperties().put(PROP_LOGFILE, logFile.getAbsolutePath());
+ }
+
+ /**
+ * Converts an ASCII character representing a hexadecimal
+ * value into its integer equivalent.
+ */
+ private int hexToByte(byte b) {
+ switch (b) {
+ case '0' :
+ return 0;
+ case '1' :
+ return 1;
+ case '2' :
+ return 2;
+ case '3' :
+ return 3;
+ case '4' :
+ return 4;
+ case '5' :
+ return 5;
+ case '6' :
+ return 6;
+ case '7' :
+ return 7;
+ case '8' :
+ return 8;
+ case '9' :
+ return 9;
+ case 'A' :
+ case 'a' :
+ return 10;
+ case 'B' :
+ case 'b' :
+ return 11;
+ case 'C' :
+ case 'c' :
+ return 12;
+ case 'D' :
+ case 'd' :
+ return 13;
+ case 'E' :
+ case 'e' :
+ return 14;
+ case 'F' :
+ case 'f' :
+ return 15;
+ default :
+ throw new IllegalArgumentException("Switch error decoding URL"); //$NON-NLS-1$
+ }
+ }
+
+ private void openLogFile() throws IOException {
+ computeLogFileLocation();
+ try {
+ log = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(logFile.getAbsolutePath(), true), "UTF-8")); //$NON-NLS-1$
+ } catch (IOException e) {
+ logFile = null;
+ throw e;
+ }
+ }
+
+ private BufferedWriter logForStream(OutputStream output) {
+ try {
+ return new BufferedWriter(new OutputStreamWriter(output, "UTF-8")); //$NON-NLS-1$
+ } catch (UnsupportedEncodingException e) {
+ return new BufferedWriter(new OutputStreamWriter(output));
+ }
+ }
+
+ private void closeLogFile() throws IOException {
+ try {
+ if (log != null) {
+ log.flush();
+ log.close();
+ }
+ } finally {
+ log = null;
+ }
+ }
+
+ private void mergeProperties(Properties destination, Properties source) {
+ if (destination == null || source == null)
+ return;
+ for (Enumeration e = source.keys(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ if (key.equals(PROP_CLASSPATH)) {
+ String destinationClasspath = destination.getProperty(PROP_CLASSPATH);
+ String sourceClasspath = source.getProperty(PROP_CLASSPATH);
+ if (destinationClasspath == null)
+ destinationClasspath = sourceClasspath;
+ else
+ destinationClasspath = destinationClasspath + sourceClasspath;
+ destination.put(PROP_CLASSPATH, destinationClasspath);
+ continue;
+ }
+ String value = source.getProperty(key);
+ if (destination.getProperty(key) == null)
+ destination.put(key, value);
+ }
+ }
+
+ private void setupVMProperties() {
+ if (vm != null)
+ System.getProperties().put(PROP_VM, vm);
+ setMultiValueProperty(PROP_VMARGS, vmargs);
+ setMultiValueProperty(PROP_COMMANDS, commands);
+ }
+
+ private void setMultiValueProperty(String property, String[] value) {
+ if (value != null) {
+ StringBuffer result = new StringBuffer(300);
+ for (int i = 0; i < value.length; i++) {
+ if (value[i] != null) {
+ result.append(value[i]);
+ result.append('\n');
+ }
+ }
+ System.getProperties().put(property, result.toString());
+ }
+ }
+
+ /*
+ * NOTE: It is ok here for EclipsePolicy to use 1.4 methods because the methods
+ * that it calls them from don't exist in Foundation so they will never be called. A more
+ * detailed explanation from Tom:
+ *
+ * They will never get called because in a pre 1.4 VM the methods
+ * getPermissions(CodeSource) and implies(ProtectionDomain, Permission) are
+ * undefined on the Policy class which is what EclipsePolicy extends. EclipsePolicy
+ * implements these two methods so it can proxy them to the parent Policy.
+ * But since these methods are not actually defined on Policy in a pre-1.4 VM
+ * nobody will actually call them (unless they casted the policy to EclipsePolicy and
+ * called our methods)
+ */
+ private class EclipsePolicy extends Policy {
+ // The policy that this EclipsePolicy is replacing
+ private Policy policy;
+
+ // The set of URLs to give AllPermissions to; this is the set of bootURLs
+ private URL[] urls;
+
+ // The AllPermissions collection
+ private PermissionCollection allPermissions;
+
+ // The AllPermission permission
+ private Permission allPermission = new AllPermission();
+
+ EclipsePolicy(Policy policy, URL[] urls) {
+ this.policy = policy;
+ this.urls = urls;
+ allPermissions = new PermissionCollection() {
+ private static final long serialVersionUID = 3258131349494708277L;
+
+ // A simple PermissionCollection that only has AllPermission
+ public void add(Permission permission) {
+ }
+
+ public boolean implies(Permission permission) {
+ return true;
+ }
+
+ public Enumeration elements() {
+ return new Enumeration() {
+ int cur = 0;
+
+ public boolean hasMoreElements() {
+ return cur < 1;
+ }
+
+ public Object nextElement() {
+ if (cur == 0) {
+ cur = 1;
+ return allPermission;
+ } else
+ throw new NoSuchElementException();
+ }
+ };
+ }
+ };
+ }
+
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ if (contains(codesource.getLocation()))
+ return allPermissions;
+ return policy == null ? allPermissions : policy.getPermissions(codesource);
+ }
+
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ if (contains(domain.getCodeSource().getLocation()))
+ return allPermissions;
+ return policy == null ? allPermissions : policy.getPermissions(domain);
+ }
+
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ if (contains(domain.getCodeSource().getLocation()))
+ return true;
+ return policy == null ? true : policy.implies(domain, permission);
+ }
+
+ public void refresh() {
+ if (policy != null)
+ policy.refresh();
+ }
+
+ private boolean contains(URL url) {
+ // Check to see if this URL is in our set of URLs to give AllPermissions to.
+ for (int i = 0; i < urls.length; i++) {
+ // We do simple equals test here because we assume the URLs will be the same objects.
+ if (urls[i] == url)
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private class StartupClassLoader extends URLClassLoader {
+
+ public StartupClassLoader(URL[] urls) {
+ super(urls);
+ }
+
+ public StartupClassLoader(URL[] urls, ClassLoader parent) {
+ super(urls, parent);
+ }
+
+ public StartupClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
+ super(urls, parent, factory);
+ }
+
+ protected String findLibrary(String name) {
+ if (extensionPaths == null)
+ return super.findLibrary(name);
+ String libName = System.mapLibraryName(name);
+ for (int i = 0; i < extensionPaths.length; i++) {
+ File libFile = new File(extensionPaths[i], libName);
+ if (libFile.isFile())
+ return libFile.getAbsolutePath();
+ }
+ return super.findLibrary(name);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/WebStartMain.java b/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/WebStartMain.java
new file mode 100644
index 000000000..dfa71c793
--- /dev/null
+++ b/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/WebStartMain.java
@@ -0,0 +1,295 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.equinox.launcher;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+/**
+ * The launcher ot start eclipse using webstart.
+ * To use this launcher, the client must accept to give all security permissions.
+ */
+//The bundles are discovered by finding all the jars on the classpath. Then they are added with their full path to the osgi.bundles list.
+public class WebStartMain extends Main {
+ private static final String PROP_WEBSTART_AUTOMATIC_INSTALLATION = "eclipse.webstart.automaticInstallation"; //$NON-NLS-1$
+ private static final String DEFAULT_OSGI_BUNDLES = "org.eclipse.equinox.common@2:start, org.eclipse.core.runtime@start"; //$NON-NLS-1$
+ private static final String PROP_OSGI_BUNDLES = "osgi.bundles"; //$NON-NLS-1$
+ private static final String PROP_WEBSTART_PRECISE_BUNDLEID = "eclipse.webstart.preciseBundleId"; //$NON-NLS-1$
+
+
+ private String[] allJars = null; //List all the jars that are on the classpath
+ private Map bundleList = null; //Map an entry (the part before the @) from the osgi.bundles list to a list of URLs. Ie: org.eclipse.core.runtime --> file:c:/foo/org.eclipse.core.runtime_3.1.0/..., file:c:/bar/org.eclipse.core.runtime/...
+ private Map bundleStartInfo = null; //Keep track of the start level info for each bundle from the osgi.bundle list.
+
+ private boolean preciseIdExtraction = false; //Flag indicating if the extraction of the id must be done by looking at bundle ids.
+
+ public static void main(String[] args) {
+ System.setSecurityManager(null); //TODO Hack so that when the classloader loading the fwk is created we don't have funny permissions. This should be revisited.
+ int result = new WebStartMain().run(args);
+ System.exit(result);
+ }
+
+ private void setDefaultBundles() {
+ if (System.getProperty(PROP_OSGI_BUNDLES) != null)
+ return;
+ System.getProperties().put(PROP_OSGI_BUNDLES, DEFAULT_OSGI_BUNDLES);
+ }
+
+ protected void basicRun(String[] args) throws Exception {
+ preciseIdExtraction = Boolean.getBoolean(PROP_WEBSTART_PRECISE_BUNDLEID);
+ setDefaultBundles();
+ addOSGiBundle();
+ initializeBundleListStructure();
+ mapURLsToBundleList();
+ //Set the fwk location since the regular lookup would not find it
+ String fwkURL = searchFor(framework, null);
+ System.getProperties().put(PROP_FRAMEWORK, fwkURL);
+ super.basicRun(args);
+ }
+
+ private void addOSGiBundle() {
+ //Add osgi to the bundle list, so we can beneficiate of the infrastructure to find its location. It will be removed from the list later on
+ System.getProperties().put(PROP_OSGI_BUNDLES, System.getProperty(PROP_OSGI_BUNDLES) + ',' + framework);
+ }
+
+ protected URL[] getBootPath(String base) throws IOException {
+ URL[] result = super.getBootPath(base);
+ buildOSGiBundleList();
+ cleanup();
+ return result;
+ }
+
+ /*
+ * Null out all the fields containing data
+ */
+ private void cleanup() {
+ allJars = null;
+ bundleList = null;
+ bundleStartInfo = null;
+ }
+
+ /*
+ * Find the target bundle among all the jars that are on the classpath.
+ * The start parameter is not used in this context
+ */
+ protected String searchFor(final String target, String start) {
+ ArrayList matches = (ArrayList) bundleList.get(target);
+ int numberOfURLs = matches.size();
+ if (numberOfURLs == 1) {
+ return extractInnerURL((String) matches.get(0));
+ }
+ if (numberOfURLs == 0)
+ return null;
+ String urls[] = new String[numberOfURLs];
+ return extractInnerURL(urls[findMax((String[]) matches.toArray(urls))]);
+ }
+
+ /*
+ * Get all the jars available on the webstart classpath
+ */
+ private String[] getAllJars() {
+ if (allJars != null)
+ return allJars;
+
+ ArrayList collector = new ArrayList();
+ try {
+ Enumeration resources = WebStartMain.class.getClassLoader().getResources(JarFile.MANIFEST_NAME);
+ while (resources.hasMoreElements()) {
+ collector.add(((URL) resources.nextElement()).toExternalForm());
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ allJars = new String[collector.size()];
+ collector.toArray(allJars);
+ if (debug)
+ printArray("Jars found on the webstart path:\n", allJars); //$NON-//$NON-NLS-1$
+
+ return allJars;
+ }
+
+ /*
+ * Extract the inner URL from a string representing a JAR url string.
+ */
+ private String extractInnerURL(String url) {
+ if (url.startsWith(JAR_SCHEME)) {
+ url = url.substring(url.indexOf(JAR_SCHEME) + 4);
+ }
+ int lastBang = url.lastIndexOf('!');
+ if (lastBang != -1) {
+ url = url.substring(0, lastBang);
+ }
+ return decode(url);
+ }
+
+ private void printArray(String header, String[] values) {
+ System.err.println(header);
+ for (int i = 0; i < values.length; i++) {
+ System.err.println("\t" + values[i]); //$NON-NLS-1$
+ }
+ }
+
+
+ /*
+ * Initialize the data structure corresponding to the osgi.bundles list
+ */
+ private void initializeBundleListStructure() {
+ final char STARTLEVEL_SEPARATOR = '@';
+
+ //In webstart the bundles list can only contain bundle names with or without a version.
+ String prop = System.getProperty(PROP_OSGI_BUNDLES);
+ if (prop == null || prop.trim().equals("")) { //$NON-NLS-1$
+ bundleList = new HashMap(0);
+ return;
+ }
+
+ bundleList = new HashMap(10);
+ bundleStartInfo = new HashMap(10);
+ StringTokenizer tokens = new StringTokenizer(prop, ","); //$NON-NLS-1$
+ while (tokens.hasMoreTokens()) {
+ String token = tokens.nextToken().trim();
+ String bundleId = token;
+ if (token.equals("")) //$NON-NLS-1$
+ continue;
+ int startLevelSeparator;
+ if ((startLevelSeparator = token.lastIndexOf(STARTLEVEL_SEPARATOR)) != -1) {
+ bundleId = token.substring(0, startLevelSeparator);
+ bundleStartInfo.put(bundleId, token.substring(startLevelSeparator));
+ }
+ bundleList.put(bundleId, new ArrayList(1)); // put a list with one element as it is likely that the element will be present
+ }
+
+ }
+
+ /*
+ * Associate urls from the list of jars with a bundle from the bundle list
+ */
+ private void mapURLsToBundleList() {
+ String[] allJars = getAllJars();
+ for (int i = 0; i < allJars.length; i++) {
+ String bundleId = extractBundleId(allJars[i]);
+ if (bundleId == null)
+ continue;
+ ArrayList bundleURLs = (ArrayList) bundleList.get(bundleId);
+ if (bundleURLs == null) {
+ int versionIdPosition = bundleId.lastIndexOf('_');
+ if (versionIdPosition == -1)
+ continue;
+ bundleURLs = (ArrayList) bundleList.get(bundleId.substring(0, versionIdPosition));
+ if (bundleURLs == null)
+ continue;
+ }
+ bundleURLs.add(allJars[i]);
+ allJars[i] = null; //Remove the entry from the list
+ }
+ }
+
+ /*
+ * return a string of the form <bundle>_<version>
+ */
+ private String extractBundleId(String url) {
+ if (preciseIdExtraction)
+ return extractBundleIdFromManifest(url);
+ else
+ return extractBundleIdFromBundleURL(url);
+ }
+
+ private String extractBundleIdFromManifest(String url) {
+ final String BUNDLE_SYMBOLICNAME = "Bundle-SymbolicName"; //$NON-NLS-1$
+ final String BUNDLE_VERSION = "Bundle-Version"; //$NON-NLS-1$
+
+ Manifest mf;
+ try {
+ mf = new Manifest(new URL(url).openStream());
+ String symbolicNameString = mf.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME);
+ if (symbolicNameString==null)
+ return null;
+
+ String bundleVersion = mf.getMainAttributes().getValue(BUNDLE_VERSION);
+ if (bundleVersion == null)
+ bundleVersion = ""; //$NON-NLS-1$
+ else
+ bundleVersion = '_' + bundleVersion;
+
+ int pos = symbolicNameString.lastIndexOf(';');
+ if (pos != -1)
+ return symbolicNameString.substring(0, pos) + bundleVersion;
+ return symbolicNameString + bundleVersion;
+ } catch (MalformedURLException e) {
+ //Ignore
+ } catch (IOException e) {
+ //Ignore
+ }
+ return null;
+
+ }
+
+ private String extractBundleIdFromBundleURL(String url) {
+ int lastBang = url.lastIndexOf('!');
+ if (lastBang == -1)
+ return null;
+ boolean jarSuffix = url.regionMatches(true, lastBang - 4, ".jar", 0, 4); //$NON-NLS-1$
+ int bundleIdStart = url.lastIndexOf('/', lastBang);
+ return url.substring(bundleIdStart + 3, lastBang - (jarSuffix ? 4 : 0)); // + 3 because URLs from webstart have a funny prefix
+ }
+
+ private void buildOSGiBundleList() {
+ //Remove the framework from the bundle list because it does not need to be installed. See addOSGiBundle
+ bundleList.remove(framework);
+
+ String[] jarsOnClasspath = getAllJars();
+ StringBuffer finalBundleList = new StringBuffer(jarsOnClasspath.length * 25);
+
+ //Add the bundles from the bundle list.
+ Collection allSelectedBundles = bundleList.entrySet();
+ for (Iterator iter = allSelectedBundles.iterator(); iter.hasNext();) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ ArrayList matches = (ArrayList) entry.getValue();
+ int numberOfURLs = matches.size();
+
+ //Get the start info
+ String startInfo = (String) bundleStartInfo.get(entry.getKey());
+ if (startInfo == null)
+ startInfo = ""; //$NON-NLS-1$
+
+ if (numberOfURLs == 1) {
+ finalBundleList.append(REFERENCE_SCHEME).append(extractInnerURL((String) matches.get(0))).append(startInfo).append(',');
+ continue;
+ }
+ if (numberOfURLs == 0)
+ continue;
+ String urls[] = new String[numberOfURLs];
+ int found = findMax((String[]) matches.toArray(urls));
+ for (int i = 0; i < urls.length; i++) {
+ if (i != found)
+ continue;
+ finalBundleList.append(REFERENCE_SCHEME).append(extractInnerURL((String) urls[found])).append(startInfo).append(',');
+ }
+ }
+
+ //Add all the other bundles if required - the common case is to add those
+ if (! Boolean.FALSE.toString().equalsIgnoreCase(System.getProperties().getProperty(PROP_WEBSTART_AUTOMATIC_INSTALLATION))) {
+ for (int i = 0; i < jarsOnClasspath.length; i++) {
+ if (jarsOnClasspath[i] != null)
+ finalBundleList.append(REFERENCE_SCHEME).append(extractInnerURL(jarsOnClasspath[i])).append(',');
+ }
+ }
+
+ System.getProperties().put(PROP_OSGI_BUNDLES, finalBundleList.toString());
+ if (debug)
+ log(finalBundleList.toString());
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/package.html b/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/package.html
new file mode 100644
index 000000000..ca2e7ce0c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/package.html
@@ -0,0 +1,18 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="IBM">
+ <meta name="GENERATOR" content="Mozilla/4.75 [en] (Windows NT 5.0; U) [Netscape]">
+ <title>Package-level Javadoc</title>
+</head>
+<body>
+Launches the Eclipse Platform.
+<h2>
+Package Specification</h2>
+
+This package is the main entry point to the Eclipse Platform. To start the platform invoke the Main
+class in this package.
+<br>&nbsp;
+</body>
+</html>

Back to the top