/******************************************************************************* * Copyright (c) 2000, 2015 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 * Mikhail Kalkov - Bug 414285, On systems with large RAM, evaluateSystemProperties and generateLibraryInfo fail for 64-bit JREs *******************************************************************************/ package org.eclipse.jdt.launching; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; 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.internal.launching.LaunchingMessages; import org.eclipse.jdt.internal.launching.LaunchingPlugin; import org.eclipse.jdt.internal.launching.Standard11xVM; import org.eclipse.jdt.internal.launching.StandardVMType; import org.eclipse.osgi.util.NLS; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * Abstract implementation of a VM install. * <p> * Clients implementing VM installs must subclass this class. * </p> */ public abstract class AbstractVMInstall implements IVMInstall, IVMInstall2, IVMInstall3 { private IVMInstallType fType; private String fId; private String fName; private File fInstallLocation; private LibraryLocation[] fSystemLibraryDescriptions; private URL fJavadocLocation; private String fVMArgs; /** * Map VM specific attributes that are persisted restored with a VM install. * @since 3.4 */ private Map<String, String> fAttributeMap = new HashMap<>(); // system properties are cached in user preferences prefixed with this key, followed // by VM type, VM id, and system property name private static final String PREF_VM_INSTALL_SYSTEM_PROPERTY = "PREF_VM_INSTALL_SYSTEM_PROPERTY"; //$NON-NLS-1$ // whether change events should be fired private boolean fNotify = true; /** * Constructs a new VM install. * * @param type The type of this VM install. * Must not be <code>null</code> * @param id The unique identifier of this VM instance * Must not be <code>null</code>. * @throws IllegalArgumentException if any of the required * parameters are <code>null</code>. */ public AbstractVMInstall(IVMInstallType type, String id) { if (type == null) { throw new IllegalArgumentException(LaunchingMessages.vmInstall_assert_typeNotNull); } if (id == null) { throw new IllegalArgumentException(LaunchingMessages.vmInstall_assert_idNotNull); } fType= type; fId= id; } /* (non-Javadoc) * Subclasses should not override this method. * @see IVMInstall#getId() */ @Override public String getId() { return fId; } /* (non-Javadoc) * Subclasses should not override this method. * @see IVMInstall#getName() */ @Override public String getName() { return fName; } /* (non-Javadoc) * Subclasses should not override this method. * @see IVMInstall#setName(String) */ @Override public void setName(String name) { if (!name.equals(fName)) { PropertyChangeEvent event = new PropertyChangeEvent(this, IVMInstallChangedListener.PROPERTY_NAME, fName, name); fName= name; if (fNotify) { JavaRuntime.fireVMChanged(event); } } } /* (non-Javadoc) * Subclasses should not override this method. * @see IVMInstall#getInstallLocation() */ @Override public File getInstallLocation() { return fInstallLocation; } /* (non-Javadoc) * Subclasses should not override this method. * @see IVMInstall#setInstallLocation(File) */ @Override public void setInstallLocation(File installLocation) { if (!installLocation.equals(fInstallLocation)) { PropertyChangeEvent event = new PropertyChangeEvent(this, IVMInstallChangedListener.PROPERTY_INSTALL_LOCATION, fInstallLocation, installLocation); fInstallLocation= installLocation; if (fNotify) { JavaRuntime.fireVMChanged(event); } } } /* (non-Javadoc) * Subclasses should not override this method. * @see IVMInstall#getVMInstallType() */ @Override public IVMInstallType getVMInstallType() { return fType; } /* (non-Javadoc) * @see IVMInstall#getVMRunner(String) */ @Override public IVMRunner getVMRunner(String mode) { return null; } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IVMInstall#getLibraryLocations() */ @Override public LibraryLocation[] getLibraryLocations() { return fSystemLibraryDescriptions; } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IVMInstall#setLibraryLocations(org.eclipse.jdt.launching.LibraryLocation[]) */ @Override public void setLibraryLocations(LibraryLocation[] locations) { if (locations == fSystemLibraryDescriptions) { return; } LibraryLocation[] newLocations = locations; if (newLocations == null) { newLocations = getVMInstallType().getDefaultLibraryLocations(getInstallLocation()); } LibraryLocation[] prevLocations = fSystemLibraryDescriptions; if (prevLocations == null) { prevLocations = getVMInstallType().getDefaultLibraryLocations(getInstallLocation()); } if (newLocations.length == prevLocations.length) { int i = 0; boolean equal = true; while (i < newLocations.length && equal) { equal = newLocations[i].equals(prevLocations[i]); i++; } if (equal) { // no change return; } } PropertyChangeEvent event = new PropertyChangeEvent(this, IVMInstallChangedListener.PROPERTY_LIBRARY_LOCATIONS, prevLocations, newLocations); fSystemLibraryDescriptions = locations; if (fNotify) { JavaRuntime.fireVMChanged(event); } } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IVMInstall#getJavadocLocation() */ @Override public URL getJavadocLocation() { return fJavadocLocation; } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IVMInstall#setJavadocLocation(java.net.URL) */ @Override public void setJavadocLocation(URL url) { if (url == fJavadocLocation) { return; } if (url != null && fJavadocLocation != null) { if (url.toExternalForm().equals(fJavadocLocation.toExternalForm())) { // no change return; } } PropertyChangeEvent event = new PropertyChangeEvent(this, IVMInstallChangedListener.PROPERTY_JAVADOC_LOCATION, fJavadocLocation, url); fJavadocLocation = url; if (fNotify) { JavaRuntime.fireVMChanged(event); } } /** * Whether this VM should fire property change notifications. * * @param notify if this VM should fire property change notifications. * @since 2.1 */ protected void setNotify(boolean notify) { fNotify = notify; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) * @since 2.1 */ @Override public boolean equals(Object object) { if (object instanceof IVMInstall) { IVMInstall vm = (IVMInstall)object; return getVMInstallType().equals(vm.getVMInstallType()) && getId().equals(vm.getId()); } return false; } /* (non-Javadoc) * @see java.lang.Object#hashCode() * @since 2.1 */ @Override public int hashCode() { return getVMInstallType().hashCode() + getId().hashCode(); } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IVMInstall#getDefaultVMArguments() * @since 3.0 */ @Override public String[] getVMArguments() { String args = getVMArgs(); if (args == null) { return null; } ExecutionArguments ex = new ExecutionArguments(args, ""); //$NON-NLS-1$ return ex.getVMArgumentsArray(); } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IVMInstall#setDefaultVMArguments(java.lang.String[]) * @since 3.0 */ @Override public void setVMArguments(String[] vmArgs) { if (vmArgs == null) { setVMArgs(null); } else { StringBuffer buf = new StringBuffer(); for (int i = 0; i < vmArgs.length; i++) { String string = vmArgs[i]; buf.append(string); buf.append(" "); //$NON-NLS-1$ } setVMArgs(buf.toString().trim()); } } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IVMInstall2#getVMArgs() */ @Override public String getVMArgs() { return fVMArgs; } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IVMInstall2#setVMArgs(java.lang.String) */ @Override public void setVMArgs(String vmArgs) { if (fVMArgs == null) { if (vmArgs == null) { // No change return; } } else if (fVMArgs.equals(vmArgs)) { // No change return; } PropertyChangeEvent event = new PropertyChangeEvent(this, IVMInstallChangedListener.PROPERTY_VM_ARGUMENTS, fVMArgs, vmArgs); fVMArgs = vmArgs; if (fNotify) { JavaRuntime.fireVMChanged(event); } } /* (non-Javadoc) * Subclasses should override. * @see org.eclipse.jdt.launching.IVMInstall2#getJavaVersion() */ @Override public String getJavaVersion() { return null; } /* (non-Javadoc) * @see org.eclipse.jdt.launching.IVMInstall3#evaluateSystemProperties(java.lang.String[], org.eclipse.core.runtime.IProgressMonitor) */ @Override public Map<String, String> evaluateSystemProperties(String[] properties, IProgressMonitor monitor) throws CoreException { //locate the launching support jar - it contains the main program to run if (monitor == null) { monitor = new NullProgressMonitor(); } Map<String, String> map = new HashMap<>(); // first check cache (preference store) to avoid launching VM boolean cached = true; IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(LaunchingPlugin.ID_PLUGIN); if(prefs != null) { for (int i = 0; i < properties.length; i++) { String property = properties[i]; String key = getSystemPropertyKey(property); String value = prefs.get(key, null); if (value != null) { map.put(property, value); } else { map.clear(); cached = false; break; } } } if (!cached) { // launch VM to evaluate properties File file = LaunchingPlugin.getFileInPlugin(new Path("lib/launchingsupport.jar")); //$NON-NLS-1$ if (file != null && file.exists()) { VMRunnerConfiguration config = new VMRunnerConfiguration("org.eclipse.jdt.internal.launching.support.LegacySystemProperties", new String[] { file.getAbsolutePath() });//$NON-NLS-1$ IVMRunner runner = getVMRunner(ILaunchManager.RUN_MODE); if (runner == null) { abort(NLS.bind(LaunchingMessages.AbstractVMInstall_0, ""), null, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); //$NON-NLS-1$ } if (!(this instanceof Standard11xVM)) { config.setVMArguments(new String[] { StandardVMType.MIN_VM_SIZE }); } config.setProgramArguments(properties); Launch launch = new Launch(null, ILaunchManager.RUN_MODE, null); if (monitor.isCanceled()) { return map; } monitor.beginTask(LaunchingMessages.AbstractVMInstall_1, 2); runner.run(config, launch, monitor); IProcess[] processes = launch.getProcesses(); if (processes.length != 1) { abort(NLS.bind(LaunchingMessages.AbstractVMInstall_0, runner), null, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); } IProcess process = processes[0]; try { int total = 0; int max = Platform.getPreferencesService().getInt( LaunchingPlugin.ID_PLUGIN, JavaRuntime.PREF_CONNECT_TIMEOUT, JavaRuntime.DEF_CONNECT_TIMEOUT, null); while (!process.isTerminated()) { try { if (total > max) { break; } Thread.sleep(50); total+=50; } catch (InterruptedException e) { } } } finally { if (!launch.isTerminated()) { launch.terminate(); } } monitor.worked(1); if (monitor.isCanceled()) { return map; } monitor.subTask(LaunchingMessages.AbstractVMInstall_3); IStreamsProxy streamsProxy = process.getStreamsProxy(); String text = null; if (streamsProxy != null) { text = streamsProxy.getOutputStreamMonitor().getContents(); } if (text != null && text.length() > 0) { try { DocumentBuilder parser = LaunchingPlugin.getParser(); Document document = parser.parse(new ByteArrayInputStream(text.getBytes())); Element envs = document.getDocumentElement(); NodeList list = envs.getChildNodes(); int length = list.getLength(); for (int i = 0; i < length; ++i) { Node node = list.item(i); short type = node.getNodeType(); if (type == Node.ELEMENT_NODE) { Element element = (Element) node; if (element.getNodeName().equals("property")) { //$NON-NLS-1$ String name = element.getAttribute("name"); //$NON-NLS-1$ String value = element.getAttribute("value"); //$NON-NLS-1$ map.put(name, value); } } } } catch (SAXException e) { String commandLine = process.getAttribute(IProcess.ATTR_CMDLINE); abort(NLS.bind(LaunchingMessages.AbstractVMInstall_4, commandLine), e, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); } catch (IOException e) { String commandLine = process.getAttribute(IProcess.ATTR_CMDLINE); abort(NLS.bind(LaunchingMessages.AbstractVMInstall_4, commandLine), e, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); } } else { String commandLine = process.getAttribute(IProcess.ATTR_CMDLINE); abort(NLS.bind(LaunchingMessages.AbstractVMInstall_0, commandLine), null, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); } monitor.worked(1); } else { abort(NLS.bind(LaunchingMessages.AbstractVMInstall_0, file), null, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR); } // cache for future reference Iterator<String> keys = map.keySet().iterator(); while (keys.hasNext()) { String property = keys.next(); String value = map.get(property); String key = getSystemPropertyKey(property); prefs.put(key, value); } } monitor.done(); return map; } /** * Generates a key used to cache system property for this VM in this plug-ins * preference store. * * @param property system property name * @return preference store key */ private String getSystemPropertyKey(String property) { StringBuffer buffer = new StringBuffer(); buffer.append(PREF_VM_INSTALL_SYSTEM_PROPERTY); buffer.append("."); //$NON-NLS-1$ buffer.append(getVMInstallType().getId()); buffer.append("."); //$NON-NLS-1$ buffer.append(getId()); buffer.append("."); //$NON-NLS-1$ buffer.append(property); return buffer.toString(); } /** * Throws a core exception with an error status object built from the given * message, lower level exception, and error code. * * @param message the status message * @param exception lower level exception associated with the error, or * <code>null</code> if none * @param code error code * @throws CoreException the "abort" core exception * @since 3.2 */ protected void abort(String message, Throwable exception, int code) throws CoreException { throw new CoreException(new Status(IStatus.ERROR, LaunchingPlugin .getUniqueIdentifier(), code, message, exception)); } /** * Sets a VM specific attribute. Attributes are persisted and restored with VM installs. * Specifying a value of <code>null</code> as a value removes the attribute. Change * notification is provided to {@link IVMInstallChangedListener} for VM attributes. * * @param key attribute key, cannot be <code>null</code> * @param value attribute value or <code>null</code> to remove the attribute * @since 3.4 */ public void setAttribute(String key, String value) { String prevValue = fAttributeMap.remove(key); boolean notify = false; if (value == null) { if (prevValue != null && fNotify) { notify = true; } } else { fAttributeMap.put(key, value); if (fNotify && (prevValue == null || !prevValue.equals(value))) { notify = true; } } if (notify) { PropertyChangeEvent event = new PropertyChangeEvent(this, key, prevValue, value); JavaRuntime.fireVMChanged(event); } } /** * Returns a VM specific attribute associated with the given key or <code>null</code> * if none. * * @param key attribute key, cannot be <code>null</code> * @return attribute value, or <code>null</code> if none * @since 3.4 */ public String getAttribute(String key) { return fAttributeMap.get(key); } /** * Returns a map of VM specific attributes stored with this VM install. Keys * and values are strings. Modifying the map does not modify the attributes * associated with this VM install. * * @return map of VM attributes * @since 3.4 */ public Map<String, String> getAttributes() { return new HashMap<>(fAttributeMap); } }