blob: 9d78e27b60753da3564f555d1195791d69c9a3ed [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2013 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.jdt.internal.launching;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
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.launching.AbstractVMInstallType;
import org.eclipse.jdt.launching.IVMInstallType;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.VMStandin;
import org.eclipse.osgi.util.NLS;
/**
* Searches for installed JREs on the Mac.
*/
public class MacInstalledJREs {
/** The executable for 'java_home' */
private static final String JAVA_HOME_PLIST = "/usr/libexec/java_home"; //$NON-NLS-1$
/** The plist attribute describing the JRE home directory */
private static final String PLIST_JVM_HOME_PATH = "JVMHomePath"; //$NON-NLS-1$
/** The plist attribute describing the JRE name */
private static final String PLIST_JVM_NAME = "JVMName"; //$NON-NLS-1$
/** The plist attribute describing the JRE version */
private static final String PLIST_JVM_VERSION = "JVMVersion"; //$NON-NLS-1$
/**
* The plist attribute describing the bundle id of the VM
* @since 3.8
*/
private static final String PLIST_JVM_BUNDLE_ID = "JVMBundleID"; //$NON-NLS-1$
public static final VMStandin[] NO_VMS = new VMStandin[0];
/**
* Custom stand-in that allows us to provide a version
* @since 3.7.0
*/
public static class MacVMStandin extends VMStandin {
String version = null;
public MacVMStandin(IVMInstallType type, File location, String name, String version, String id) {
super(type, id);
setInstallLocation(location);
setName(name);
this.version = version;
}
@Override
public String getJavaVersion() {
return version;
}
}
/**
* Parses the XML output produced from "java_home -X" (see bug 325777), and return a collection
* of descriptions of JRE installations.
*
* @param monitor the {@link IProgressMonitor} or <code>null</code>
* @return array of {@link VMStandin}s installed in the OS
* @exception CoreException if unable to parse the output or the executable does not exist
*/
public static VMStandin[] getInstalledJREs(IProgressMonitor monitor) throws CoreException {
SubMonitor smonitor = SubMonitor.convert(monitor);
try {
// locate the "java_home" executable
File java_home = new File(JAVA_HOME_PLIST);
if (!java_home.exists()) {
throw new CoreException(new Status(IStatus.WARNING, LaunchingPlugin.getUniqueIdentifier(), "The java_home executable does not exist")); //$NON-NLS-1$
}
String[] cmdLine = new String[] {JAVA_HOME_PLIST, "-X"}; //$NON-NLS-1$
Process p = null;
try {
p = DebugPlugin.exec(cmdLine, null);
IProcess process = DebugPlugin.newProcess(new Launch(null, ILaunchManager.RUN_MODE, null), p, "JRE Install Detection"); //$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) {
// do nothing
}
}
return parseJREInfo(process, monitor);
} finally {
if (p != null) {
p.destroy();
}
}
}
finally {
if(!smonitor.isCanceled()) {
smonitor.done();
}
}
}
/**
* Parses the output from 'java_home -X'.
*
* @param process process with output from 'java_home -X'
* @param the {@link IProgressMonitor} or <code>null</code>
* @return array JRE descriptions installed in the OS
* @exception CoreException if unable to parse the output
*/
private static VMStandin[] parseJREInfo(IProcess process, IProgressMonitor monitor) throws CoreException {
IStreamsProxy streamsProxy = process.getStreamsProxy();
String text = null;
if (streamsProxy != null) {
text = streamsProxy.getOutputStreamMonitor().getContents();
}
if (text != null && text.length() > 0) {
ByteArrayInputStream stream = new ByteArrayInputStream(text.getBytes());
return parseJREInfo(stream, monitor);
}
return NO_VMS;
}
/**
* Parse {@link JREDescriptor}s from the given input stream. The stream is expected to be in the
* XML properties format.
*
* @param monitor the {@link IProgressMonitor} or <code>null</code>
* @param stream
* @return the array of {@link VMStandin}s or an empty array never <code>null</code>
* @since 3.8
*/
public static VMStandin[] parseJREInfo(InputStream stream, IProgressMonitor monitor) {
SubMonitor smonitor = SubMonitor.convert(monitor, LaunchingMessages.MacInstalledJREs_0, 10);
try {
Object result = new PListParser().parse(stream);
if (result instanceof Object[]) {
Object[] maps = (Object[]) result;
smonitor.setWorkRemaining(maps.length);
List<VMStandin> jres= new ArrayList<>();
AbstractVMInstallType mactype = (AbstractVMInstallType) JavaRuntime.getVMInstallType("org.eclipse.jdt.internal.launching.macosx.MacOSXType"); //$NON-NLS-1$
if(mactype != null) {
for (int i = 0; i < maps.length; i++) {
if(smonitor.isCanceled()) {
///stop processing and return what we found
return jres.toArray(new VMStandin[jres.size()]);
}
Object object = maps[i];
if (object instanceof Map) {
Map<?, ?> map = (Map<?, ?>) object;
Object home = map.get(PLIST_JVM_HOME_PATH);
Object name = map.get(PLIST_JVM_NAME);
Object version = map.get(PLIST_JVM_VERSION);
if (home instanceof String && name instanceof String && version instanceof String) {
smonitor.setTaskName(NLS.bind(LaunchingMessages.MacInstalledJREs_1, new String[] {(String) name, (String) version}));
String ver = (String) version;
File loc = new File((String)home);
//10.8.2+ can have more than one of the same VM, which will have the same name
//augment it with the version to make it easier to distinguish
StringBuilder namebuff = new StringBuilder(name.toString());
namebuff.append(" [").append(ver).append("]"); //$NON-NLS-1$//$NON-NLS-2$
MacVMStandin vm = new MacVMStandin(mactype, loc, namebuff.toString(), ver, computeId(map, ver));
vm.setJavadocLocation(mactype.getDefaultJavadocLocation(loc));
vm.setLibraryLocations(mactype.getDefaultLibraryLocations(loc));
vm.setVMArgs(mactype.getDefaultVMArguments(loc));
if (!jres.contains(vm)) { // remove duplicates
jres.add(vm);
}
}
}
smonitor.worked(1);
}
}
return jres.toArray(new VMStandin[jres.size()]);
}
} catch (CoreException ce) {
LaunchingPlugin.log(ce);
}
finally {
smonitor.done();
}
return NO_VMS;
}
/**
* Tries to compute the descriptor id using the {@link #PLIST_JVM_BUNDLE_ID}. If that is not defined
* we fall back to using the version.
* @param map the map to look up the VM bundle version in
* @param version the current version - fall-back for no VM bundle id defined
* @return the id to use for the {@link JREDescriptor}
* @since 3.8
*/
static String computeId(Map<?, ?> map, String version) {
Object o = map.get(PLIST_JVM_BUNDLE_ID);
if(o instanceof String) {
return (String) o;
}
return version;
}
}