blob: 3f6e8de39f66bbbd8a76fe018f12274f0e5dc008 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2020 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
* This is an implementation of an early-draft specification developed under the Java
* Community Process (JCP) and is made available for testing and evaluation purposes
* only. The code is not compatible with any specification of the JCP.
*
* Contributors:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.jdt.internal.launching.environments;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.xml.parsers.DocumentBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.launching.LaunchingPlugin;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstallChangedListener;
import org.eclipse.jdt.launching.IVMInstallType;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.PropertyChangeEvent;
import org.eclipse.jdt.launching.VMStandin;
import org.eclipse.jdt.launching.environments.CompatibleEnvironment;
import org.eclipse.jdt.launching.environments.IAccessRuleParticipant;
import org.eclipse.jdt.launching.environments.IExecutionEnvironment;
import org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager;
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;
/**
* Utility class for execution environments.
*
* @since 3.2
*/
public class EnvironmentsManager implements IExecutionEnvironmentsManager, IVMInstallChangedListener, IPreferenceChangeListener {
/**
* Extension configuration element name.
*/
private static final String ANALYZER_ELEMENT = "analyzer"; //$NON-NLS-1$
/**
* Extension configuration element name.
*/
static final String ENVIRONMENT_ELEMENT = "environment"; //$NON-NLS-1$
/**
* Extension configuration element name.
*/
static final String RULE_PARTICIPANT_ELEMENT = "ruleParticipant"; //$NON-NLS-1$
private static EnvironmentsManager fgManager = null;
/**
* Preference store key for XML storing default environments.
*/
private static final String PREF_DEFAULT_ENVIRONMENTS_XML = "org.eclipse.jdt.launching.PREF_DEFAULT_ENVIRONMENTS_XML"; //$NON-NLS-1$
/**
* List of environments
*/
private TreeSet<IExecutionEnvironment> fEnvironments = null;
/**
* List of access rule participants
*/
private Set<AccessRuleParticipant> fRuleParticipants = null;
/**
* Map of environments keyed by id
*/
private Map<String, IExecutionEnvironment> fEnvironmentsMap = null;
/**
* Map of analyzers keyed by id
*/
private Map<String, Analyzer> fAnalyzers = null;
/**
* <code>true</code> while updating the default settings preferences
*/
private boolean fIsUpdatingDefaults = false;
/**
* Whether compatible environments have been initialized
*/
private boolean fInitializedCompatibilities = false;
/**
* XML attribute
*/
private static final String VM_ID = "vmId"; //$NON-NLS-1$
/**
* XML attribute
*/
private static final String ENVIRONMENT_ID = "environmentId"; //$NON-NLS-1$
/**
* XML element
*/
private static final String DEFAULT_ENVIRONMENT = "defaultEnvironment"; //$NON-NLS-1$
/**
* XML document
*/
private static final String DEFAULT_ENVIRONMENTS = "defaultEnvironments"; //$NON-NLS-1$
/**
* Returns the singleton environments manager.
*
* @return environments manager
*/
public static EnvironmentsManager getDefault() {
if (fgManager == null) {
fgManager = new EnvironmentsManager();
}
return fgManager;
}
/**
* Constructs the new manager.
*/
private EnvironmentsManager() {
JavaRuntime.addVMInstallChangedListener(this);
InstanceScope.INSTANCE.getNode(LaunchingPlugin.ID_PLUGIN).addPreferenceChangeListener(this);
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager#getExecutionEnvironments()
*/
@Override
public synchronized IExecutionEnvironment[] getExecutionEnvironments() {
initializeExtensions();
return fEnvironments.toArray(new IExecutionEnvironment[fEnvironments.size()]);
}
/**
* Returns all access rule participants that are not specific to an execution environment.
*
* @return all access rule participants that are not specific to an execution environment.
* @since 3.3
*/
public synchronized IAccessRuleParticipant[] getAccessRuleParticipants() {
initializeExtensions();
return fRuleParticipants.toArray(new IAccessRuleParticipant[fRuleParticipants.size()]);
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager#getEnvironment(java.lang.String)
*/
@Override
public synchronized IExecutionEnvironment getEnvironment(String id) {
initializeExtensions();
return fEnvironmentsMap.get(id);
}
/**
* Returns all registered analyzers
*
* @return all registered analyzers
*/
public synchronized Analyzer[] getAnalyzers() {
initializeExtensions();
Collection<Analyzer> collection = fAnalyzers.values();
return collection.toArray(new Analyzer[collection.size()]);
}
private String getExecutionEnvironmentCompliance(IExecutionEnvironment executionEnvironment) {
String desc = executionEnvironment.getId();
if (desc.indexOf(JavaCore.VERSION_16) != -1) {
return JavaCore.VERSION_16;
} else if (desc.indexOf(JavaCore.VERSION_15) != -1) {
return JavaCore.VERSION_15;
} else if (desc.indexOf(JavaCore.VERSION_14) != -1) {
return JavaCore.VERSION_14;
} else if (desc.indexOf(JavaCore.VERSION_13) != -1) {
return JavaCore.VERSION_13;
} else if (desc.indexOf(JavaCore.VERSION_12) != -1) {
return JavaCore.VERSION_12;
} else if (desc.indexOf(JavaCore.VERSION_11) != -1) {
return JavaCore.VERSION_11;
} else if (desc.indexOf(JavaCore.VERSION_10) != -1) {
return JavaCore.VERSION_10;
} else if (desc.indexOf(JavaCore.VERSION_9) != -1) {
return JavaCore.VERSION_9;
} else if (desc.indexOf(JavaCore.VERSION_1_8) != -1) {
return JavaCore.VERSION_1_8;
} else if (desc.indexOf(JavaCore.VERSION_1_7) != -1) {
return JavaCore.VERSION_1_7;
} else if (desc.indexOf(JavaCore.VERSION_1_6) != -1) {
return JavaCore.VERSION_1_6;
} else if (desc.indexOf(JavaCore.VERSION_1_5) != -1) {
return JavaCore.VERSION_1_5;
} else if (desc.indexOf(JavaCore.VERSION_1_4) != -1) {
return JavaCore.VERSION_1_4;
} else if (desc.indexOf(JavaCore.VERSION_1_3) != -1) {
return JavaCore.VERSION_1_3;
} else if (desc.indexOf(JavaCore.VERSION_1_2) != -1) {
return JavaCore.VERSION_1_2;
} else if (desc.indexOf(JavaCore.VERSION_1_1) != -1) {
return JavaCore.VERSION_1_1;
} else if (desc.indexOf("1.0") != -1) { //$NON-NLS-1$
return "1.0"; //$NON-NLS-1$
}
return JavaCore.VERSION_1_3;
}
private synchronized void initializeExtensions() {
if (fEnvironments == null) {
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(LaunchingPlugin.ID_PLUGIN, JavaRuntime.EXTENSION_POINT_EXECUTION_ENVIRONMENTS);
IConfigurationElement[] configs= extensionPoint.getConfigurationElements();
fEnvironments = new TreeSet<>(new Comparator<IExecutionEnvironment>() {
@Override
public int compare(IExecutionEnvironment o1, IExecutionEnvironment o2) {
String compliance1 = getExecutionEnvironmentCompliance(o1);
String compliance2 = getExecutionEnvironmentCompliance(o2);
int result = JavaCore.compareJavaVersions(compliance1, compliance2);
if (result == 0) {
return o1.getId().compareTo(o2.getId());
}
return result;
}
});
fRuleParticipants = new LinkedHashSet<>();
fEnvironmentsMap = new HashMap<>(configs.length);
fAnalyzers = new HashMap<>(configs.length);
for (int i = 0; i < configs.length; i++) {
IConfigurationElement element = configs[i];
String name = element.getName();
switch (name) {
case ENVIRONMENT_ELEMENT:
String id = element.getAttribute("id"); //$NON-NLS-1$
if (id == null) {
LaunchingPlugin.log(NLS.bind("Execution environment must specify \"id\" attribute. Contributed by {0}.", new String[] { //$NON-NLS-1$
element.getContributor().getName() }));
} else {
IExecutionEnvironment env = new ExecutionEnvironment(element);
fEnvironments.add(env);
fEnvironmentsMap.put(id, env);
}
break;
case ANALYZER_ELEMENT:
id = element.getAttribute("id"); //$NON-NLS-1$
if (id == null) {
LaunchingPlugin.log(NLS.bind("Execution environment analyzer must specify \"id\" attribute. Contributed by {0}", new String[] { //$NON-NLS-1$
element.getContributor().getName() }));
} else {
fAnalyzers.put(id, new Analyzer(element));
}
break;
case RULE_PARTICIPANT_ELEMENT:
id = element.getAttribute("id"); //$NON-NLS-1$
if (id == null) {
LaunchingPlugin.log(NLS.bind("Execution environment rule participant must specify \"id\" attribute. Contributed by {0}", new String[] { //$NON-NLS-1$
element.getContributor().getName() }));
} else {
// use a linked hash set to avoid duplicate rule participants
fRuleParticipants.add(new AccessRuleParticipant(element));
}
break;
default:
break;
}
}
}
}
/**
* Initializes compatibility settings.
*/
void initializeCompatibilities() {
IVMInstallType[] installTypes = JavaRuntime.getVMInstallTypes();
synchronized (this) {
if (!fInitializedCompatibilities) {
fInitializedCompatibilities = true;
for (int i = 0; i < installTypes.length; i++) {
IVMInstallType type = installTypes[i];
IVMInstall[] installs = type.getVMInstalls();
for (int j = 0; j < installs.length; j++) {
IVMInstall install = installs[j];
// TODO: progress reporting?
analyze(install, new NullProgressMonitor());
}
}
initializeDefaultVMs();
}
}
}
/**
* Reads persisted default VMs from the preference store
*/
private synchronized void initializeDefaultVMs() {
String xml = InstanceScope.INSTANCE.getNode(LaunchingPlugin.ID_PLUGIN).get(PREF_DEFAULT_ENVIRONMENTS_XML, ""); //$NON-NLS-1$
try {
if (xml.length() > 0) {
DocumentBuilder parser = LaunchingPlugin.getParser();
Document document = parser.parse(new ByteArrayInputStream(xml.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(DEFAULT_ENVIRONMENT)) {
String envId = element.getAttribute(ENVIRONMENT_ID);
String vmId = element.getAttribute(VM_ID);
ExecutionEnvironment environment = (ExecutionEnvironment) getEnvironment(envId);
if (environment != null) {
IVMInstall vm = JavaRuntime.getVMFromCompositeId(vmId);
if (vm != null) {
environment.initDefaultVM(vm);
}
}
}
}
}
}
} catch (CoreException e) {
LaunchingPlugin.log(e);
} catch (SAXException e) {
LaunchingPlugin.log(e);
} catch (IOException e) {
LaunchingPlugin.log(e);
}
}
/**
* Returns an XML description of default VMs per environment. Returns
* an empty string when there are none.
* @return an XML description of default VMs per environment. Returns
* an empty string when there are none.
*/
private String getDefatulVMsAsXML() {
int count = 0;
try {
Document doc = DebugPlugin.newDocument();
Element envs = doc.createElement(DEFAULT_ENVIRONMENTS);
doc.appendChild(envs);
IExecutionEnvironment[] environments = getExecutionEnvironments();
for (int i = 0; i < environments.length; i++) {
IExecutionEnvironment env = environments[i];
IVMInstall vm = env.getDefaultVM();
if (vm != null) {
count++;
Element element = doc.createElement(DEFAULT_ENVIRONMENT);
element.setAttribute(ENVIRONMENT_ID, env.getId());
element.setAttribute(VM_ID, JavaRuntime.getCompositeIdFromVM(vm));
envs.appendChild(element);
}
}
if (count > 0) {
return DebugPlugin.serializeDocument(doc);
}
} catch (CoreException e) {
LaunchingPlugin.log(e);
}
return ""; //$NON-NLS-1$
}
/**
* Analyzes compatible execution environments for the given VM install.
*
* @param vm the {@link IVMInstall} to find environments for
* @param monitor a progress monitor or <code>null</code>
*/
private void analyze(IVMInstall vm, IProgressMonitor monitor) {
Analyzer[] analyzers = getAnalyzers();
for (int i = 0; i < analyzers.length; i++) {
Analyzer analyzer = analyzers[i];
try {
CompatibleEnvironment[] environments = analyzer.analyze(vm, monitor);
for (int j = 0; j < environments.length; j++) {
CompatibleEnvironment compatibleEnvironment = environments[j];
ExecutionEnvironment environment = (ExecutionEnvironment) compatibleEnvironment.getCompatibleEnvironment();
environment.add(vm, compatibleEnvironment.isStrictlyCompatbile());
}
} catch (CoreException e) {
LaunchingPlugin.log(e);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.IVMInstallChangedListener#defaultVMInstallChanged(org.eclipse.jdt.launching.IVMInstall, org.eclipse.jdt.launching.IVMInstall)
*/
@Override
public void defaultVMInstallChanged(IVMInstall previous, IVMInstall current) {
// nothing
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmChanged(org.eclipse.jdt.launching.PropertyChangeEvent)
*/
@Override
public synchronized void vmChanged(PropertyChangeEvent event) {
IVMInstall vm = (IVMInstall) event.getSource();
if (vm instanceof VMStandin) {
return;
}
vmRemoved(vm);
vmAdded(vm);
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmAdded(org.eclipse.jdt.launching.IVMInstall)
*/
@Override
public synchronized void vmAdded(IVMInstall vm) {
// TODO: progress reporting?
if (vm instanceof VMStandin) {
return;
}
analyze(vm, new NullProgressMonitor());
}
/* (non-Javadoc)
* @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmRemoved(org.eclipse.jdt.launching.IVMInstall)
*/
@Override
public synchronized void vmRemoved(IVMInstall vm) {
if (vm instanceof VMStandin) {
return;
}
IExecutionEnvironment[] environments = getExecutionEnvironments();
for (int i = 0; i < environments.length; i++) {
ExecutionEnvironment environment = (ExecutionEnvironment) environments[i];
environment.remove(vm);
}
}
synchronized void updateDefaultVMs() {
try {
fIsUpdatingDefaults = true;
InstanceScope.INSTANCE.getNode(LaunchingPlugin.ID_PLUGIN).put(PREF_DEFAULT_ENVIRONMENTS_XML, getDefatulVMsAsXML());
} finally {
fIsUpdatingDefaults = false;
}
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent)
*/
@Override
public void preferenceChange(PreferenceChangeEvent event) {
// don't respond to myself
if (fIsUpdatingDefaults) {
return;
}
if (event.getKey().equals(PREF_DEFAULT_ENVIRONMENTS_XML)) {
initializeDefaultVMs();
}
}
}