Skip to content

Commit 3dd64ab

Browse files
Allow a Maven plugin to require a Java version
Introduced in Maven 4.0.0-alpha-3 - MNG-7566 Plugin tools generate it since 3.8.1 - MPLUGIN-425 As many plugins required newer JDK we can introduce it also for Maven 3.x for better error reporting
1 parent ca4d701 commit 3dd64ab

File tree

13 files changed

+328
-56
lines changed

13 files changed

+328
-56
lines changed

maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ private void execute(
184184
MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
185185

186186
try {
187-
mavenPluginManager.checkRequiredMavenVersion(mojoDescriptor.getPluginDescriptor());
187+
mavenPluginManager.checkPrerequisites(mojoDescriptor.getPluginDescriptor());
188188
} catch (PluginIncompatibleException e) {
189189
throw new LifecycleExecutionException(mojoExecution, session.getCurrentProject(), e);
190190
}

maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginDescriptorCache.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ protected static PluginDescriptor clone(PluginDescriptor original) {
104104
clone.setName(original.getName());
105105
clone.setDescription(original.getDescription());
106106
clone.setRequiredMavenVersion(original.getRequiredMavenVersion());
107+
clone.setRequiredJavaVersion(original.getRequiredJavaVersion());
107108

108109
clone.setPluginArtifact(ArtifactUtils.copyArtifactSafe(original.getPluginArtifact()));
109110

maven-core/src/main/java/org/apache/maven/plugin/MavenPluginManager.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,19 @@ MojoDescriptor getMojoDescriptor(
7171
* Verifies the specified plugin is compatible with the current Maven runtime.
7272
*
7373
* @param pluginDescriptor The descriptor of the plugin to check, must not be {@code null}.
74+
* @deprecated Use {@link #checkPrerequisites(PluginDescriptor)} instead.
7475
*/
76+
@Deprecated
7577
void checkRequiredMavenVersion(PluginDescriptor pluginDescriptor) throws PluginIncompatibleException;
7678

79+
/**
80+
* Verifies that the specified plugin's prerequisites are met.
81+
*
82+
* @param pluginDescriptor The descriptor of the plugin to check, must not be {@code null}.
83+
* @since 3.9.12
84+
*/
85+
void checkPrerequisites(PluginDescriptor pluginDescriptor) throws PluginIncompatibleException;
86+
7787
/**
7888
* Sets up the class realm for the specified plugin. Both the class realm and the plugin artifacts that constitute
7989
* it will be stored in the plugin descriptor.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.plugin;
20+
21+
import java.util.function.Consumer;
22+
23+
import org.apache.maven.plugin.descriptor.PluginDescriptor;
24+
25+
/**
26+
* Service responsible for checking if plugin's prerequisites are met.
27+
*
28+
* @since 3.9.12
29+
*/
30+
@FunctionalInterface
31+
public interface MavenPluginPrerequisitesChecker extends Consumer<PluginDescriptor> {
32+
/**
33+
*
34+
* @param pluginDescriptor
35+
* @throws IllegalStateException in case the checked prerequisites are not met
36+
*/
37+
@Override
38+
void accept(PluginDescriptor pluginDescriptor);
39+
}

maven-core/src/main/java/org/apache/maven/plugin/PluginIncompatibleException.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
public class PluginIncompatibleException extends PluginManagerException {
2727

2828
public PluginIncompatibleException(Plugin plugin, String message) {
29-
super(plugin, message, (Throwable) null);
29+
this(plugin, message, null);
30+
}
31+
32+
public PluginIncompatibleException(Plugin plugin, String message, Throwable cause) {
33+
super(plugin, message, cause);
3034
}
3135
}

maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.Map;
3636
import java.util.Objects;
3737
import java.util.jar.JarFile;
38+
import java.util.stream.Collectors;
3839
import java.util.zip.ZipEntry;
3940

4041
import org.apache.maven.RepositoryUtils;
@@ -49,6 +50,7 @@
4950
import org.apache.maven.plugin.ExtensionRealmCache;
5051
import org.apache.maven.plugin.InvalidPluginDescriptorException;
5152
import org.apache.maven.plugin.MavenPluginManager;
53+
import org.apache.maven.plugin.MavenPluginPrerequisitesChecker;
5254
import org.apache.maven.plugin.MavenPluginValidator;
5355
import org.apache.maven.plugin.Mojo;
5456
import org.apache.maven.plugin.MojoExecution;
@@ -169,6 +171,9 @@ public class DefaultMavenPluginManager implements MavenPluginManager {
169171
@Requirement
170172
private PluginValidationManager pluginValidationManager;
171173

174+
@Requirement
175+
private List<MavenPluginPrerequisitesChecker> prerequisitesCheckers;
176+
172177
private ExtensionDescriptorBuilder extensionDescriptorBuilder = new ExtensionDescriptorBuilder();
173178

174179
private PluginDescriptorBuilder builder = new PluginDescriptorBuilder();
@@ -279,22 +284,37 @@ public MojoDescriptor getMojoDescriptor(
279284
return mojoDescriptor;
280285
}
281286

282-
public void checkRequiredMavenVersion(PluginDescriptor pluginDescriptor) throws PluginIncompatibleException {
283-
String requiredMavenVersion = pluginDescriptor.getRequiredMavenVersion();
284-
if (StringUtils.isNotBlank(requiredMavenVersion)) {
287+
@Override
288+
public void checkPrerequisites(PluginDescriptor pluginDescriptor) throws PluginIncompatibleException {
289+
List<IllegalStateException> prerequisiteExceptions = new ArrayList<>();
290+
prerequisitesCheckers.forEach(c -> {
285291
try {
286-
if (!runtimeInformation.isMavenVersion(requiredMavenVersion)) {
287-
throw new PluginIncompatibleException(
288-
pluginDescriptor.getPlugin(),
289-
"The plugin " + pluginDescriptor.getId() + " requires Maven version "
290-
+ requiredMavenVersion);
291-
}
292-
} catch (RuntimeException e) {
293-
logger.warn("Could not verify plugin's Maven prerequisite: " + e.getMessage());
292+
c.accept(pluginDescriptor);
293+
} catch (IllegalStateException e) {
294+
prerequisiteExceptions.add(e);
294295
}
296+
});
297+
// aggregate all exceptions
298+
if (!prerequisiteExceptions.isEmpty()) {
299+
String messages = prerequisiteExceptions.stream()
300+
.map(IllegalStateException::getMessage)
301+
.collect(Collectors.joining(", "));
302+
PluginIncompatibleException pie = new PluginIncompatibleException(
303+
pluginDescriptor.getPlugin(),
304+
"The plugin " + pluginDescriptor.getId() + " has unmet prerequisites: " + messages,
305+
prerequisiteExceptions.get(0));
306+
// the first exception is added as cause, all other ones as suppressed exceptions
307+
prerequisiteExceptions.stream().skip(1).forEach(pie::addSuppressed);
308+
throw pie;
295309
}
296310
}
297311

312+
@Override
313+
@Deprecated
314+
public void checkRequiredMavenVersion(PluginDescriptor pluginDescriptor) throws PluginIncompatibleException {
315+
checkPrerequisites(pluginDescriptor);
316+
}
317+
298318
public void setupPluginRealm(
299319
PluginDescriptor pluginDescriptor,
300320
MavenSession session,
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.plugin.internal;
20+
21+
import javax.inject.Named;
22+
import javax.inject.Singleton;
23+
24+
import org.apache.maven.plugin.MavenPluginPrerequisitesChecker;
25+
import org.apache.maven.plugin.descriptor.PluginDescriptor;
26+
import org.eclipse.aether.util.version.GenericVersionScheme;
27+
import org.eclipse.aether.version.InvalidVersionSpecificationException;
28+
import org.eclipse.aether.version.Version;
29+
import org.eclipse.aether.version.VersionConstraint;
30+
import org.eclipse.aether.version.VersionScheme;
31+
32+
@Named
33+
@Singleton
34+
public class MavenPluginJavaPrerequisiteChecker implements MavenPluginPrerequisitesChecker {
35+
private final VersionScheme versionScheme = new GenericVersionScheme();
36+
37+
@Override
38+
public void accept(PluginDescriptor pluginDescriptor) {
39+
String requiredJavaVersion = pluginDescriptor.getRequiredJavaVersion();
40+
if (requiredJavaVersion != null && !requiredJavaVersion.isEmpty()) {
41+
String currentJavaVersion = System.getProperty("java.version");
42+
if (!matchesVersion(requiredJavaVersion, currentJavaVersion)) {
43+
throw new IllegalStateException("Required Java version " + requiredJavaVersion
44+
+ " is not met by current version: " + currentJavaVersion);
45+
}
46+
}
47+
}
48+
49+
boolean matchesVersion(String requiredVersion, String currentVersion) {
50+
VersionConstraint constraint;
51+
try {
52+
constraint = versionScheme.parseVersionConstraint(requiredVersion.equals("8") ? "1.8" : requiredVersion);
53+
} catch (InvalidVersionSpecificationException e) {
54+
throw new IllegalArgumentException("Invalid 'requiredJavaVersion' given in plugin descriptor", e);
55+
}
56+
Version current;
57+
try {
58+
current = versionScheme.parseVersion(currentVersion);
59+
} catch (InvalidVersionSpecificationException e) {
60+
throw new IllegalStateException("Could not parse current Java version", e);
61+
}
62+
if (constraint.getRange() == null) {
63+
return constraint.getVersion().compareTo(current) <= 0;
64+
}
65+
return constraint.containsVersion(current);
66+
}
67+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.plugin.internal;
20+
21+
import javax.inject.Inject;
22+
import javax.inject.Named;
23+
import javax.inject.Singleton;
24+
25+
import org.apache.maven.plugin.MavenPluginPrerequisitesChecker;
26+
import org.apache.maven.plugin.descriptor.PluginDescriptor;
27+
import org.apache.maven.rtinfo.RuntimeInformation;
28+
import org.slf4j.Logger;
29+
import org.slf4j.LoggerFactory;
30+
31+
@Named
32+
@Singleton
33+
public class MavenPluginMavenPrerequisiteChecker implements MavenPluginPrerequisitesChecker {
34+
private final Logger logger = LoggerFactory.getLogger(getClass());
35+
private final RuntimeInformation runtimeInformation;
36+
37+
@Inject
38+
public MavenPluginMavenPrerequisiteChecker(RuntimeInformation runtimeInformation) {
39+
super();
40+
this.runtimeInformation = runtimeInformation;
41+
}
42+
43+
@Override
44+
public void accept(PluginDescriptor pluginDescriptor) {
45+
String requiredMavenVersion = pluginDescriptor.getRequiredMavenVersion();
46+
47+
boolean isBlankVersion =
48+
requiredMavenVersion == null || requiredMavenVersion.trim().isEmpty();
49+
50+
if (!isBlankVersion) {
51+
boolean isRequirementMet = false;
52+
try {
53+
isRequirementMet = runtimeInformation.isMavenVersion(requiredMavenVersion);
54+
} catch (IllegalArgumentException e) {
55+
logger.warn(
56+
"Could not verify plugin's Maven prerequisite as an invalid version is given in "
57+
+ requiredMavenVersion,
58+
e);
59+
return;
60+
}
61+
if (!isRequirementMet) {
62+
throw new IllegalStateException("Required Maven version " + requiredMavenVersion
63+
+ " is not met by current version " + runtimeInformation.getMavenVersion());
64+
}
65+
}
66+
}
67+
}

maven-core/src/main/java/org/apache/maven/plugin/version/internal/DefaultPluginVersionResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ private boolean isCompatible(PluginVersionRequest request, String version) {
273273
}
274274

275275
try {
276-
pluginManager.checkRequiredMavenVersion(pluginDescriptor);
276+
pluginManager.checkPrerequisites(pluginDescriptor);
277277
} catch (PluginIncompatibleException e) {
278278
if (logger.isDebugEnabled()) {
279279
logger.warn("Ignoring incompatible plugin version " + version, e);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.plugin.internal;
20+
21+
import org.junit.Test;
22+
23+
import static org.junit.Assert.assertFalse;
24+
import static org.junit.Assert.assertThrows;
25+
import static org.junit.Assert.assertTrue;
26+
27+
public class MavenPluginJavaPrerequisiteCheckerTest {
28+
29+
@Test
30+
public void testMatchesVersion() {
31+
MavenPluginJavaPrerequisiteChecker checker = new MavenPluginJavaPrerequisiteChecker();
32+
assertTrue(checker.matchesVersion("8", "1.8"));
33+
assertTrue(checker.matchesVersion("8", "9.0.1+11"));
34+
assertTrue(checker.matchesVersion("1.0", "1.8"));
35+
assertTrue(checker.matchesVersion("1.8", "9.0.1+11"));
36+
assertFalse(checker.matchesVersion("[1.0,2],[3,4]", "2.1"));
37+
assertTrue(checker.matchesVersion("[1.0,2],[3,4]", "3.1"));
38+
assertThrows(IllegalArgumentException.class, () -> checker.matchesVersion("(1.0,0)", "11"));
39+
}
40+
}

0 commit comments

Comments
 (0)