blob: f3f695309036ac0a45bc588f47e012d95939f5d1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 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
* Michael Allman - Bug 211648, Bug 156343 - Standard VM not supported on MacOS
* Thomas Schindl - Bug 399798, StandardVMType should allow to contribute default source and Javadoc locations for ext libraries
* Mikhail Kalkov - Bug 414285, On systems with large RAM, evaluateSystemProperties and generateLibraryInfo fail for 64-bit JREs
*******************************************************************************/
package org.eclipse.jdt.internal.launching;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Stream;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.Launch;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.AbstractVMInstallType;
import org.eclipse.jdt.launching.ILibraryLocationResolver;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.LibraryLocation;
import org.eclipse.osgi.util.NLS;
/**
* A VM install type for VMs the conform to the standard
* JDK installation layout.
*/
public class StandardVMType extends AbstractVMInstallType {
/**
* Constants for common {@link String}s
* @since 3.7
*/
private static final String RT_JAR = "rt.jar"; //$NON-NLS-1$
private static final String SRC = "src"; //$NON-NLS-1$
private static final String SRC_ZIP = "src.zip"; //$NON-NLS-1$
private static final String SRC_JAR = "src.jar"; //$NON-NLS-1$
private static final String JRE = "jre"; //$NON-NLS-1$
private static final String LIB = "lib"; //$NON-NLS-1$
private static final String BAR = "|"; //$NON-NLS-1$
private static final String RELEASE_FILE = "release"; //$NON-NLS-1$
private static final String JAVA_VERSION = "JAVA_VERSION"; //$NON-NLS-1$
private static final String JRT_FS_JAR = "jrt-fs.jar"; //$NON-NLS-1$
public static final String ID_STANDARD_VM_TYPE = "org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType"; //$NON-NLS-1$
/**
* The minimal -Xmx size for launching a JVM. <br>
* <b>Note:</b> Must be omitted for Standard11xVM! <br>
* <b>Note:</b> Must be at least -Xmx16m for JRockit, see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=433455">bug 433455</a>.
*
* @since 3.7.100
*/
public static final String MIN_VM_SIZE = "-Xmx16m"; //$NON-NLS-1$
/**
* Name filter for files ending in .jar or .zip
*
* @since 3.7.0
*/
private static FilenameFilter fgArchiveFilter = new FilenameFilter() {
@Override
public boolean accept(File arg0, String arg1) {
return arg1.endsWith(".zip") || arg1.endsWith(".jar"); //$NON-NLS-1$//$NON-NLS-2$
}
};
/**
* The root path for the attached source
*/
private String fDefaultRootPath = ""; //$NON-NLS-1$
/**
* Map of the install path for which we were unable to generate
* the library info during this session.
*/
private static Map<String, LibraryInfo> fgFailedInstallPath = new HashMap<>();
/**
* Cache for default library locations. See {@link #getDefaultLibraryLocations(File)}
* <br><br>
* Map&lt;{@link String}, {@link LibraryLocation}&gt;
*
* @since 3.7
*/
private static Map<String, List<LibraryLocation>> fgDefaultLibLocs = new HashMap<>();
/**
* The list of locations in which to look for the java executable in candidate
* VM install locations, relative to the VM install location.
*/
private static final String[] fgCandidateJavaFiles = {"javaw", "javaw.exe", "java", "java.exe", "j9w", "j9w.exe", "j9", "j9.exe"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
private static final String[] fgCandidateJavaLocations = {"bin" + File.separatorChar, JRE + File.separatorChar + "bin" + File.separatorChar}; //$NON-NLS-1$ //$NON-NLS-2$
private static ILibraryLocationResolver[] fgLibraryLocationResolvers = null;
/**
* Starting in the specified VM install location, attempt to find the 'java' executable
* file. If found, return the corresponding <code>File</code> object, otherwise return
* <code>null</code>.
* @param vmInstallLocation the {@link File} location to look in
* @return the {@link File} for the Java executable or <code>null</code>
*/
public static File findJavaExecutable(File vmInstallLocation) {
// Try each candidate in order. The first one found wins. Thus, the order
// of fgCandidateJavaLocations and fgCandidateJavaFiles is significant.
for (int i = 0; i < fgCandidateJavaFiles.length; i++) {
for (int j = 0; j < fgCandidateJavaLocations.length; j++) {
File javaFile = new File(vmInstallLocation, fgCandidateJavaLocations[j] + fgCandidateJavaFiles[i]);
if (javaFile.isFile()) {
return javaFile;
}
}
}
return null;
}
/**
* Returns the listing of {@link ILibraryLocationResolver}s
*
* @return the known list of {@link ILibraryLocationResolver}s
* @since 3.7.0
*/
private static ILibraryLocationResolver[] getLibraryLocationResolvers() {
if( fgLibraryLocationResolvers == null ) {
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(LaunchingPlugin.ID_PLUGIN, JavaRuntime.EXTENSION_POINT_LIBRARY_LOCATION_RESOLVERS);
IConfigurationElement[] configs = extensionPoint.getConfigurationElements();
// The "libraryLocationResolvers" extension point doesn't have any conflict resolution.
// Sorting by namespace at least makes makes the order predictable.
Arrays.sort(configs, new Comparator<IConfigurationElement>() {
@Override
public int compare(IConfigurationElement e1, IConfigurationElement e2) {
return e1.getNamespaceIdentifier().compareTo(e2.getNamespaceIdentifier());
}
});
List<ILibraryLocationResolver> resolvers = new ArrayList<>(configs.length);
for( int i = 0; i < configs.length; i++ ) {
IConfigurationElement e = configs[i];
try {
resolvers.add((ILibraryLocationResolver) e.createExecutableExtension("class")); //$NON-NLS-1$
}
catch (CoreException e1) {
LaunchingPlugin.log(e1.getStatus());
}
}
fgLibraryLocationResolvers = resolvers.toArray(new ILibraryLocationResolver[0]);
}
return fgLibraryLocationResolvers;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.IVMInstallType#getName()
*/
@Override
public String getName() {
return LaunchingMessages.StandardVMType_Standard_VM_3;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.AbstractVMInstallType#doCreateVMInstall(java.lang.String)
*/
@Override
protected IVMInstall doCreateVMInstall(String id) {
return new StandardVM(this, id);
}
/**
* Return library information corresponding to the specified install
* location. If the information does not exist, create it using the given Java
* executable.
* @param javaHome the Java home folder
* @param javaExecutable the Java executable file
* @return the {@link LibraryInfo} for the home and executable path or an empty object, never <code>null</code>
*/
protected synchronized LibraryInfo getLibraryInfo(File javaHome, File javaExecutable) {
String installPath = javaHome.getAbsolutePath();
LibraryInfo info = LaunchingPlugin.getLibraryInfo(installPath);
if (info == null || LaunchingPlugin.timeStampChanged(installPath)) {
info = fgFailedInstallPath.get(installPath);
if (info == null) {
String version = readReleaseVersion(javaHome);
if (JavaCore.compareJavaVersions(version, JavaCore.VERSION_1_8) > 0) {
info = new LibraryInfo(version, new String[0], new String[0], new String[0]);
LaunchingPlugin.setLibraryInfo(installPath, info);
} else {
info = generateLibraryInfo(javaHome, javaExecutable);
if (info == null) {
// Bug 536943: try again in case we have a timing problem with process execution and output retrieval
info = generateLibraryInfo(javaHome, javaExecutable);
}
if (info == null) {
info = getDefaultLibraryInfo(javaHome);
fgFailedInstallPath.put(installPath, info);
} else {
// only persist if we were able to generate information - see bug 70011
LaunchingPlugin.setLibraryInfo(installPath, info);
}
}
}
}
return info;
}
/**
* Return <code>true</code> if the appropriate system libraries can be found for the
* specified java executable, <code>false</code> otherwise.
* @param javaHome the Java home folder
* @param javaExecutable the Java executable file
* @return <code>true</code> if the default system libraries can be detected for the given install location
* <code>false</code> otherwise
*/
protected boolean canDetectDefaultSystemLibraries(File javaHome, File javaExecutable) {
LibraryLocation[] locations = getDefaultLibraryLocations(javaHome);
String version = getVMVersion(javaHome, javaExecutable);
return locations.length > 0 && !version.startsWith("1.1"); //$NON-NLS-1$
}
/**
* Returns the version of the VM at the given location, with the given
* executable.
*
* @param javaHome the Java home folder
* @param javaExecutable the Java executable file
* @return String
*/
protected String getVMVersion(File javaHome, File javaExecutable) {
LibraryInfo info = getLibraryInfo(javaHome, javaExecutable);
return info.getVersion();
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.IVMInstallType#detectInstallLocation()
*/
@Override
public File detectInstallLocation() {
// We want a Mac OSX VM install so don't process the install location for this type
if(Platform.OS_MACOSX.equals(Platform.getOS())) {
return null;
}
return getJavaHomeLocation();
}
/**
* Returns the VM install location using the <code>java.home</code> system
* property or <code>null</code>.
*
* @return the install location of this type of VM based off of the
* <code>java.home</code> system property, or <code>null</code> if not found
*
* @since 3.7
*/
protected File getJavaHomeLocation() {
// Retrieve the 'java.home' system property. If that directory doesn't exist,
// return null.
File javaHome;
try {
javaHome= new File (System.getProperty("java.home")).getCanonicalFile(); //$NON-NLS-1$
} catch (IOException e) {
LaunchingPlugin.log(e);
return null;
}
if (!javaHome.exists()) {
return null;
}
// Find the 'java' executable file under the java home directory. If it can't be
// found, return null.
File javaExecutable = findJavaExecutable(javaHome);
if (javaExecutable == null) {
return null;
}
// If the reported java home directory terminates with 'jre', first see if
// the parent directory contains the required libraries
boolean foundLibraries = false;
if (javaHome.getName().equalsIgnoreCase(JRE)) {
File parent= new File(javaHome.getParent());
if (canDetectDefaultSystemLibraries(parent, javaExecutable)) {
javaHome = parent;
foundLibraries = true;
}
}
// If we haven't already found the libraries, look in the reported java home dir
if (!foundLibraries) {
if (!canDetectDefaultSystemLibraries(javaHome, javaExecutable)) {
return null;
}
}
return javaHome;
}
/**
* Return an <code>IPath</code> corresponding to the single library file containing the
* standard Java classes for most VMs version 1.2 and above.
*
* @param javaHome the Java home folder
* @return the {@link IPath} to the <code>rt.jar</code> file
*/
protected IPath getDefaultSystemLibrary(File javaHome) {
IPath jreLibPath= new Path(javaHome.getPath()).append(LIB).append(RT_JAR);
if (jreLibPath.toFile().isFile()) {
return jreLibPath;
}
return new Path(javaHome.getPath()).append(JRE).append(LIB).append(RT_JAR);
}
/**
* Returns a path to the source attachment for the given library, or
* an empty path if none.
*
* @param libLocation the {@link File} location of the library to find the source for
* @return a path to the source attachment for the given library, or
* an empty path if none
*/
protected IPath getDefaultSystemLibrarySource(File libLocation) {
File parent= libLocation.getParentFile();
while (parent != null) {
File parentsrc= new File(parent, SRC_JAR);
if (parentsrc.isFile()) {
setDefaultRootPath(SRC);
return new Path(parentsrc.getPath());
}
parentsrc= new File(parent, SRC_ZIP);
if (parentsrc.isFile()) {
setDefaultRootPath(""); //$NON-NLS-1$
return new Path(parentsrc.getPath());
}
parent = parent.getParentFile();
}
// if we didn't find any of the normal source files, look for J9 source
IPath result = checkForJ9LibrarySource(libLocation);
if (result != null) {
return result;
}
// check for <lib>-src.jar pattern
IPath libName = new Path(libLocation.getName());
String extension = libName.getFileExtension();
String prefix = libName.removeFileExtension().lastSegment();
if (extension != null) {
IPath srcPath = new Path(libLocation.getPath());
srcPath = srcPath.removeLastSegments(1);
StringBuilder buf = new StringBuilder();
buf.append(prefix);
buf.append("-src."); //$NON-NLS-1$
buf.append(extension);
srcPath = srcPath.append(buf.toString());
if (srcPath.toFile().exists()) {
return srcPath;
}
}
setDefaultRootPath(""); //$NON-NLS-1$
return Path.EMPTY;
}
// J9 has a known/fixed structure for its libraries and source locations. Here just
// look for the source associated with each lib.
private IPath checkForJ9LibrarySource(File libLocation) {
File parent= libLocation.getParentFile();
String name = libLocation.getName();
if (name.equalsIgnoreCase("classes.zip")) { //$NON-NLS-1$
File source = new File(parent, "source/source.zip"); //$NON-NLS-1$
return source.isFile() ? new Path(source.getPath()) : Path.EMPTY;
}
if (name.equalsIgnoreCase("locale.zip")) { //$NON-NLS-1$
File source = new File(parent, "source/locale-src.zip"); //$NON-NLS-1$
return source.isFile() ? new Path(source.getPath()) : Path.EMPTY;
}
if (name.equalsIgnoreCase("charconv.zip")) { //$NON-NLS-1$
File source = new File(parent, "charconv-src.zip"); //$NON-NLS-1$
return source.isFile() ? new Path(source.getPath()) : Path.EMPTY;
}
return null;
}
/**
* Returns the package root path
*
* @return the package root path
*/
protected IPath getDefaultPackageRootPath() {
return new Path(getDefaultRootPath());
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.IVMInstallType#getDefaultLibraryLocations(java.io.File)
*/
@Override
public LibraryLocation[] getDefaultLibraryLocations(File installLocation) {
//NOTE: We do not add libraries from the "endorsed" directory explicitly, as
//the bootpath contains these entries already (if they exist).
// Determine the java executable that corresponds to the specified install location
// and use this to generate library information. If no java executable was found,
// the 'standard' libraries will be returned.
List<LibraryLocation> allLibs = fgDefaultLibLocs.get(installLocation.getAbsolutePath());
if(allLibs == null) {
File javaExecutable = findJavaExecutable(installLocation);
LibraryInfo libInfo;
if (javaExecutable == null) {
libInfo = getDefaultLibraryInfo(installLocation);
} else {
libInfo = getLibraryInfo(installLocation, javaExecutable);
}
// Add all endorsed libraries - they are first, as they replace
allLibs = new ArrayList<>(gatherAllLibraries(libInfo.getEndorsedDirs()));
URL url = getDefaultJavadocLocation(installLocation);
if (libInfo.getBootpath().length == 0) {
// TODO: Bug 489207: Temporary workaround for Jigsaw-previews that don't declare a bootpath.
// JDT Core currently requires a non-empty library path, so let's give it jrt-fs.jar as a stand-in for now.
// Code referencing org.eclipse.jdt.internal.compiler.util.JimageUtil.JRT_FS_JAR looks for this file.
IPath sourceRootPath = Path.EMPTY;
// src zip moved to lib folder from JDK 9 EA Build 151
IPath path = new Path(installLocation.getAbsolutePath()).append(LIB).append(SRC_ZIP);
File lib = path.toFile();
if (lib.exists() && lib.isFile()) {
sourceRootPath = getDefaultSystemLibrarySource(lib); // To attach source if available
} else {
path = new Path(installLocation.getAbsolutePath()).append(SRC_ZIP);
lib = path.toFile();
if (lib.exists() && lib.isFile()) {
sourceRootPath = getDefaultSystemLibrarySource(lib); // To attach source if available
}
}
IPath pathName = new Path(installLocation.getAbsolutePath()).append(LIB).append(JRT_FS_JAR);
// From Java 9 149 version, we see that jrt-fs.jar is moved to lib directory so we need to look at both places
File jrtfsJar = pathName.toFile();
if (!jrtfsJar.exists()) {
pathName = new Path(installLocation.getAbsolutePath()).append(JRT_FS_JAR);
}
LibraryLocation libraryLocation = new LibraryLocation(pathName,
sourceRootPath, getDefaultPackageRootPath(),
getDefaultJavadocLocation(installLocation));
allLibs.add(libraryLocation);
}
// next is the boot path libraries
String[] bootpath = libInfo.getBootpath();
List<LibraryLocation> boot = new ArrayList<>(bootpath.length);
for (int i = 0; i < bootpath.length; i++) {
IPath path = new Path(bootpath[i]);
File lib = path.toFile();
if (lib.exists() && lib.isFile()) {
LibraryLocation libraryLocation = new LibraryLocation(path,
getDefaultSystemLibrarySource(lib),
getDefaultPackageRootPath(),
url);
boot.add(libraryLocation);
}
}
allLibs.addAll(boot);
// Add all extension libraries
allLibs.addAll(gatherAllLibraries(libInfo.getExtensionDirs()));
//remove duplicates
HashSet<String> set = new HashSet<>();
LibraryLocation lib = null;
for(ListIterator<LibraryLocation> liter = allLibs.listIterator(); liter.hasNext();) {
lib = liter.next();
IPath systemLibraryPath = lib.getSystemLibraryPath();
String device = systemLibraryPath.getDevice();
if (device != null) {
// @see Bug 197866 - Installed JRE Wizard creates duplicate system libraries when drive letter is lower case
systemLibraryPath = systemLibraryPath.setDevice(device.toUpperCase());
}
if(!set.add(systemLibraryPath.toOSString())) {
//did not add it, duplicate
liter.remove();
}
}
fgDefaultLibLocs.put(installLocation.getAbsolutePath(), allLibs);
}
return allLibs.toArray(new LibraryLocation[allLibs.size()]);
}
/**
* Returns default library information for the given install location.
*
* @param installLocation the VM install location
* @return LibraryInfo
*/
protected LibraryInfo getDefaultLibraryInfo(File installLocation) {
IPath rtjar = getDefaultSystemLibrary(installLocation);
File extDir = getDefaultExtensionDirectory(installLocation);
File endDir = getDefaultEndorsedDirectory(installLocation);
String[] dirs = null;
if (extDir == null) {
dirs = new String[0];
} else {
dirs = new String[] {extDir.getAbsolutePath()};
}
String[] endDirs = null;
if (endDir == null) {
endDirs = new String[0];
} else {
endDirs = new String[] {endDir.getAbsolutePath()};
}
return new LibraryInfo("???", new String[] {rtjar.toOSString()}, dirs, endDirs); //$NON-NLS-1$
}
/**
* Returns a list of all zip's and jars contained in the given directories.
*
* @param dirPaths a list of absolute paths of directories to search
* @return List of all zip's and jars
*/
public static List<LibraryLocation> gatherAllLibraries(String[] dirPaths) {
List<LibraryLocation> libraries = new ArrayList<>();
for (int i = 0; i < dirPaths.length; i++) {
File extDir = new File(dirPaths[i]);
if (extDir.isDirectory()) {
String[] names = extDir.list(fgArchiveFilter);
if (names != null) {
for (int j = 0; j < names.length; j++) {
File jar = new File(extDir, names[j]);
if (jar.isFile()) {
try {
IPath libPath = new Path(jar.getCanonicalPath());
IPath sourcePath = Path.EMPTY;
IPath packageRoot = Path.EMPTY;
URL javadocLocation = null;
URL indexLocation = null;
for (ILibraryLocationResolver resolver : getLibraryLocationResolvers()) {
try {
sourcePath = resolver.getSourcePath(libPath);
packageRoot = resolver.getPackageRoot(libPath);
javadocLocation = resolver.getJavadocLocation(libPath);
indexLocation = resolver.getIndexLocation(libPath);
if (sourcePath != Path.EMPTY || packageRoot != Path.EMPTY || javadocLocation != null || indexLocation != null) {
break;
}
} catch(Exception e) {
LaunchingPlugin.log(e);
}
}
LibraryLocation library = new LibraryLocation(libPath, sourcePath, packageRoot, javadocLocation, indexLocation);
libraries.add(library);
} catch (IOException e) {
LaunchingPlugin.log(e);
}
}
}
}
}
}
return libraries;
}
/**
* Returns the default location of the extension directory, based on the given
* install location. The resulting file may not exist, or be <code>null</code>
* if an extension directory is not supported.
*
* @param installLocation the VM install location
* @return default extension directory or <code>null</code>
*/
protected File getDefaultExtensionDirectory(File installLocation) {
File jre = null;
if (installLocation.getName().equalsIgnoreCase(JRE)) {
jre = installLocation;
} else {
jre = new File(installLocation, JRE);
}
File lib = new File(jre, LIB);
File ext = new File(lib, "ext"); //$NON-NLS-1$
return ext;
}
/**
* Returns the default location of the endorsed directory, based on the
* given install location. The resulting file may not exist, or be
* <code>null</code> if an endorsed directory is not supported.
*
* @param installLocation the VM install location
* @return default endorsed directory or <code>null</code>
*/
protected File getDefaultEndorsedDirectory(File installLocation) {
File lib = new File(installLocation, LIB);
File ext = new File(lib, "endorsed"); //$NON-NLS-1$
return ext;
}
protected String getDefaultRootPath() {
return fDefaultRootPath;
}
protected void setDefaultRootPath(String defaultRootPath) {
fDefaultRootPath = defaultRootPath;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.IVMInstallType#validateInstallLocation(java.io.File)
*/
@Override
public IStatus validateInstallLocation(File javaHome) {
IStatus status = null;
File javaExecutable = findJavaExecutable(javaHome);
if (javaExecutable == null) {
status = new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), 0, LaunchingMessages.StandardVMType_Not_a_JDK_Root__Java_executable_was_not_found_1, null); //
} else {
if (canDetectDefaultSystemLibraries(javaHome, javaExecutable)) {
status = new Status(IStatus.OK, LaunchingPlugin.getUniqueIdentifier(), 0, LaunchingMessages.StandardVMType_ok_2, null);
} else {
status = new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), 0, LaunchingMessages.StandardVMType_Not_a_JDK_root__System_library_was_not_found__1, null);
}
}
return status;
}
/**
* Generates library information for the given java executable. A main
* program is run (<code>org.eclipse.jdt.internal.launching.support.
* LibraryDetector</code>), that dumps the system properties for bootpath
* and extension directories. This output is then parsed and cached for
* future reference.
*
* @param javaHome the Java home folder
* @param javaExecutable the Java executable file
*
* @return library info or <code>null</code> if none
*/
protected LibraryInfo generateLibraryInfo(File javaHome, File javaExecutable) {
LibraryInfo info = null;
// if this is 1.1.X, the properties will not exist
IPath classesZip = new Path(javaHome.getAbsolutePath()).append(LIB).append("classes.zip"); //$NON-NLS-1$
if (classesZip.toFile().exists()) {
return new LibraryInfo("1.1.x", new String[] {classesZip.toOSString()}, new String[0], new String[0]); //$NON-NLS-1$
}
//locate the launching support jar - it contains the main program to run
File file = LaunchingPlugin.getFileInPlugin(new Path("lib/launchingsupport.jar")); //$NON-NLS-1$
if (file != null && file.exists()) {
String javaExecutablePath = javaExecutable.getAbsolutePath();
String[] cmdLine = new String[] { javaExecutablePath, MIN_VM_SIZE,
"-classpath", file.getAbsolutePath(), "org.eclipse.jdt.internal.launching.support.LibraryDetector" }; //$NON-NLS-1$ //$NON-NLS-2$
Process p = null;
try {
String envp[] = null;
if (Platform.OS_MACOSX.equals(Platform.getOS())) {
Map<String, String> map = DebugPlugin.getDefault().getLaunchManager().getNativeEnvironmentCasePreserved();
if (map.remove(StandardVMDebugger.JAVA_JVM_VERSION) != null) {
envp = new String[map.size()];
Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
int i = 0;
while (iterator.hasNext()) {
Entry<String, String> entry = iterator.next();
envp[i] = entry.getKey() + "=" + entry.getValue(); //$NON-NLS-1$
i++;
}
}
}
p = DebugPlugin.exec(cmdLine, null, envp);
IProcess process = DebugPlugin.newProcess(new Launch(null, ILaunchManager.RUN_MODE, null), p, "Library Detection"); //$NON-NLS-1$
process.setAttribute(IProcess.ATTR_CMDLINE, String.join(" ", cmdLine)); //$NON-NLS-1$
for (int i= 0; i < 600; i++) {
// Wait no more than 30 seconds (600 * 50 milliseconds)
if (process.isTerminated()) {
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
LaunchingPlugin.log(e);
}
}
checkProcessResult(process);
info = parseLibraryInfo(process);
} catch (Throwable ioe) {
LaunchingPlugin.log(ioe);
} finally {
if (p != null) {
p.destroy();
}
}
}
if (info == null) {
// log error that we were unable to generate library information - see bug 70011
LaunchingPlugin.log(NLS.bind("Failed to retrieve default libraries for {0}", new String[]{javaHome.getAbsolutePath()})); //$NON-NLS-1$
}
return info;
}
/**
* Parses the output from 'LibraryDetector'.
*
* @param process the backing {@link IProcess} that was run
* @return the new {@link LibraryInfo} object or <code>null</code>
*/
protected LibraryInfo parseLibraryInfo(IProcess process) {
IStreamsProxy streamsProxy = process.getStreamsProxy();
String text = null;
if (streamsProxy != null) {
text = streamsProxy.getOutputStreamMonitor().getContents();
}
if (text != null && text.length() > 0) {
int index = text.indexOf(BAR);
if (index > 0) {
String version = text.substring(0, index);
text = text.substring(index + 1);
index = text.indexOf(BAR);
if (index > 0) {
String bootPaths = text.substring(0, index);
String[] bootPath = parsePaths(bootPaths);
text = text.substring(index + 1);
index = text.indexOf(BAR);
if (index > 0) {
String extDirPaths = text.substring(0, index);
String endorsedDirsPath = text.substring(index + 1);
String[] extDirs = parsePaths(extDirPaths);
String[] endDirs = parsePaths(endorsedDirsPath);
return new LibraryInfo(version, bootPath, extDirs, endDirs);
}
}
}
}
return null;
}
protected String[] parsePaths(String paths) {
List<String> list = new ArrayList<>();
int pos = 0;
int index = paths.indexOf(File.pathSeparatorChar, pos);
while (index > 0) {
String path = paths.substring(pos, index);
list.add(path);
pos = index + 1;
index = paths.indexOf(File.pathSeparatorChar, pos);
}
String path = paths.substring(pos);
if (!path.equals("null")) { //$NON-NLS-1$
list.add(path);
}
return list.toArray(new String[list.size()]);
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.IVMInstallType#disposeVMInstall(java.lang.String)
*/
@Override
public void disposeVMInstall(String id) {
IVMInstall vm = findVMInstall(id);
if (vm != null) {
String path = vm.getInstallLocation().getAbsolutePath();
LaunchingPlugin.setLibraryInfo(path, null);
fgFailedInstallPath.remove(path);
fgDefaultLibLocs.remove(path);
}
super.disposeVMInstall(id);
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.AbstractVMInstallType#getDefaultJavadocLocation(java.io.File)
*/
@Override
public URL getDefaultJavadocLocation(File installLocation) {
File javaExecutable = findJavaExecutable(installLocation);
if (javaExecutable != null) {
LibraryInfo libInfo = getLibraryInfo(installLocation, javaExecutable);
if (libInfo != null) {
String version = libInfo.getVersion();
return getDefaultJavadocLocation(version);
}
}
return null;
}
/**
* Returns a default Javadoc location for a language version, or <code>null</code>.
*
* @param version language version such as "1.4"
* @return URL to default Javadoc location, or <code>null</code>
*/
public static URL getDefaultJavadocLocation(String version) {
try {
if (version.startsWith(JavaCore.VERSION_11)) {
return new URL("https://docs.oracle.com/en/java/javase/11/docs/api/"); //$NON-NLS-1$
} else if (version.startsWith(JavaCore.VERSION_10)) {
return new URL("https://docs.oracle.com/javase/10/docs/api/"); //$NON-NLS-1$
} else if (version.startsWith(JavaCore.VERSION_9)) {
return new URL("https://docs.oracle.com/javase/9/docs/api/"); //$NON-NLS-1$
} else if (version.startsWith(JavaCore.VERSION_1_8)) {
return new URL("https://docs.oracle.com/javase/8/docs/api/"); //$NON-NLS-1$
} else if (version.startsWith(JavaCore.VERSION_1_7)) {
return new URL("https://docs.oracle.com/javase/7/docs/api/"); //$NON-NLS-1$
} else if (version.startsWith(JavaCore.VERSION_1_6)) {
return new URL("https://docs.oracle.com/javase/6/docs/api/"); //$NON-NLS-1$
} else if (version.startsWith(JavaCore.VERSION_1_5)) {
return new URL("https://docs.oracle.com/javase/1.5.0/docs/api/"); //$NON-NLS-1$
} else if (version.startsWith(JavaCore.VERSION_1_4)) {
// archived: http://download.oracle.com/javase/1.4.2/docs/api/
return new URL("https://docs.oracle.com/javase/1.5.0/docs/api/"); //$NON-NLS-1$
} else if (version.startsWith(JavaCore.VERSION_1_3)) {
// archived: http://download.oracle.com/javase/1.3/docs/api/
return new URL("https://docs.oracle.com/javase/1.5.0/docs/api/"); //$NON-NLS-1$
}
} catch (MalformedURLException e) {
}
return null;
}
public synchronized String readReleaseVersion(File javaHome) {
String version = ""; //$NON-NLS-1$
if (Files.notExists(Paths.get(javaHome.getAbsolutePath(), RELEASE_FILE))) {
return version;
}
try (Stream<String> lines = Files.lines(Paths.get(javaHome.getAbsolutePath(), RELEASE_FILE)).filter(s -> s.contains(JAVA_VERSION))) {
Optional<String> hasVersion = lines.findFirst();
if (hasVersion.isPresent()) {
String line = hasVersion.get();
version = line.substring(14, line.length() - 1); // length of JAVA_VERSION + 2 in JAVA_VERSION="9"
}
}
catch (IOException e) {
e.printStackTrace();
}
return version;
}
/*
* Logs an error if the process is not yet done, or if the process exited with an error code.
*/
private static void checkProcessResult(IProcess process) throws DebugException {
boolean isTerminated = process.isTerminated();
if (!isTerminated) {
String output = getOutput(process);
Object[] errorInfo = { process.getAttribute(IProcess.ATTR_CMDLINE), output };
String errorMessage = NLS.bind("Process not finished.\n Command line arguments: {0}\nOutput: {1}", errorInfo); //$NON-NLS-1$
IllegalStateException exception = new IllegalStateException(errorMessage);
LaunchingPlugin.log(exception);
} else {
int exitCode = process.getExitValue();
if (exitCode != 0) {
String output = getOutput(process);
Object[] errorInfo = { Integer.valueOf(exitCode), process.getAttribute(IProcess.ATTR_CMDLINE), output };
String errorMessage = NLS.bind("Process returned with error code \"{0}\".\nCommand line arguments: {1}\nOutput: {2}", errorInfo); //$NON-NLS-1$
IllegalStateException exception = new IllegalStateException(errorMessage);
LaunchingPlugin.log(exception);
}
}
}
private static String getOutput(IProcess process) {
IStreamsProxy streamsProxy = process.getStreamsProxy();
String output = "IProcess.getStreamsProxy() returned null"; //$NON-NLS-1$
if (streamsProxy != null) {
String[] lines = { "Standard output:", //$NON-NLS-1$
streamsProxy.getOutputStreamMonitor().getContents(), "Standard error:", //$NON-NLS-1$
streamsProxy.getErrorStreamMonitor().getContents() };
output = String.join(System.lineSeparator(), Arrays.asList(lines));
}
return output;
}
}