diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 8eee66be4d..4c4ea06842 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -2,6 +2,8 @@
## 2.2.0
+* 📅 Release Date: _expected_ end of Februar 2023
+
### Mojos without a mapping are now executed by default in incremental builds
Before it was necessary to explicitly configure a mapping or there is a connector or the plugin itself contains mappings for a mojo to participate in the incremental maven build.
@@ -23,9 +25,21 @@ The property "true" and the "skip" property i
account by M2E in the sense that, if enabled, M2E ignores the corresponding folder, which no longer appear in the Package Explorer as "Java" folders but as standard folders.
This allows, depending on the need (especially compilation time), to either not compile tests or not copy test resources.
-In general it is not recomended to use the mentioned properties but to use `-DskipTests` instead:
+In general it is not recommended to use the mentioned properties but to use `-DskipTests` instead:
https://maven.apache.org/surefire/maven-surefire-plugin/examples/skipping-tests.html
+
+### Configuration of Maven Execution JRE
+
+In the past the project's build JRE was also used by default to execute Maven itself.
+Now the default Java version for executing Maven is determined from the configuration of the `maven-enforcer-plugin` rule [`requireJavaVersion`](https://maven.apache.org/enforcer/enforcer-rules/requireJavaVersion.html) when creating or updating the Maven configuration. This value is no longer considered for configuring the project's build JRE.
+In case this plugin configuration is not found one falls back to either project's build JRE or workspace default JRE.
+
+For each Maven build configuration you can overwrite the default execution JRE in the Maven Launch configuration's JRE tab:
+
+![Maven Launch Configuration JRE Tab](https://user-images.githubusercontent.com/185025/208966517-7d847058-23b9-4e2e-8b1a-7a86df4836bd.png)
+
+
## 2.1.0
* 📅 Release Date: November 24th 2022
diff --git a/org.eclipse.m2e.core.tests/META-INF/MANIFEST.MF b/org.eclipse.m2e.core.tests/META-INF/MANIFEST.MF
index 6b14c50a61..60603f825e 100644
--- a/org.eclipse.m2e.core.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.m2e.core.tests/META-INF/MANIFEST.MF
@@ -7,11 +7,14 @@ Bundle-Version: 2.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-Vendor: Eclipse.org - m2e
Require-Bundle: org.eclipse.m2e.tests.common,
- org.junit,
- org.eclipse.core.resources,
org.eclipse.core.runtime,
- org.eclipse.m2e.maven.runtime
-Import-Package: javax.annotation;version="1.2.0",
- org.apache.commons.io
+ org.eclipse.m2e.launching,
+ org.eclipse.jdt.launching,
+ org.eclipse.debug.core,
+ org.eclipse.jdt.core
+Import-Package: org.apache.commons.io,
+ org.junit,
+ org.mockito,
+ org.mockito.stubbing
Eclipse-BundleShape: dir
Automatic-Module-Name: org.eclipse.m2e.core.tests
diff --git a/org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithVersion/pom.xml b/org.eclipse.m2e.core.tests/resources/projects/enforcerSettingsWithVersion/pom.xml
similarity index 100%
rename from org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithVersion/pom.xml
rename to org.eclipse.m2e.core.tests/resources/projects/enforcerSettingsWithVersion/pom.xml
diff --git a/org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithVersionRange/pom.xml b/org.eclipse.m2e.core.tests/resources/projects/enforcerSettingsWithVersionRange/pom.xml
similarity index 96%
rename from org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithVersionRange/pom.xml
rename to org.eclipse.m2e.core.tests/resources/projects/enforcerSettingsWithVersionRange/pom.xml
index 888ab0d305..ce8f8bffaa 100644
--- a/org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithVersionRange/pom.xml
+++ b/org.eclipse.m2e.core.tests/resources/projects/enforcerSettingsWithVersionRange/pom.xml
@@ -35,7 +35,7 @@
- [11.0.10,16)
+ [11.0.6,13)
diff --git a/org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithoutRequiredJavaVersion/pom.xml b/org.eclipse.m2e.core.tests/resources/projects/enforcerSettingsWithoutRequiredJavaVersion/pom.xml
similarity index 100%
rename from org.eclipse.m2e.jdt.tests/projects/enforcerSettingsWithoutRequiredJavaVersion/pom.xml
rename to org.eclipse.m2e.core.tests/resources/projects/enforcerSettingsWithoutRequiredJavaVersion/pom.xml
diff --git a/org.eclipse.m2e.core.tests/src/org/eclipse/m2e/internal/launch/MavenLaunchDelegateTest.java b/org.eclipse.m2e.core.tests/src/org/eclipse/m2e/internal/launch/MavenLaunchDelegateTest.java
new file mode 100644
index 0000000000..f1b4162150
--- /dev/null
+++ b/org.eclipse.m2e.core.tests/src/org/eclipse/m2e/internal/launch/MavenLaunchDelegateTest.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 2022, 2023 Hannes Wellmann 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:
+ * Hannes Wellmann - initial API and implementation
+ * Konrad Windszus - Add tests for required java runtime version implied by enforcer rule
+ *******************************************************************************/
+
+package org.eclipse.m2e.internal.launch;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.util.List;
+
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jdt.internal.launching.StandardVMType;
+import org.eclipse.jdt.launching.AbstractVMInstall;
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+import org.eclipse.jdt.launching.IVMInstall;
+import org.eclipse.jdt.launching.IVMInstallType;
+import org.eclipse.jdt.launching.JavaRuntime;
+import org.eclipse.m2e.actions.MavenLaunchConstants;
+import org.eclipse.m2e.tests.common.AbstractMavenProjectTestCase;
+import org.junit.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+@SuppressWarnings("restriction")
+public class MavenLaunchDelegateTest extends AbstractMavenProjectTestCase {
+
+ // If there is a dedicated locations for m2e.launching tests one day, move it
+ // there.
+
+ private static final String DEFAULT_VM = "defaultVM";
+ private static final List AVAILABLE_VM_VERSIONS = List.of("17.0.4", "11.0.7", "13.0.5", "11.0.1", "1.8.0");
+
+ @Test
+ public void testGetBestMatchingVM_majorOnly() throws InvalidVersionSpecificationException {
+ try (var mock = mockJavaRuntime()) {
+ assertEquals("11.0.7", MavenLaunchDelegate.getBestMatchingVM("11").getId());
+ }
+ }
+
+ @Test
+ public void testGetBestMatchingVM_rangeWithOnlyMajorLowerBound() throws InvalidVersionSpecificationException {
+ try (var mock = mockJavaRuntime()) {
+ assertEquals("11.0.7", MavenLaunchDelegate.getBestMatchingVM("[11,)").getId());
+ }
+ }
+
+ @Test
+ public void testGetBestMatchingVM_9versionRange() throws InvalidVersionSpecificationException {
+ try (var mock = mockJavaRuntime()) {
+ assertEquals("17.0.4", MavenLaunchDelegate.getBestMatchingVM("[11,18)").getId());
+ }
+ }
+
+ @Test
+ public void testGetBestMatchingVM_1XversionRange() throws InvalidVersionSpecificationException {
+ try (var mock = mockJavaRuntime()) {
+ assertEquals("1.8.0", MavenLaunchDelegate.getBestMatchingVM("[1.8,9)").getId());
+ }
+ }
+
+ @Test
+ public void testRequiredJavaVersionFromEnforcerRule_Version() throws Exception {
+ IProject project = importProject("resources/projects/enforcerSettingsWithVersion/pom.xml");
+ assertRequiredJavaBuildVersion(project, "13.0.3", "13.0.5");
+ }
+
+ @Test
+ public void testRequiredJavaVersionFromEnforcerRule_VersionRange() throws Exception {
+ IProject project = importProject("resources/projects/enforcerSettingsWithVersionRange/pom.xml");
+ assertRequiredJavaBuildVersion(project, "[11.0.6,13)", "11.0.7");
+ }
+
+ @Test
+ public void testRequiredJavaVersionFromEnforcerRule_NoVersionRange() throws Exception {
+ IProject project = importProject("resources/projects/enforcerSettingsWithoutRequiredJavaVersion/pom.xml");
+ assertRequiredJavaBuildVersion(project, null, DEFAULT_VM);
+ }
+
+ private void assertRequiredJavaBuildVersion(IProject project, String expectedVersionRange, String expectedVMVersion)
+ throws Exception {
+
+ waitForJobsToComplete();
+
+ File pomFile = project.getLocation().toFile();
+
+ assertEquals(expectedVersionRange, MavenLaunchDelegate.readEnforcedJavaVersion(pomFile, monitor));
+
+ String pomDir = "${workspace_loc:/" + project.getName() + "}";
+
+ try (var mock = mockJavaRuntime()) {
+ ILaunchConfigurationWorkingCopy config = createMavenLaunchConfig(pomDir);
+ assertEquals(expectedVMVersion, new MavenLaunchDelegate().getVMInstall(config).getId());
+
+ ILaunchConfigurationWorkingCopy config2 = createMavenLaunchConfig(pomDir);
+ config2.setAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH,
+ "org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/");
+ assertEquals(DEFAULT_VM, new MavenLaunchDelegate().getVMInstall(config2).getId());
+ // When a JRE_CONTAINER_PATH is set, getVMInstall hands over to
+ // JavaRuntime.computeVMInstall(), which is mocked in this test to always return
+ // the defaultVM.
+ }
+ }
+
+ private static MockedStatic mockJavaRuntime() {
+ IVMInstall defaultVM = Mockito.mock(IVMInstall.class);
+ Mockito.when(defaultVM.getId()).thenReturn(DEFAULT_VM);
+
+ IVMInstallType standardVMType = Mockito.mock(StandardVMType.class, Mockito.CALLS_REAL_METHODS);
+ IVMInstall[] installs = AVAILABLE_VM_VERSIONS.stream().map(version -> {
+ AbstractVMInstall vm = Mockito.mock(AbstractVMInstall.class, Mockito.CALLS_REAL_METHODS);
+ when(vm.getId()).thenReturn(version);
+ when(vm.getJavaVersion()).thenReturn(version);
+ when(vm.getVMInstallType()).thenReturn(standardVMType);
+ when(vm.getName()).thenReturn("JDK " + version);
+ return vm;
+ }).toArray(IVMInstall[]::new);
+ Mockito.doReturn(installs).when(standardVMType).getVMInstalls();
+
+ MockedStatic javaRuntimeMock = Mockito.mockStatic(JavaRuntime.class, Mockito.CALLS_REAL_METHODS);
+ javaRuntimeMock.when(() -> JavaRuntime.getVMInstallTypes()).thenReturn(new IVMInstallType[] { standardVMType });
+ javaRuntimeMock.when(() -> JavaRuntime.computeVMInstall(Mockito.any())).thenReturn(defaultVM);
+ return javaRuntimeMock;
+ }
+
+ private static ILaunchConfigurationWorkingCopy createMavenLaunchConfig(String pomDir) throws CoreException {
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ String name = launchManager.generateLaunchConfigurationName("RequiredJavaVersionFromEnforcerRuleTest");
+ ILaunchConfigurationWorkingCopy config = launchManager
+ .getLaunchConfigurationType(MavenLaunchConstants.LAUNCH_CONFIGURATION_TYPE_ID).newInstance(null, name);
+ config.setAttribute(ILaunchManager.ATTR_PRIVATE, true);
+ config.setAttribute(MavenLaunchConstants.ATTR_POM_DIR, pomDir);
+ return config;
+ }
+
+}
diff --git a/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/JavaConfigurationFromEnforcerTest.java b/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/JavaConfigurationFromEnforcerTest.java
deleted file mode 100644
index 704103c327..0000000000
--- a/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/JavaConfigurationFromEnforcerTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2022, 2022 Hannes Wellmann 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:
- * Hannes Wellmann - initial API and implementation
- *******************************************************************************/
-
-package org.eclipse.m2e.jdt.tests;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.runtime.IPath;
-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.m2e.tests.common.AbstractMavenProjectTestCase;
-import org.junit.Test;
-
-public class JavaConfigurationFromEnforcerTest extends AbstractMavenProjectTestCase {
- private static final String JRE_CONTAINER_PREFIX = "org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/";
-
- @Test
- public void testEnforcer_Version() throws Exception {
- IProject project = importProject("projects/enforcerSettingsWithVersion/pom.xml");
- waitForJobsToComplete();
- IJavaProject jproject = JavaCore.create(project);
- assertEquals("1.8", jproject.getOption(JavaCore.COMPILER_SOURCE, false));
- assertEquals("1.8", jproject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, false));
- assertEquals(List.of("JavaSE-13"), getJREContainerVMType(jproject));
- }
-
- @Test
- public void testEnforcer_VersionRange() throws Exception {
- IProject project = importProject("projects/enforcerSettingsWithVersionRange/pom.xml");
- waitForJobsToComplete();
- IJavaProject jproject = JavaCore.create(project);
- assertEquals("1.8", jproject.getOption(JavaCore.COMPILER_SOURCE, false));
- assertEquals("1.8", jproject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, false));
- assertEquals(List.of("JavaSE-11"), getJREContainerVMType(jproject));
- }
-
- @Test
- public void testEnforcer_NoVersionRange() throws Exception {
- IProject project = importProject("projects/enforcerSettingsWithoutRequiredJavaVersion/pom.xml");
- waitForJobsToComplete();
- IJavaProject jproject = JavaCore.create(project);
- assertEquals("1.8", jproject.getOption(JavaCore.COMPILER_SOURCE, false));
- assertEquals("1.8", jproject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, false));
- assertEquals(List.of("JavaSE-1.8"), getJREContainerVMType(jproject));
- }
-
-
- private static List getJREContainerVMType(IJavaProject jproject) throws JavaModelException {
- return Arrays.stream(jproject.getRawClasspath())
- .filter(cp -> cp.getEntryKind() == IClasspathEntry.CPE_CONTAINER).map(IClasspathEntry::getPath)
- .map(IPath::toString).filter(p -> p.startsWith(JRE_CONTAINER_PREFIX))
- .map(p -> p.substring(JRE_CONTAINER_PREFIX.length())).toList();
- }
-}
diff --git a/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/messages.properties b/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/messages.properties
index 6839b71be5..c9b9db21db 100644
--- a/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/messages.properties
+++ b/org.eclipse.m2e.jdt.ui/src/org/eclipse/m2e/jdt/ui/internal/messages.properties
@@ -5,5 +5,5 @@ MavenClasspathContainerPage_title=Maven Dependencies
MavenPreferencePage_executionEnvironmentJreLink=See Execution Environments for the JREs used for specific execution environments.
MavenPreferencePage_workspaceDefaultJreLink=See Installed JREs for the default workspace JRE.
MavenPreferencePage_jreSystemLibraryVersion=JRE System Library Version
-MavenPreferencePage_useExecutionEnvironment=Use execution environment based on maven-enforcer-plugin/maven-compiler-plugin configuration
+MavenPreferencePage_useExecutionEnvironment=Use execution environment based on maven-compiler-plugin configuration
MavenPreferencePage_useWorkspaceDefault=Use workspace default
\ No newline at end of file
diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java
index 229e42570a..049b7a7860 100644
--- a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java
+++ b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2019 Sonatype, Inc. and others.
+ * Copyright (c) 2008, 2023 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
@@ -21,7 +21,6 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -44,18 +43,12 @@
import org.eclipse.jdt.launching.environments.IExecutionEnvironment;
import org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager;
-import org.apache.maven.artifact.versioning.ArtifactVersion;
-import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
-import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
-import org.apache.maven.artifact.versioning.Restriction;
-import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.internal.M2EUtils;
-import org.eclipse.m2e.core.internal.embedder.MavenImpl;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.project.IProjectConfigurationManager;
import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator;
@@ -89,12 +82,6 @@ public abstract class AbstractJavaProjectConfigurator extends AbstractProjectCon
public static final String COMPILER_PLUGIN_GROUP_ID = "org.apache.maven.plugins";
- private static final String GOAL_ENFORCE = "enforce"; //$NON-NLS-1$
-
- public static final String ENFORCER_PLUGIN_ARTIFACT_ID = "maven-enforcer-plugin"; //$NON-NLS-1$
-
- public static final String ENFORCER_PLUGIN_GROUP_ID = "org.apache.maven.plugins"; //$NON-NLS-1$
-
protected static final List RELEASES;
protected static final List SOURCES;
@@ -171,8 +158,7 @@ public void configure(ProjectConfigurationRequest request, IProgressMonitor moni
addProjectSourceFolders(classpath, options, request, monitor);
String executionEnvironmentId = getExecutionEnvironmentId(options);
- String buildEnvironmentId = getMinimumJavaBuildEnvironmentId(request, monitor);
- addJREClasspathContainer(classpath, buildEnvironmentId != null ? buildEnvironmentId : executionEnvironmentId);
+ addJREClasspathContainer(classpath, executionEnvironmentId);
addMavenClasspathContainer(classpath);
@@ -813,12 +799,6 @@ protected List getCompilerMojoExecutions(ProjectConfigurationRequ
monitor, GOAL_COMPILE, GOAL_TESTCOMPILE);
}
- protected List getEnforcerMojoExecutions(ProjectConfigurationRequest request, IProgressMonitor monitor)
- throws CoreException {
- return request.mavenProjectFacade().getMojoExecutions(ENFORCER_PLUGIN_GROUP_ID, ENFORCER_PLUGIN_ARTIFACT_ID,
- monitor, GOAL_ENFORCE);
- }
-
private String getCompilerLevel(MavenProject mavenProject, MojoExecution execution, String parameter, String source,
List levels, IProgressMonitor monitor) {
int levelIdx = getLevelIndex(source, levels);
@@ -861,74 +841,6 @@ private int getLevelIndex(String level, List levels) {
return idx;
}
- private String getMinimumJavaBuildEnvironmentId(ProjectConfigurationRequest request, IProgressMonitor monitor) {
- try {
- List mojoExecutions = getEnforcerMojoExecutions(request, monitor);
- for(MojoExecution mojoExecution : mojoExecutions) {
- String version = getMinimumJavaBuildEnvironmentId(request.mavenProject(), mojoExecution, monitor);
- if(version != null) {
- return version;
- }
- }
- } catch(CoreException | InvalidVersionSpecificationException ex) {
- log.error("Failed to determine minimum build Java version, assuming default", ex);
- }
- return null;
- }
-
- private String getMinimumJavaBuildEnvironmentId(MavenProject mavenProject, MojoExecution mojoExecution,
- IProgressMonitor monitor) throws InvalidVersionSpecificationException, CoreException {
- // https://maven.apache.org/enforcer/enforcer-rules/requireJavaVersion.html
- String version = ((MavenImpl) maven).getMojoParameterValue(mavenProject, mojoExecution,
- List.of("rules", "requireJavaVersion", "version"), String.class, monitor);
- if(version == null) {
- return null;
- }
- return getMinimumJavaBuildEnvironmentId(version);
- }
-
- private String getMinimumJavaBuildEnvironmentId(String versionSpec) throws InvalidVersionSpecificationException {
- VersionRange vr = VersionRange.createFromVersionSpec(versionSpec);
- ArtifactVersion recommendedVersion = vr.getRecommendedVersion();
- List versionRestrictions = List.of();
- if(recommendedVersion == null) {
- versionRestrictions = getVersionRangeRestrictionsIgnoringMicroAndQualifier(vr);
- } else {
- // only consider major and minor version here, micro and qualifier not relevant inside IDE (probably)
- recommendedVersion = getMajorMinorOnlyVersion(recommendedVersion);
- }
- // find lowest matching environment id
- for(Entry entry : ENVIRONMENTS.entrySet()) {
- ArtifactVersion environmentVersion = new DefaultArtifactVersion(entry.getKey());
- boolean foundMatchingVersion;
- if(recommendedVersion == null) {
- foundMatchingVersion = versionRestrictions.stream().anyMatch(r -> r.containsVersion(environmentVersion));
- } else {
- // only singular versions ever have a recommendedVersion
- int compareTo = recommendedVersion.compareTo(environmentVersion);
- foundMatchingVersion = compareTo <= 0;
- }
- if(foundMatchingVersion) {
- return entry.getValue();
- }
- }
- return null;
- }
-
- private static List getVersionRangeRestrictionsIgnoringMicroAndQualifier(VersionRange versionRange) {
- return versionRange.getRestrictions().stream().map(restriction -> {
- ArtifactVersion lowerBound = restriction.getLowerBound();
- ArtifactVersion upperBound = restriction.getUpperBound();
- return new Restriction(//
- lowerBound != null ? getMajorMinorOnlyVersion(lowerBound) : null, restriction.isLowerBoundInclusive(),
- upperBound != null ? getMajorMinorOnlyVersion(upperBound) : null, restriction.isUpperBoundInclusive());
- }).toList();
- }
-
- private static ArtifactVersion getMajorMinorOnlyVersion(ArtifactVersion lower) {
- return new DefaultArtifactVersion(lower.getMajorVersion() + "." + lower.getMinorVersion());
- }
-
private double asDouble(String level) {
if(level == null || level.isEmpty()) {
return -1;
diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/actions/ExecutePomAction.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/actions/ExecutePomAction.java
index 6a3d098f12..b0efee9666 100644
--- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/actions/ExecutePomAction.java
+++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/actions/ExecutePomAction.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2019 Sonatype, Inc. and others.
+ * Copyright (c) 2008, 2023 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
@@ -42,11 +42,6 @@
import org.eclipse.debug.ui.ILaunchGroup;
import org.eclipse.debug.ui.ILaunchShortcut;
import org.eclipse.debug.ui.RefreshTab;
-import org.eclipse.jdt.core.IClasspathEntry;
-import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
-import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelection;
@@ -216,11 +211,6 @@ private ILaunchConfiguration createLaunchConfiguration(IContainer basedir, Strin
setProjectConfiguration(workingCopy, basedir);
- IPath path = getJREContainerPath(basedir);
- if(path != null) {
- workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, path.toPortableString());
- }
-
// TODO when launching Maven with debugger consider to add the following property
// -Dmaven.surefire.debug="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -Xnoagent -Djava.compiler=NONE"
@@ -245,21 +235,6 @@ private void setProjectConfiguration(ILaunchConfigurationWorkingCopy workingCopy
}
}
- // TODO ideally it should use MavenProject, but it is faster to scan IJavaProjects
- private IPath getJREContainerPath(IContainer basedir) throws CoreException {
- IProject project = basedir.getProject();
- if(project != null && project.hasNature(JavaCore.NATURE_ID)) {
- IJavaProject javaProject = JavaCore.create(project);
- IClasspathEntry[] entries = javaProject.getRawClasspath();
- for(IClasspathEntry entry : entries) {
- if(JavaRuntime.JRE_CONTAINER.equals(entry.getPath().segment(0))) {
- return entry.getPath();
- }
- }
- }
- return null;
- }
-
private ILaunchConfiguration getLaunchConfiguration(IContainer basedir, String mode) {
if(goalName != null) {
return createLaunchConfiguration(basedir, goalName);
diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenLaunchDelegate.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenLaunchDelegate.java
index eb22e44d18..6b8a692db1 100644
--- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenLaunchDelegate.java
+++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenLaunchDelegate.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2019 Sonatype, Inc.
+ * Copyright (c) 2008, 2023 Sonatype, Inc.
* 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
@@ -20,35 +20,60 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import java.util.Optional;
+import java.util.SortedMap;
+import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IProgressMonitor;
+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.IPreferencesService;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+import org.eclipse.jdt.launching.IVMInstall;
+import org.eclipse.jdt.launching.IVMInstall2;
+import org.eclipse.jdt.launching.IVMInstallType;
import org.eclipse.jdt.launching.IVMRunner;
import org.eclipse.jdt.launching.JavaLaunchDelegate;
+import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.osgi.util.NLS;
+import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.project.MavenProject;
import org.eclipse.m2e.actions.MavenLaunchConstants;
import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.embedder.IMavenConfiguration;
+import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.internal.launch.AbstractMavenRuntime;
+import org.eclipse.m2e.core.project.IMavenProjectFacade;
+import org.eclipse.m2e.core.project.IMavenProjectRegistry;
import org.eclipse.m2e.internal.launch.MavenRuntimeLaunchSupport.VMArguments;
public class MavenLaunchDelegate extends JavaLaunchDelegate implements MavenLaunchConstants {
- static final Logger log = LoggerFactory.getLogger(MavenLaunchDelegate.class);
+ private static final Logger log = LoggerFactory.getLogger(MavenLaunchDelegate.class);
+
+ private static final ILog ECLIPSE_LOG = Platform.getLog(MavenLaunchDelegate.class);
private static final String LAUNCHER_TYPE = "org.codehaus.classworlds.Launcher"; //$NON-NLS-1$
@@ -163,6 +188,176 @@ public String getProgramArguments(ILaunchConfiguration configuration) throws Cor
return programArguments;
}
+ public static File getPomDirectory(ILaunchConfiguration configuration) {
+ if(configuration == null) {
+ return null;
+ }
+ // set associated project name (if there is some)
+ String pomDir;
+ try {
+ pomDir = configuration.getAttribute(MavenLaunchConstants.ATTR_POM_DIR, "");
+ } catch(CoreException ex) {
+ log.warn("Failed to retrieve attribute '{}' from launch configuration {}", MavenLaunchConstants.ATTR_POM_DIR,
+ configuration.getName());
+ return null;
+ }
+ try {
+ return new File(LaunchingUtils.substituteVar(pomDir));
+ } catch(CoreException e) {
+ log.debug("Cannot substitute vars in {}", pomDir, e);
+ return null;
+ }
+ }
+
+ public static Optional getContainer(File file) {
+ // try to retrieve associated Eclipse project
+ return Optional.ofNullable(file).map(f -> Path.fromOSString(f.toString()))
+ .map(ResourcesPlugin.getWorkspace().getRoot()::getContainerForLocation);
+ }
+
+ @Override
+ public IVMInstall getVMInstall(ILaunchConfiguration configuration) throws CoreException {
+ // use the Maven JVM if nothing explicitly configured in the launch configuration
+ File pomDirectory = getPomDirectory(configuration);
+ if(!configuration.hasAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH)) {
+ String requiredJavaVersion = readEnforcedJavaVersion(pomDirectory, monitor);
+ if(requiredJavaVersion != null) {
+ IVMInstall jre = getBestMatchingVM(requiredJavaVersion);
+ if(jre != null) {
+ return jre;
+ }
+ }
+ }
+ Optional project = getContainer(pomDirectory).map(IContainer::getProject);
+ if(project.isPresent()) {
+ // Set the project name so that super.getVMInstall() called below, can find the JDT-Compiler JDK
+ ILaunchConfigurationWorkingCopy workingCopy = configuration.getWorkingCopy();
+ workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, project.get().getName());
+ configuration = workingCopy;
+ }
+ return super.getVMInstall(configuration);
+ }
+
+ public static String readEnforcedJavaVersion(File pomDirectory, IProgressMonitor monitor) {
+ try {
+ Optional container = getContainer(pomDirectory);
+ if(container.isPresent()) {
+ IMavenProjectRegistry projectManager = MavenPlugin.getMavenProjectRegistry();
+ IFile pomFile = container.get().getFile(Path.fromOSString(IMavenConstants.POM_FILE_NAME));
+ IMavenProjectFacade mavenProject = projectManager.create(pomFile, true, new NullProgressMonitor());
+ if(mavenProject != null) {
+ return readEnforcedVersion(mavenProject, monitor);
+ }
+ }
+ //TODO: handle the case if the pomDirectory points to a project not in the project. Then load the bare project.
+ } catch(CoreException ex) {
+ logEnforcedJavaVersionCalculationError(ex);
+ }
+ return null;
+ }
+
+ private static final String GOAL_ENFORCE = "enforce"; //$NON-NLS-1$
+
+ private static final String ENFORCER_PLUGIN_ARTIFACT_ID = "maven-enforcer-plugin"; //$NON-NLS-1$
+
+ private static final String ENFORCER_PLUGIN_GROUP_ID = "org.apache.maven.plugins"; //$NON-NLS-1$
+
+ private static String readEnforcedVersion(IMavenProjectFacade project, IProgressMonitor monitor)
+ throws CoreException {
+ List mojoExecutions = project.getMojoExecutions(ENFORCER_PLUGIN_GROUP_ID,
+ ENFORCER_PLUGIN_ARTIFACT_ID, monitor, GOAL_ENFORCE);
+ for(MojoExecution mojoExecution : mojoExecutions) {
+ String version = getRequiredJavaVersionFromEnforcerRule(project.getMavenProject(monitor), mojoExecution, monitor);
+ if(version != null) {
+ return version;
+ }
+ }
+ return null;
+ }
+
+ private static String getRequiredJavaVersionFromEnforcerRule(MavenProject mavenProject, MojoExecution mojoExecution,
+ IProgressMonitor monitor) throws CoreException {
+ // https://maven.apache.org/enforcer/enforcer-rules/requireJavaVersion.html
+ List parameter = List.of("rules", "requireJavaVersion", "version");
+ @SuppressWarnings("restriction")
+ String version = ((org.eclipse.m2e.core.internal.embedder.MavenImpl) MavenPlugin.getMaven())
+ .getMojoParameterValue(mavenProject, mojoExecution, parameter, String.class, monitor);
+ if(version == null) {
+ return null;
+ }
+ // normalize version (https://issues.apache.org/jira/browse/MENFORCER-440)
+ if("8".equals(version)) {
+ version = "1.8";
+ }
+ return version;
+ }
+
+ private static void logEnforcedJavaVersionCalculationError(Throwable e) {
+ ECLIPSE_LOG.error(
+ "Failed to determine required Java version from maven-enforcer-plugin configuration, assuming default", e);
+ }
+
+ public static IVMInstall getBestMatchingVM(String requiredVersion) {
+ try {
+ VersionRange versionRange = VersionRange.createFromVersionSpec(requiredVersion);
+ // find all matching JVMs (sorted by version)
+ List matchingJREs = getAllMatchingJREs(versionRange);
+
+ // for ranges with only lower bound or just a recommended version pick newest version having equal major version
+ ArtifactVersion mainVersion;
+ if(versionRange.getRecommendedVersion() != null) {
+ mainVersion = versionRange.getRecommendedVersion();
+ } else if(versionRange.getRestrictions().size() == 1
+ && versionRange.getRestrictions().get(0).getUpperBound() == null) {
+ mainVersion = versionRange.getRestrictions().get(0).getLowerBound();
+ } else {
+ mainVersion = null;
+ }
+ if(mainVersion != null) {
+ return matchingJREs.stream()
+ .filter(jre -> getArtifactVersion(jre).getMajorVersion() == mainVersion.getMajorVersion()).findFirst()
+ .orElse(null);
+ }
+ return !matchingJREs.isEmpty() ? matchingJREs.get(0) : null;
+ } catch(InvalidVersionSpecificationException ex) {
+ log.warn("Invalid version range", ex);
+ }
+ return null;
+ }
+
+ private static List getAllMatchingJREs(VersionRange versionRange) {
+ // find all matching JVMs and sort by their version (highest first)
+ SortedMap installedJREsByVersion = new TreeMap<>(Comparator.reverseOrder());
+
+ for(IVMInstallType vmType : JavaRuntime.getVMInstallTypes()) {
+ for(IVMInstall vm : vmType.getVMInstalls()) {
+ if(satisfiesVersionRange(vm, versionRange)) {
+ if(vm instanceof IVMInstall2 vm2) {
+ installedJREsByVersion.put(new DefaultArtifactVersion(vm2.getJavaVersion()), vm);
+ } else {
+ log.debug("Skipping IVMInstall '{}' from type {} as not implementing IVMInstall2", vm.getName(),
+ vmType.getName());
+ }
+ }
+ }
+ }
+ return List.copyOf(installedJREsByVersion.values());
+ }
+
+ private static boolean satisfiesVersionRange(IVMInstall jre, VersionRange versionRange) {
+ ArtifactVersion jreVersion = getArtifactVersion(jre);
+ if(versionRange.getRecommendedVersion() != null) {
+ return jreVersion.compareTo(versionRange.getRecommendedVersion()) >= 0;
+ }
+ return versionRange.containsVersion(jreVersion);
+ }
+
+ private static final ArtifactVersion DEFAULT_JAVA_VERSION = new DefaultArtifactVersion("0.0.0");
+
+ private static ArtifactVersion getArtifactVersion(IVMInstall jre) {
+ return jre instanceof IVMInstall2 jre2 ? new DefaultArtifactVersion(jre2.getJavaVersion()) : DEFAULT_JAVA_VERSION;
+ }
+
@Override
public String getVMArguments(ILaunchConfiguration configuration) throws CoreException {
VMArguments arguments = launchSupport.getVMArguments();
diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/Messages.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/Messages.java
index f9216c61f7..027dbdc165 100644
--- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/Messages.java
+++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/Messages.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008-2010 Sonatype, Inc.
+ * Copyright (c) 2008-2023 Sonatype, Inc.
* 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
@@ -81,6 +81,10 @@ public class Messages extends NLS {
public static String MavenLaunchMainTab_property_dialog_title;
+ public static String MavenJRETab_lblDefault;
+
+ public static String MavenJRETab_lblDefaultDetailsRequiredJavaVersion;
+
public static String MavenLaunchExtensionsTab_name;
public static String MavenLaunchExtensionsTab_lblExtensions;
diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/messages.properties b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/messages.properties
index 2370e2231c..e87792f0ae 100644
--- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/messages.properties
+++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/messages.properties
@@ -33,6 +33,8 @@ MavenLaunchMainTab_lblEnableColorOutput_Never=Never
MavenLaunchMainTab_lblUserSettings_text=User settings\:
MavenLaunchMainTab_property_dialog_edit_title=Edit Parameter
MavenLaunchMainTab_property_dialog_title=Add Parameter
+MavenJRETab_lblDefault=Default Maven Execution JRE: {0}
+MavenJRETab_lblDefaultDetailsRequiredJavaVersion={0} (auto-selected from required Java version range {1})
MavenLaunchUtils_error_no_maven_install=Can't find Maven installation {0}
MavenLaynchDelegate_unsupported_source_locator=Unknown or unsupported source locator {0}
launchBrowseFs=File Syste&m...
diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/ui/internal/launch/MavenJRETab.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/ui/internal/launch/MavenJRETab.java
index dfd6ed833e..dab0346951 100644
--- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/ui/internal/launch/MavenJRETab.java
+++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/ui/internal/launch/MavenJRETab.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008-2010 Sonatype, Inc.
+ * Copyright (c) 2008-2023 Sonatype, Inc.
* 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
@@ -13,15 +13,29 @@
package org.eclipse.m2e.ui.internal.launch;
+import java.io.File;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaJRETab;
+import org.eclipse.jdt.internal.debug.ui.jres.JREDescriptor;
import org.eclipse.jdt.internal.debug.ui.launcher.VMArgumentsBlock;
+import org.eclipse.jdt.launching.IVMInstall;
+import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
+import org.eclipse.m2e.internal.launch.MavenLaunchDelegate;
+import org.eclipse.m2e.internal.launch.Messages;
+
@SuppressWarnings("restriction")
public class MavenJRETab extends JavaJRETab {
@@ -40,6 +54,67 @@ public void createControl(Composite parent) {
((GridData) vmArgumentsBlock.getControl().getLayoutData()).horizontalSpan = 2;
}
+ @Override
+ protected IJavaProject getJavaProject() {
+ File pomDir = MavenLaunchDelegate.getPomDirectory(getLaunchConfiguration());
+ return MavenLaunchDelegate.getContainer(pomDir) //
+ .map(IContainer::getProject).filter(IProject::exists) //
+ .map(JavaCore::create).orElse(null);
+ }
+
+ /**
+ * Retrieves information about Maven JRE set in Maven project properties.
+ *
+ * @return the descriptor for the default Maven JRE
+ */
+ @Override
+ protected JREDescriptor getDefaultJREDescriptor() {
+ File pomDirectory = MavenLaunchDelegate.getPomDirectory(getLaunchConfiguration());
+ String version = MavenLaunchDelegate.readEnforcedJavaVersion(pomDirectory, null);
+ IVMInstall mavenJre = version != null ? MavenLaunchDelegate.getBestMatchingVM(version) : null;
+ String details;
+ if(mavenJre != null) { // add link
+ details = NLS.bind(Messages.MavenJRETab_lblDefaultDetailsRequiredJavaVersion, mavenJre.getName(), version);
+ } else {
+ // TODO: add logic for getting the underlying project then fall back to default
+ details = super.getDefaultJREDescriptor().getDescription();
+ }
+ return new JREDescriptor() {
+ @Override
+ public String getDescription() {
+ return NLS.bind(Messages.MavenJRETab_lblDefault, details);
+ }
+ };
+ }
+
+ /**
+ * Need to overwrite from parent, to prevent calling JavaJRETab.checkCompliance().
+ */
+ @Override
+ public boolean isValid(ILaunchConfiguration config) {
+ setErrorMessage(null);
+ setMessage(null);
+
+ IStatus status = fJREBlock.getStatus();
+ if(!status.isOK()) {
+ setErrorMessage(status.getMessage());
+ return false;
+ }
+ // prevent calling JavaJRETab.checkCompliance(), as that uses the wrong JDK for the checks when the default is checked
+ // also Maven launch type is not detected as external program
+ //TODO: isExternalToolConfiguration() should be protected and be overrideable
+ /**
+ * if(!isExternalToolConfiguration(fLaunchConfiguration)) { status = checkCompliance(); if (!status.isOK()) {
+ * setErrorMessage(status.getMessage()); return false; } }
+ */
+
+ ILaunchConfigurationTab dynamicTab = getDynamicTab();
+ if(dynamicTab != null) {
+ return dynamicTab.isValid(config);
+ }
+ return true;
+ }
+
@Override
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
super.performApply(configuration);