blob: b313c48f1b53050dbc1e1fca8c2773d9ed52db9c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2018 Sonatype, Inc. and others
* All rights reserved. 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:
* Sonatype, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.m2e.jdt.internal.launch;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
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.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.StandardClasspathProvider;
import org.eclipse.osgi.util.NLS;
import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.project.IMavenProjectRegistry;
import org.eclipse.m2e.core.project.ResolverConfiguration;
import org.eclipse.m2e.jdt.IClassifierClasspathProvider;
import org.eclipse.m2e.jdt.IClasspathManager;
import org.eclipse.m2e.jdt.IMavenClassifierManager;
import org.eclipse.m2e.jdt.MavenJdtPlugin;
import org.eclipse.m2e.jdt.internal.MavenClasspathHelpers;
import org.eclipse.m2e.jdt.internal.Messages;
import org.eclipse.m2e.jdt.internal.ModuleSupport;
public class MavenRuntimeClasspathProvider extends StandardClasspathProvider {
public static final String MAVEN_SOURCEPATH_PROVIDER = "org.eclipse.m2e.launchconfig.sourcepathProvider"; //$NON-NLS-1$
public static final String MAVEN_CLASSPATH_PROVIDER = "org.eclipse.m2e.launchconfig.classpathProvider"; //$NON-NLS-1$
private static final String THIS_PROJECT_CLASSIFIER = ""; //$NON-NLS-1$
public static final String JDT_JUNIT_TEST = "org.eclipse.jdt.junit.launchconfig"; //$NON-NLS-1$
public static final String JDT_JAVA_APPLICATION = "org.eclipse.jdt.launching.localJavaApplication"; //$NON-NLS-1$
public static final String JDT_TESTNG_TEST = "org.testng.eclipse.launchconfig"; //$NON-NLS-1$
private static final Set<String> supportedTypes = new HashSet<String>();
static {
// not exactly nice, but works with eclipse 3.2, 3.3 and 3.4M3
supportedTypes.add(MavenRuntimeClasspathProvider.JDT_JAVA_APPLICATION);
supportedTypes.add(MavenRuntimeClasspathProvider.JDT_JUNIT_TEST);
supportedTypes.add(MavenRuntimeClasspathProvider.JDT_TESTNG_TEST);
}
IMavenProjectRegistry projectManager = MavenPlugin.getMavenProjectRegistry();
public IRuntimeClasspathEntry[] computeUnresolvedClasspath(final ILaunchConfiguration configuration)
throws CoreException {
boolean isModular = ModuleSupport.isModularConfiguration(configuration);
boolean useDefault = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, true);
if(useDefault) {
IJavaProject javaProject = JavaRuntime.getJavaProject(configuration);
IRuntimeClasspathEntry jreEntry = JavaRuntime.computeJREEntry(configuration);
IRuntimeClasspathEntry projectEntry;
if(isModular) {
projectEntry = ModuleSupport.newModularProjectRuntimeClasspathEntry(javaProject);
} else {
projectEntry = JavaRuntime.newProjectRuntimeClasspathEntry(javaProject);
}
IRuntimeClasspathEntry mavenEntry = JavaRuntime.newRuntimeContainerClasspathEntry(
new Path(IClasspathManager.CONTAINER_ID), IRuntimeClasspathEntry.USER_CLASSES);
if(jreEntry == null) {
return new IRuntimeClasspathEntry[] {projectEntry, mavenEntry};
}
return new IRuntimeClasspathEntry[] {jreEntry, projectEntry, mavenEntry};
}
// recover persisted classpath
if(isModular) {
IRuntimeClasspathEntry[] runtimeModulePaths = recoverRuntimePath(configuration,
IJavaLaunchConfigurationConstants.ATTR_MODULEPATH);
IRuntimeClasspathEntry[] runtimeClasspaths = recoverRuntimePath(configuration,
IJavaLaunchConfigurationConstants.ATTR_CLASSPATH);
IRuntimeClasspathEntry[] result = Arrays.copyOf(runtimeModulePaths,
runtimeModulePaths.length + runtimeClasspaths.length);
System.arraycopy(runtimeClasspaths, 0, result, runtimeModulePaths.length, runtimeClasspaths.length);
return result;
}
return recoverRuntimePath(configuration, IJavaLaunchConfigurationConstants.ATTR_CLASSPATH);
}
public IRuntimeClasspathEntry[] resolveClasspath(final IRuntimeClasspathEntry[] entries,
final ILaunchConfiguration configuration) throws CoreException {
IProgressMonitor monitor = new NullProgressMonitor(); // XXX
return MavenPlugin.getMaven().execute((context, monitor1) -> resolveClasspath0(entries, configuration, monitor1), monitor);
}
IRuntimeClasspathEntry[] resolveClasspath0(IRuntimeClasspathEntry[] entries, ILaunchConfiguration configuration,
IProgressMonitor monitor) throws CoreException {
IJavaProject javaProject = JavaRuntime.getJavaProject(configuration);
boolean isModularConfiguration = JavaRuntime.isModularConfiguration(configuration);
int scope = getArtifactScope(configuration);
Set<IRuntimeClasspathEntry> all = new LinkedHashSet<IRuntimeClasspathEntry>(entries.length);
for(IRuntimeClasspathEntry entry : entries) {
if(entry.getType() == IRuntimeClasspathEntry.CONTAINER
&& MavenClasspathHelpers.isMaven2ClasspathContainer(entry.getPath())) {
addMavenClasspathEntries(all, entry, configuration, scope, monitor, isModularConfiguration);
} else if(entry.getType() == IRuntimeClasspathEntry.PROJECT) {
if(javaProject.getPath().equals(entry.getPath())) {
addProjectEntries(all, entry.getPath(), scope, THIS_PROJECT_CLASSIFIER, configuration, monitor,
ModuleSupport.determineClasspathPropertyForMainProject(isModularConfiguration, javaProject));
} else {
addStandardClasspathEntries(all, entry, configuration);
}
} else {
addStandardClasspathEntries(all, entry, configuration);
}
}
return all.toArray(new IRuntimeClasspathEntry[all.size()]);
}
private void addStandardClasspathEntries(Set<IRuntimeClasspathEntry> all, IRuntimeClasspathEntry entry,
ILaunchConfiguration configuration) throws CoreException {
IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveRuntimeClasspathEntry(entry, configuration);
for(int j = 0; j < resolved.length; j++ ) {
all.add(resolved[j]);
}
}
private void addMavenClasspathEntries(Set<IRuntimeClasspathEntry> resolved,
IRuntimeClasspathEntry runtimeClasspathEntry, ILaunchConfiguration configuration, int scope,
IProgressMonitor monitor, boolean isModularConfiguration) throws CoreException {
IJavaProject javaProject = JavaRuntime.getJavaProject(configuration);
MavenJdtPlugin plugin = MavenJdtPlugin.getDefault();
IClasspathManager buildpathManager = plugin.getBuildpathManager();
IClasspathEntry[] cp = buildpathManager.getClasspath(javaProject.getProject(), scope, false, monitor);
for(IClasspathEntry entry : cp) {
int classpathProperty = isModularConfiguration ? ModuleSupport.determineModularClasspathProperty(entry)
: IRuntimeClasspathEntry.USER_CLASSES;
switch(entry.getEntryKind()) {
case IClasspathEntry.CPE_PROJECT:
addProjectEntries(resolved, entry.getPath(), scope, getArtifactClassifier(entry), configuration, monitor,
classpathProperty);
break;
case IClasspathEntry.CPE_LIBRARY:
resolved.add(JavaRuntime.newArchiveRuntimeClasspathEntry(entry.getPath(), classpathProperty));
break;
// case IClasspathEntry.CPE_SOURCE:
// resolved.add(newSourceClasspathEntry(javaProject, cp[i]));
// break;
}
}
}
protected int getArtifactScope(ILaunchConfiguration configuration) throws CoreException {
String typeid = configuration.getType().getAttribute("id"); //$NON-NLS-1$
if(JDT_JAVA_APPLICATION.equals(typeid)) {
IResource[] resources = configuration.getMappedResources();
// MNGECLIPSE-530: NPE starting openarchitecture workflow
if(resources == null || resources.length == 0) {
return IClasspathManager.CLASSPATH_RUNTIME;
}
// ECLIPSE-33: applications from test sources should use test scope
final Set<IPath> testSources = new HashSet<IPath>();
IJavaProject javaProject = JavaRuntime.getJavaProject(configuration);
IMavenProjectFacade facade = projectManager.create(javaProject.getProject(), new NullProgressMonitor());
if(facade == null) {
return IClasspathManager.CLASSPATH_RUNTIME;
}
testSources.addAll(Arrays.asList(facade.getTestCompileSourceLocations()));
//If a test folder was added by a plugin (hello build-helper-maven-plugin) to the project model,
//facade.getTestCompileSourceLocations() would miss it.
//So as a complement, we add all Eclipse folders having the "test" attribute for that project
//XXX The following most likely makes calling facade.getTestCompileSourceLocations() redundant
//(we'd prolly have some issues if compile source locations didn't get that "test" flag).
testSources.addAll(getEclipseTestSources(javaProject));
for(int i = 0; i < resources.length; i++ ) {
for(IPath testPath : testSources) {
if(testPath.isPrefixOf(resources[i].getProjectRelativePath())) {
return IClasspathManager.CLASSPATH_TEST;
}
}
}
return IClasspathManager.CLASSPATH_RUNTIME;
} else if(JDT_JUNIT_TEST.equals(typeid) || JDT_TESTNG_TEST.equals(typeid)) {
return IClasspathManager.CLASSPATH_TEST;
} else {
throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, 0,
NLS.bind(Messages.MavenRuntimeClasspathProvider_error_unsupported, typeid), null));
}
}
private Set<IPath> getEclipseTestSources(IJavaProject javaProject) throws JavaModelException {
IClasspathEntry[] cpes = javaProject.getRawClasspath();
Set<IPath> eclipseTestSources = Stream.of(cpes).filter(MavenClasspathHelpers::isTestSource)
.map(cpe -> cpe.getPath().makeRelativeTo(javaProject.getPath())).collect(Collectors.toSet());
return eclipseTestSources;
}
protected void addProjectEntries(Set<IRuntimeClasspathEntry> resolved, IPath path, int scope, String classifier,
ILaunchConfiguration launchConfiguration, final IProgressMonitor monitor, int classpathProperty)
throws CoreException {
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IProject project = root.getProject(path.segment(0));
IMavenProjectFacade projectFacade = projectManager.create(project, monitor);
if(projectFacade == null) {
return;
}
ResolverConfiguration configuration = projectFacade.getResolverConfiguration();
if(configuration == null) {
return;
}
IJavaProject javaProject = JavaCore.create(project);
boolean projectResolved = false;
for(IClasspathEntry entry : javaProject.getRawClasspath()) {
IRuntimeClasspathEntry rce = null;
switch(entry.getEntryKind()) {
case IClasspathEntry.CPE_SOURCE:
if(!projectResolved) {
IMavenClassifierManager mavenClassifierManager = MavenJdtPlugin.getDefault().getMavenClassifierManager();
IClassifierClasspathProvider classifierClasspathProvider = mavenClassifierManager
.getClassifierClasspathProvider(projectFacade, classifier);
if(IClasspathManager.CLASSPATH_TEST == scope) {
classifierClasspathProvider.setTestClasspath(resolved, projectFacade, monitor, classpathProperty);
} else {
classifierClasspathProvider.setRuntimeClasspath(resolved, projectFacade, monitor, classpathProperty);
}
projectResolved = true;
}
break;
case IClasspathEntry.CPE_CONTAINER:
IClasspathContainer container = JavaCore.getClasspathContainer(entry.getPath(), javaProject);
if(container != null && !MavenClasspathHelpers.isMaven2ClasspathContainer(entry.getPath())) {
switch(container.getKind()) {
case IClasspathContainer.K_APPLICATION:
rce = JavaRuntime.newRuntimeContainerClasspathEntry(container.getPath(),
IRuntimeClasspathEntry.USER_CLASSES, javaProject);
break;
// case IClasspathContainer.K_DEFAULT_SYSTEM:
// unresolved.add(JavaRuntime.newRuntimeContainerClasspathEntry(container.getPath(), IRuntimeClasspathEntry.STANDARD_CLASSES, javaProject));
// break;
// case IClasspathContainer.K_SYSTEM:
// unresolved.add(JavaRuntime.newRuntimeContainerClasspathEntry(container.getPath(), IRuntimeClasspathEntry.BOOTSTRAP_CLASSES, javaProject));
// break;
}
}
break;
case IClasspathEntry.CPE_LIBRARY:
rce = JavaRuntime.newArchiveRuntimeClasspathEntry(entry.getPath(),
classpathProperty == IRuntimeClasspathEntry.USER_CLASSES ? IRuntimeClasspathEntry.USER_CLASSES
: ModuleSupport.determineModularClasspathProperty(entry));
break;
case IClasspathEntry.CPE_VARIABLE:
if(!JavaRuntime.JRELIB_VARIABLE.equals(entry.getPath().segment(0))) {
rce = JavaRuntime.newVariableRuntimeClasspathEntry(entry.getPath());
}
break;
case IClasspathEntry.CPE_PROJECT:
IProject res = root.getProject(entry.getPath().segment(0));
if(res != null) {
IJavaProject otherProject = JavaCore.create(res);
if(otherProject != null) {
rce = JavaRuntime.newDefaultProjectClasspathEntry(otherProject);
}
}
break;
default:
break;
}
if(rce != null) {
addStandardClasspathEntries(resolved, rce, launchConfiguration);
}
}
}
public static boolean isSupportedType(String id) {
return supportedTypes.contains(id);
}
public static void enable(ILaunchConfiguration config) throws CoreException {
if(config instanceof ILaunchConfigurationWorkingCopy) {
enable((ILaunchConfigurationWorkingCopy) config);
} else {
ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy();
enable(wc);
wc.doSave();
}
}
private static void enable(ILaunchConfigurationWorkingCopy wc) {
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER, MAVEN_CLASSPATH_PROVIDER);
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_SOURCE_PATH_PROVIDER, MAVEN_SOURCEPATH_PROVIDER);
}
public static void disable(ILaunchConfiguration config) throws CoreException {
ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy();
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER, (String) null);
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_SOURCE_PATH_PROVIDER, (String) null);
wc.doSave();
}
private static String getArtifactClassifier(IClasspathEntry entry) {
IClasspathAttribute[] attributes = entry.getExtraAttributes();
for(IClasspathAttribute attribute : attributes) {
if(IClasspathManager.CLASSIFIER_ATTRIBUTE.equals(attribute.getName())) {
return attribute.getValue();
}
}
return null;
}
public static void enable(IProject project) throws CoreException {
for(ILaunchConfiguration config : getLaunchConfiguration(project)) {
if(isSupportedType(config.getType().getIdentifier())) {
enable(config);
}
}
}
public static void disable(IProject project) throws CoreException {
for(ILaunchConfiguration config : getLaunchConfiguration(project)) {
if(isSupportedType(config.getType().getIdentifier())) {
disable(config);
}
}
}
private static List<ILaunchConfiguration> getLaunchConfiguration(IProject project) throws CoreException {
ArrayList<ILaunchConfiguration> result = new ArrayList<ILaunchConfiguration>();
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfiguration[] configurations = launchManager.getLaunchConfigurations();
for(ILaunchConfiguration config : configurations) {
String projectName = config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null);
if(project.getName().equals(projectName)) {
result.add(config);
}
}
return result;
}
}