blob: 5c5370b4409b8135ddc5ee6eba2cd9f6d4fba8bd [file] [log] [blame]
/*******************************************************************************
* 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.core.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 static final String JAVA_6 = "1.6";
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);
if (checkVersion(System.getProperty("java.version"), JAVA_6))
preciseIdExtraction = true;
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++) {
Object[] bundleInfo = extractBundleId(allJars[i]);
if (bundleInfo == null)
continue;
String bsn = (String) bundleInfo[0];
if (bsn == null)
continue;
String version = (String) bundleInfo[1];
ArrayList bundleURLs = null;
if (bsn != null && version != null) {
bundleURLs = (ArrayList) bundleList.get(bsn+ '_' + version);
}
if (bundleURLs == null) {
bundleURLs = (ArrayList) bundleList.get(bsn);
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 Object[] extractBundleId(String url) {
if (preciseIdExtraction)
return extractBundleIdFromManifest(url);
else
return extractBundleIdFromBundleURL(url);
}
private Object[] 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);
int pos = symbolicNameString.lastIndexOf(';');
if (pos != -1)
return new Object[] {symbolicNameString.substring(0, pos), bundleVersion};
return new Object[] {symbolicNameString, bundleVersion};
} catch (MalformedURLException e) {
e.printStackTrace();
//Ignore
} catch (IOException e) {e.printStackTrace();
//Ignore
}
return null;
}
private Object[] extractBundleIdFromBundleURL(String url) {
//First extract the relevant part of the 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);
String fileName = url.substring(bundleIdStart + 3, lastBang - (jarSuffix ? 4 : 0)); // + 3 because URLs from webstart have a funny prefix
//Separate the version from the bsn
String bsn = null;
String version = null;
int underScore = fileName.indexOf('_');
while (underScore >= 0) {
bsn = fileName.substring(0, underScore);
version = fileName.substring(underScore + 1);
if (! isValidVersion(version)) {
underScore = fileName.indexOf('_', underScore + 1);
} else {
break;
}
}
return new Object[] {bsn, version};
}
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());
}
private boolean isValidVersion(String version) {
int major = 0;
int minor = 0;
int micro = 0;
String qualifier = ""; //$NON-NLS-1$
final String SEPARATOR = ".";
try {
StringTokenizer st = new StringTokenizer(version, SEPARATOR, true);
major = Integer.parseInt(st.nextToken());
if (st.hasMoreTokens()) {
st.nextToken(); // consume delimiter
minor = Integer.parseInt(st.nextToken());
if (st.hasMoreTokens()) {
st.nextToken(); // consume delimiter
micro = Integer.parseInt(st.nextToken());
if (st.hasMoreTokens()) {
st.nextToken(); // consume delimiter
qualifier = st.nextToken();
if (st.hasMoreTokens()) {
return false;
}
}
}
}
}
catch (NoSuchElementException e) {
return false;
}
catch (NumberFormatException e) {
return false;
}
return isValidVersionSegment(major, minor, micro, qualifier);
}
private boolean isValidVersionSegment(int major, int minor, int micro, String qualifier) {
if (major < 0) {
return false;
}
if (minor < 0) {
return false;
}
if (micro < 0) {
}
int length = qualifier.length();
for (int i = 0; i < length; i++) {
if ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-".indexOf(qualifier.charAt(i)) == -1) { //$NON-NLS-1$
return false;
}
}
return true;
}
}