blob: 703c18e747ab1f63c7025437b26195578d609eed [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.launcher;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.*;
import java.util.*;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipFile;
/**
* The launcher to start eclipse using webstart. To use this launcher, the client
* must accept to give all security permissions.
* <p>
* <b>Note:</b> This class should not be referenced programmatically by
* other Java code. This class exists only for the purpose of launching Eclipse
* using Java webstart. To launch Eclipse programmatically, use
* org.eclipse.core.runtime.adaptor.EclipseStarter. The fields and methods
* on this class are not API.
*
* @noextend This class is not intended to be subclassed by clients.
* @noinstantiate This class is not intended to be instantiated by clients.
* @noreference This class is not intended to be referenced by clients.
*
* @deprecated Java WebStart is removed in Java 11.
*
* This API is planned to be deleted, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=544262
*
*/
//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.
@Deprecated
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_CHECK_CONFIG = "osgi.checkConfiguration"; //$NON-NLS-1$
private Map<String, List<BundleInfo>> allBundles = null; // Map of all the bundles found on the classpath. Id -> ArrayList of BundleInfo
private List<BundleInfo> bundleList = null; //The list of bundles found on the osgi.bundle list
protected class BundleInfo {
String bsn;
String version;
String startData;
String location;
}
/**
* @noreference This method is not intended to be referenced by clients.
*/
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);
if (!Boolean.getBoolean(PROP_NOSHUTDOWN))
System.exit(result);
}
private void setDefaultBundles() {
if (System.getProperty(PROP_OSGI_BUNDLES) != null)
return;
System.getProperties().put(PROP_OSGI_BUNDLES, DEFAULT_OSGI_BUNDLES);
}
@Override
protected void basicRun(String[] args) throws Exception {
setDefaultBundles();
initializeBundleListStructure();
discoverBundles();
//Set the fwk location since the regular lookup would not find it
String fwkURL = searchFor(framework, null);
if (fwkURL == null) {
//MESSAGE CAN"T FIND THE FWK
}
allBundles.remove(framework);
System.getProperties().put(PROP_FRAMEWORK, fwkURL);
super.basicRun(args);
}
@Override
protected void beforeFwkInvocation() {
// set the check config option so we pick up modified bundle jars (bug 152825)
if (System.getProperty(PROP_CHECK_CONFIG) == null)
System.getProperties().put(PROP_CHECK_CONFIG, "true"); //$NON-NLS-1$
buildOSGiBundleList();
cleanup();
}
/*
* Null out all the fields containing data
*/
private void cleanup() {
allBundles = null;
bundleList = null;
}
/*
* Find the target bundle among all the bundles that are on the classpath.
* The start parameter is not used in this context
*/
@Override
protected String searchFor(final String target, String start) {
List<BundleInfo> matches = allBundles.get(target);
if (matches == null)
return null;
int numberOfMatches = matches.size();
if (numberOfMatches == 1) {
return matches.get(0).location;
}
if (numberOfMatches == 0)
return null;
String[] versions = new String[numberOfMatches];
int highest = 0;
for (int i = 0; i < versions.length; i++) {
versions[i] = matches.get(i).version;
}
highest = findMax(null, versions);
return matches.get(highest).location;
}
private BundleInfo findBundle(final String target, String version, boolean removeMatch) {
List<BundleInfo> matches = allBundles.get(target);
int numberOfMatches = matches != null ? matches.size() : 0;
if (numberOfMatches == 1) {
//TODO Need to check the version
return removeMatch ? matches.remove(0) : matches.get(0);
}
if (numberOfMatches == 0)
return null;
if (version != null) {
for (Iterator<BundleInfo> iterator = matches.iterator(); iterator.hasNext();) {
BundleInfo bi = iterator.next();
if (bi.version.equals(version)) {
if (removeMatch)
iterator.remove();
return bi;
}
}
//TODO Need to log the fact that we could not find the version mentioned
return null;
}
String[] versions = new String[numberOfMatches];
int highest = 0;
for (int i = 0; i < versions.length; i++) {
versions[i] = matches.get(i).version;
}
highest = findMax(null, versions);
return removeMatch ? matches.remove(highest) : matches.get(highest);
}
/*
* Get all the bundles available on the webstart classpath
*/
private void discoverBundles() {
allBundles = new HashMap<>();
try {
Enumeration<URL> resources = WebStartMain.class.getClassLoader().getResources(JarFile.MANIFEST_NAME);
while (resources.hasMoreElements()) {
BundleInfo found = getBundleInfo(resources.nextElement());
if (found == null)
continue;
List<BundleInfo> matching = allBundles.get(found.bsn);
if (matching == null) {
matching = new ArrayList<>(1);
allBundles.put(found.bsn, matching);
}
matching.add(found);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String extractInnerURL(URL url) {
try {
URLConnection connection = null;
try {
connection = url.openConnection();
if (connection instanceof JarURLConnection) {
JarFile jarFile = ((JarURLConnection) connection).getJarFile();
String name = jarFile.getName();
// Some VMs may not return a jar name as a security precaution
if (name == null || name.length() == 0)
name = getJarNameByReflection(jarFile);
if (name != null && name.length() > 0)
return "file:" + name; //$NON-NLS-1$
}
} finally {
if (connection != null)
connection.getInputStream().close();
}
} catch (IOException e) {
//Ignore and return the external form
}
return url.toExternalForm();
}
/*
* Get a value of the ZipFile.name field using reflection.
* For this to succeed, we need the "suppressAccessChecks" permission.
*/
private String getJarNameByReflection(JarFile jarFile) {
if (jarFile == null)
return null;
Field nameField = null;
try {
nameField = ZipFile.class.getDeclaredField("name"); //$NON-NLS-1$
} catch (NoSuchFieldException e1) {
try {
nameField = ZipFile.class.getDeclaredField("fileName"); //$NON-NLS-1$
} catch (NoSuchFieldException e) {
//ignore
}
}
if (nameField == null || Modifier.isStatic(nameField.getModifiers()) || nameField.getType() != String.class)
return null;
try {
nameField.setAccessible(true);
return (String) nameField.get(jarFile);
} catch (SecurityException e) {
// Don't have permissions, ignore
} catch (IllegalArgumentException e) {
// Shouldn't happen
} catch (IllegalAccessException e) {
// Shouldn't happen
}
return null;
}
/*
* Construct bundle info objects from items found on 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 ArrayList<>(0);
return;
}
bundleList = new ArrayList<>(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;
BundleInfo toAdd = new BundleInfo();
toAdd.bsn = bundleId;
if ((startLevelSeparator = token.lastIndexOf(STARTLEVEL_SEPARATOR)) != -1) {
toAdd.bsn = token.substring(0, startLevelSeparator);
toAdd.startData = token.substring(startLevelSeparator);
//Note that here we don't try to parse the start attribute since this info is then used to recompose the value for osgi.bundles
}
bundleList.add(toAdd);
}
}
private BundleInfo getBundleInfo(URL manifestURL) {
final String BUNDLE_SYMBOLICNAME = "Bundle-SymbolicName"; //$NON-NLS-1$
final String BUNDLE_VERSION = "Bundle-Version"; //$NON-NLS-1$
final String DEFAULT_VERSION = "0.0.0"; //$NON-NLS-1$
Manifest mf;
try {
mf = new Manifest(manifestURL.openStream());
String symbolicNameString = mf.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME);
if (symbolicNameString == null)
return null;
BundleInfo result = new BundleInfo();
String version = mf.getMainAttributes().getValue(BUNDLE_VERSION);
result.version = (version != null) ? version : DEFAULT_VERSION;
result.location = extractInnerURL(manifestURL);
int pos = symbolicNameString.lastIndexOf(';');
if (pos != -1) {
result.bsn = symbolicNameString.substring(0, pos);
return result;
}
result.bsn = symbolicNameString;
return result;
} catch (IOException e) {
if (debug)
e.printStackTrace();
}
return null;
}
//Build the osgi bundle list. The allbundles data structure is changed during the process.
private void buildOSGiBundleList() {
StringBuilder finalBundleList = new StringBuilder(allBundles.size() * 30);
//First go through all the bundles of the bundle
for (Iterator<BundleInfo> iterator = bundleList.iterator(); iterator.hasNext();) {
BundleInfo searched = iterator.next();
BundleInfo found = findBundle(searched.bsn, searched.version, true);
if (found != null)
finalBundleList.append(REFERENCE_SCHEME).append(found.location).append(searched.startData).append(',');
}
if (!Boolean.FALSE.toString().equalsIgnoreCase(System.getProperties().getProperty(PROP_WEBSTART_AUTOMATIC_INSTALLATION))) {
for (Iterator<List<BundleInfo>> iterator = allBundles.values().iterator(); iterator.hasNext();) {
List<BundleInfo> toAdd = iterator.next();
for (Iterator<BundleInfo> iterator2 = toAdd.iterator(); iterator2.hasNext();) {
BundleInfo bi = iterator2.next();
finalBundleList.append(REFERENCE_SCHEME).append(bi.location).append(',');
}
}
}
System.getProperties().put(PROP_OSGI_BUNDLES, finalBundleList.toString());
}
}