/******************************************************************************* * Copyright (c) 2000, 2013 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 * Jeff Myers myersj@gmail.com - fix for #75201 * Ralf Ebert ralf@ralfebert.de - fix for #307109 *******************************************************************************/ package org.eclipse.jdt.internal.launching.macosx; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.internal.launching.LaunchingPlugin; import org.eclipse.jdt.internal.launching.LibraryInfo; import org.eclipse.jdt.internal.launching.MacInstalledJREs; import org.eclipse.jdt.internal.launching.StandardVMType; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.IVMInstallType; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.VMStandin; import org.eclipse.osgi.util.NLS; /** * This class provides the implementation of the {@link IVMInstallType} for Mac OSX. * * The default VM locations are outlined below. each VM except for developer VMs provide links in the * <code>/System/Library/Frameworks/JavaVM.framework/Versions/</code> folder, with a link named * <code>CurrentJDK</code> that points to the VM you have set using the Java preference tool in the system preferences. * <br><br> * The directory structure for Java VMs prior to Snow Leopard is as follows: * <pre> * /System/Library/Frameworks/JavaVM.framework/Versions/ * 1.3.1/ * Classes/ * classes.jar * ui.jar * Home/ * src.jar * </pre> * * The directory structure for developer VMs is: * <pre> * /Library/Java/JavaVirtualMachines/ * 1.7.0.jdk/ * Contents/ * Home/ * bin/ * lib/ * ... * src.zip * </pre> * * The directory structure for Snow Leopard and Lion VMs is: * <pre> * /System/Library/Java/JavaVirtualMachines/ * 1.6.0.jdk/ * Contents/ * Classes/ * Home/ * src.zip * </pre> * * @see http://developer.apple.com/library/mac/#qa/qa1170/_index.html * @see http://developer.apple.com/library/mac/#releasenotes/Java/JavaSnowLeopardUpdate3LeopardUpdate8RN/NewandNoteworthy/NewandNoteworthy.html#//apple_ref/doc/uid/TP40010380-CH4-SW1 */ public class MacOSXVMInstallType extends StandardVMType { /** The OS keeps all the JVM versions in this directory */ private static final String JVM_VERSION_LOC= "/System/Library/Frameworks/JavaVM.framework/Versions/"; //$NON-NLS-1$ private static final File JVM_VERSIONS_FOLDER= new File(JVM_VERSION_LOC); /** The name of a Unix link to MacOS X's default VM */ private static final String CURRENT_JDK= "CurrentJDK"; //$NON-NLS-1$ /** The root of a JVM */ private static final String JVM_HOME= "Home"; //$NON-NLS-1$ /** The doc (for all JVMs) lives here (if the developer kit has been expanded)*/ private static final String JAVADOC_LOC= "/Developer/Documentation/Java/Reference/"; //$NON-NLS-1$ /** The doc for 1.4.1 is kept in a sub directory of the above. */ private static final String JAVADOC_SUBDIR= "/doc/api"; //$NON-NLS-1$ /** * The name of the src.zip file for the JDK source * @since 3.2.200 */ static final String SRC_ZIP = "src.zip"; //$NON-NLS-1$ /** * The name of the src.jar file for legacy JDK/JREs * @since 3.2.200 */ static final String SRC_JAR = "src.jar"; //$NON-NLS-1$ /** * The name of the source used for libraries on the Mac * @since 3.2.200 */ static final String SRC_NAME = "src"; //$NON-NLS-1$ /** * The name of the Contents folder found within a JRE/JDK folder * @since 3.2.200 */ static final String JVM_CONTENTS = "Contents"; //$NON-NLS-1$ /** * The name of the Classes folder used to hold the libraries for a legacy JDK/JRE * @since 3.2.200 */ static final String JVM_CLASSES = "Classes"; //$NON-NLS-1$ /** * The name of the Versions folder for legacy JRE/JDK installs * @since 3.2.200 */ static final String JVM_VERSIONS = "Versions"; //$NON-NLS-1$ @Override public String getName() { return Messages.MacOSXVMInstallType_0; } @Override public IVMInstall doCreateVMInstall(String id) { return new MacOSXVMInstall(this, id); } /* * @see IVMInstallType#detectInstallLocation() */ @Override public File detectInstallLocation() { try { // try to find the VM used to launch Eclipse // https://bugs.eclipse.org/bugs/show_bug.cgi?id=407402 File defaultLocation = getJavaHomeLocation(); // find all installed VMs VMStandin[] vms = MacInstalledJREs.getInstalledJREs(null); File firstLocation = null; IVMInstall firstInstall = null; IVMInstall defaultInstall = null; for (int i= 0; i < vms.length; i++) { File location = vms[i].getInstallLocation(); IVMInstall install = findVMInstall(vms[i].getId()); if (install == null) { install= vms[i].convertToRealVM(); } if (i == 0) { firstLocation = location; firstInstall = install; } if (defaultInstall == null && defaultLocation != null && defaultLocation.equals(location)) { defaultInstall = install; } } // determine the default VM if (defaultInstall == null) { if (defaultLocation != null) { // prefer the VM used to launch Eclipse String version = System.getProperty("java.version"); //$NON-NLS-1$ VMStandin standin = new MacInstalledJREs.MacVMStandin(this, defaultLocation, version == null ? Messages.MacOSXVMInstallType_jre : NLS.bind(Messages.MacOSXVMInstallType_jre_version, version), (version == null ? "???" : version), //$NON-NLS-1$ String.valueOf(System.currentTimeMillis())); defaultInstall = standin.convertToRealVM(); } else { defaultInstall = firstInstall; defaultLocation = firstLocation; } } if (defaultInstall != null) { try { JavaRuntime.setDefaultVMInstall(defaultInstall, null); } catch (CoreException e) { LaunchingPlugin.log(e); } } return defaultLocation; } catch (CoreException e) { MacOSXLaunchingPlugin.getDefault().getLog().log(e.getStatus()); return detectInstallLocationOld(); } } /** * The proper way to find installed JREs is to parse the XML output produced from "java_home -X" * (see bug 325777). However, if that fails, revert to the hard coded search. * * @return file that points to the default JRE install */ private File detectInstallLocationOld() { String javaVMName= System.getProperty("java.vm.name"); //$NON-NLS-1$ if (javaVMName == null) { return null; } if (!JVM_VERSIONS_FOLDER.exists() || !JVM_VERSIONS_FOLDER.isDirectory()) { String message= NLS.bind(Messages.MacOSXVMInstallType_1, JVM_VERSIONS_FOLDER); LaunchingPlugin.log(message); return null; } // find all installed VMs File defaultLocation= null; File[] versions= getAllVersionsOld(); File currentJDK= getCurrentJDKOld(); for (int i= 0; i < versions.length; i++) { File versionFile= versions[i]; String version= versionFile.getName(); File home= new File(versionFile, JVM_HOME); if (home.exists()) { boolean isDefault= currentJDK.equals(versionFile); IVMInstall install= findVMInstall(version); if (install == null) { VMStandin vm= new VMStandin(this, version); vm.setInstallLocation(home); vm.setName(version); vm.setLibraryLocations(getDefaultLibraryLocations(home)); vm.setJavadocLocation(getDefaultJavadocLocation(home)); install= vm.convertToRealVM(); } if (isDefault) { defaultLocation= home; try { JavaRuntime.setDefaultVMInstall(install, null); } catch (CoreException e) { LaunchingPlugin.log(e); } } } } return defaultLocation; } /** * The proper way to find installed JREs is to parse the XML output produced from "java_home -X" * (see bug 325777). However, if that fails, revert to the hard coded search. * * @return array of files that point to JRE install directories */ private File[] getAllVersionsOld() { File[] versionFiles= JVM_VERSIONS_FOLDER.listFiles(); for (int i= 0; i < versionFiles.length; i++) { versionFiles[i]= resolveSymbolicLinks(versionFiles[i]); } return versionFiles; } /** * The proper way to find the default JRE is to parse the XML output produced from "java_home -X" * and take the first entry in the list. However, if that fails, revert to the hard coded search. * * @return a file that points to the default JRE install directory */ private File getCurrentJDKOld() { return resolveSymbolicLinks(new File(JVM_VERSIONS_FOLDER, CURRENT_JDK)); } private File resolveSymbolicLinks(File file) { try { return file.getCanonicalFile(); } catch (IOException ex) { return file; } } /* (non-Javadoc) * @see org.eclipse.jdt.internal.launching.StandardVMType#getDefaultLibraryInfo(java.io.File) */ @Override protected LibraryInfo getDefaultLibraryInfo(File installLocation) { IPath rtjar = getDefaultSystemLibrary(installLocation); if(rtjar.toFile().isFile()) { //not a Mac OS VM, default to the standard VM type info collection return super.getDefaultLibraryInfo(installLocation); } File classes = new File(installLocation, "../Classes"); //$NON-NLS-1$ File lib1= new File(classes, "classes.jar"); //$NON-NLS-1$ File lib2= new File(classes, "ui.jar"); //$NON-NLS-1$ String[] libs = new String[] { lib1.toString(),lib2.toString() }; File lib = new File(installLocation, "lib"); //$NON-NLS-1$ File extDir = new File(lib, "ext"); //$NON-NLS-1$ String[] dirs = null; if (extDir.exists()) dirs = new String[] {extDir.getAbsolutePath()}; else dirs = new String[0]; File endDir = new File(lib, "endorsed"); //$NON-NLS-1$ String[] endDirs = null; if (endDir.exists()) endDirs = new String[] {endDir.getAbsolutePath()}; else endDirs = new String[0]; return new LibraryInfo("???", libs, dirs, endDirs); //$NON-NLS-1$ } /* (non-Javadoc) * @see org.eclipse.jdt.internal.launching.StandardVMType#getDefaultSystemLibrarySource(java.io.File) */ @Override protected IPath getDefaultSystemLibrarySource(File libLocation) { File parent = libLocation.getParentFile(); File src = null; //Walk the parent hierarchy, stop if we run out of parents or we hit the /Contents directory. //For the new shape of JRE/JDKs we can stop once we hit the root Contents folder, for legacy versions //we can stop when we hit the Versions folder String pname = parent.getName(); while (parent != null && !JVM_CONTENTS.equals(pname) && !JVM_VERSIONS.equals(pname)) { //In Mac OSX supplied JDK/JREs the /Home directory is co-located to the /Classes directory if(JVM_CLASSES.equals(pname)) { src = new File(parent.getParent(), JVM_HOME); src = getSourceInParent(src); } else { src = getSourceInParent(parent); } if(src != null) { setDefaultRootPath(SRC_NAME); return new Path(src.getPath()); } parent = parent.getParentFile(); } setDefaultRootPath(""); //$NON-NLS-1$ return Path.EMPTY; } /** * Checks to see if <code>src.zip</code> or <code>src.jar</code> exists in the given parent * folder. Returns <code>null</code> if it does not exist. * <br><br> * The newer naming of the archive is <code>src.zip</code> and the older (pre-1.6) is * <code>src.jar</code> * * @param parent the parent directory * @return the {@link File} for the source archive or <code>null</code> * @since 3.2.200 */ File getSourceInParent(File parent) { if(parent.isDirectory()) { File src = new File(parent, SRC_ZIP); if(src.isFile()) { return src; } src = new File(src, SRC_JAR); if(src.isFile()) { return src; } } return null; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.launching.StandardVMType#validateInstallLocation(java.io.File) */ @Override public IStatus validateInstallLocation(File javaHome) { String id= MacOSXLaunchingPlugin.getUniqueIdentifier(); File java= new File(javaHome, "bin"+File.separator+"java"); //$NON-NLS-2$ //$NON-NLS-1$ if (java.isFile()) return new Status(IStatus.OK, id, 0, "ok", null); //$NON-NLS-1$ return new Status(IStatus.ERROR, id, 0, Messages.MacOSXVMInstallType_2, null); } /* (non-Javadoc) * @see org.eclipse.jdt.internal.launching.StandardVMType#getDefaultJavadocLocation(java.io.File) */ @Override public URL getDefaultJavadocLocation(File installLocation) { // try in local filesystem String id= null; try { String post= File.separator + JVM_HOME; String path= installLocation.getCanonicalPath(); if (path.startsWith(JVM_VERSION_LOC) && path.endsWith(post)) id= path.substring(JVM_VERSION_LOC.length(), path.length()-post.length()); } catch (IOException ex) { // we use the fall back from below } if (id != null) { String s= JAVADOC_LOC + id + JAVADOC_SUBDIR; File docLocation= new File(s); if (!docLocation.exists()) { s= JAVADOC_LOC + id; docLocation= new File(s); if (!docLocation.exists()) s= null; } if (s != null) { try { return new URL("file", "", s); //$NON-NLS-1$ //$NON-NLS-2$ } catch (MalformedURLException ex) { // we use the fall back from below } } } // fall back return super.getDefaultJavadocLocation(installLocation); } /* * Overridden to make it visible. */ @Override protected String getVMVersion(File javaHome, File javaExecutable) { return super.getVMVersion(javaHome, javaExecutable); } }