From 04712fd001c77e0bedeb737f8b9c062a73fa85d9 Mon Sep 17 00:00:00 2001 From: Martin Desruisseaux Date: Sun, 5 Oct 2025 16:56:37 +0200 Subject: [PATCH 1/3] Upgrade to Maven 4.0.0-rc-4. --- .github/workflows/maven-verify.yml | 4 ++-- pom.xml | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven-verify.yml b/.github/workflows/maven-verify.yml index 2bc0ff1..ea5b27a 100644 --- a/.github/workflows/maven-verify.yml +++ b/.github/workflows/maven-verify.yml @@ -26,6 +26,6 @@ jobs: name: Verify uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v4 with: - ff-maven: "4.0.0-rc-2" # Maven version for fail-fast-build - maven-matrix: '[ "4.0.0-rc-2" ]' + ff-maven: "4.0.0-rc-4" # Maven version for fail-fast-build + maven-matrix: '[ "4.0.0-rc-4" ]' jdk-matrix: '[ "17", "21" ]' diff --git a/pom.xml b/pom.xml index 4a0a3af..80355fd 100644 --- a/pom.xml +++ b/pom.xml @@ -74,14 +74,14 @@ 17 - 4.0.0-rc-2 + 4.0.0-rc-4 6.0.0 5.13.4 4.0.0-beta-5 3.2.0 4.0.0-beta-1 - 4.0.0-beta-3 + 4.0.0-beta-4 4.10.2 ${mavenPluginPluginVersion} @@ -138,6 +138,12 @@ ${mavenVersion} provided + + org.apache.maven + maven-xml + ${mavenVersion} + provided + org.apache.maven.shared file-management From 33ef00904e135c6991da1b1e4bc7370dfafe94e4 Mon Sep 17 00:00:00 2001 From: Martin Desruisseaux Date: Sun, 12 Oct 2025 10:50:47 +0200 Subject: [PATCH 2/3] Remove the `useDefaultManifestFile` configuration parameter, which was deprecated since Maven 3.0.0. --- src/it/MJAR-210/invoker.properties | 19 --------- src/it/MJAR-210/pom.xml | 39 ------------------- .../maven/plugins/jar/AbstractJarMojo.java | 17 -------- 3 files changed, 75 deletions(-) delete mode 100644 src/it/MJAR-210/invoker.properties delete mode 100644 src/it/MJAR-210/pom.xml diff --git a/src/it/MJAR-210/invoker.properties b/src/it/MJAR-210/invoker.properties deleted file mode 100644 index 7a6d3ae..0000000 --- a/src/it/MJAR-210/invoker.properties +++ /dev/null @@ -1,19 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -invoker.goals = clean package -invoker.buildResult = failure diff --git a/src/it/MJAR-210/pom.xml b/src/it/MJAR-210/pom.xml deleted file mode 100644 index 2d4d92c..0000000 --- a/src/it/MJAR-210/pom.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - 4.0.0 - org.apache.maven.plugins - maven-jar-plugin-test-mjar-210 - 1.0 - jar - Maven - - - - org.apache.maven.plugins - maven-jar-plugin - @project.version@ - - true - - - - - diff --git a/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java b/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java index d1c618c..f5b6676 100644 --- a/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java +++ b/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java @@ -109,17 +109,6 @@ public abstract class AbstractJarMojo implements org.apache.maven.api.plugin.Moj @Parameter private MavenArchiveConfiguration archive = new MavenArchiveConfiguration(); - /** - * Using this property will fail your build cause it has been removed from the plugin configuration. See the menu entry - * Using Your Own Manifest File for the - * plugin. - * - * @deprecated For version 3.0.0 this parameter is only defined here to break the build if you use it! - */ - @Parameter(property = "jar.useDefaultManifestFile", defaultValue = "false") - @Deprecated - private boolean useDefaultManifestFile; - /** * */ @@ -295,12 +284,6 @@ public Path createArchive() throws MojoException { */ @Override public void execute() throws MojoException { - if (useDefaultManifestFile) { - throw new MojoException("You are using 'useDefaultManifestFile' which has been removed" - + " from the maven-jar-plugin. " - + "Please see the link >>Using Your Own Manifest File<< on the plugin site."); - } - if (skipIfEmpty && isEmpty(getClassesDirectory())) { getLog().info("Skipping packaging of the " + getType()); } else { From a2932acb8af8b46154b543cd36919a3204d063b1 Mon Sep 17 00:00:00 2001 From: Martin Desruisseaux Date: Sun, 12 Oct 2025 11:49:54 +0200 Subject: [PATCH 3/3] Edition in Javadoc (mostly formatting) and minor code simplifications. This commit should not change the behaviour of the Maven JAR plugin, except when a null argument is given to `getJarFile` in which case the `IllegalArgumentException` has been replaced by `NullPointerException`. --- .../maven/plugins/jar/AbstractJarMojo.java | 115 +++++++++--------- .../org/apache/maven/plugins/jar/JarMojo.java | 5 +- .../apache/maven/plugins/jar/TestJarMojo.java | 9 +- 3 files changed, 65 insertions(+), 64 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java b/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java index f5b6676..ad7a9bd 100644 --- a/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java +++ b/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java @@ -18,12 +18,14 @@ */ package org.apache.maven.plugins.jar; +import java.io.File; import java.io.IOException; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Map; +import java.util.Objects; +import java.util.jar.Attributes; import java.util.stream.Stream; import org.apache.maven.api.ProducedArtifact; @@ -43,10 +45,9 @@ import org.codehaus.plexus.archiver.jar.JarArchiver; /** - * Base class for creating a jar from project classes. + * Base class for creating a JAR file from project classes. * * @author Emmanuel Venisse - * @version $Id$ */ public abstract class AbstractJarMojo implements org.apache.maven.api.plugin.Mojo { @@ -56,8 +57,6 @@ public abstract class AbstractJarMojo implements org.apache.maven.api.plugin.Moj private static final String MODULE_DESCRIPTOR_FILE_NAME = "module-info.class"; - private static final String SEPARATOR = FileSystems.getDefault().getSeparator(); - /** * List of files to include. Specified as fileset patterns which are relative to the input directory whose contents * is being packaged into the JAR. @@ -85,19 +84,19 @@ public abstract class AbstractJarMojo implements org.apache.maven.api.plugin.Moj private String finalName; /** - * The Jar archiver. + * The JAR archiver. */ @Inject private Map archivers; /** - * The {@link Project}. + * The Maven project. */ @Inject private Project project; /** - * The {@link Session}. + * The session. */ @Inject private Session session; @@ -109,22 +108,20 @@ public abstract class AbstractJarMojo implements org.apache.maven.api.plugin.Moj @Parameter private MavenArchiveConfiguration archive = new MavenArchiveConfiguration(); - /** - * - */ @Inject private ProjectManager projectManager; /** - * Require the jar plugin to build a new JAR even if none of the contents appear to have changed. By default, this - * plugin looks to see if the output jar exists and inputs have not changed. If these conditions are true, the - * plugin skips creation of the jar. This does not work when other plugins, like the maven-shade-plugin, are - * configured to post-process the jar. This plugin can not detect the post-processing, and so leaves the - * post-processed jar in place. This can lead to failures when those plugins do not expect to find their own output - * as an input. Set this parameter to true to avoid these problems by forcing this plugin to recreate the - * jar every time.
- * Starting with 3.0.0 the property has been renamed from jar.forceCreation to - * maven.jar.forceCreation. + * Require the jar plugin to build a new JAR even if none of the contents appear to have changed. + * By default, this plugin looks to see if the output JAR exists and inputs have not changed. + * If these conditions are true, the plugin skips creation of the JAR file. + * This does not work when other plugins, like the maven-shade-plugin, are configured to post-process the JAR. + * This plugin can not detect the post-processing, and so leaves the post-processed JAR file in place. + * This can lead to failures when those plugins do not expect to find their own output as an input. + * Set this parameter to {@code true} to avoid these problems by forcing this plugin to recreate the JAR every time. + * + *

Starting with 3.0.0 the property has been renamed from {@code jar.forceCreation} + * to {@code maven.jar.forceCreation}.

*/ @Parameter(property = "maven.jar.forceCreation", defaultValue = "false") private boolean forceCreation; @@ -136,9 +133,10 @@ public abstract class AbstractJarMojo implements org.apache.maven.api.plugin.Moj private boolean skipIfEmpty; /** - * Timestamp for reproducible output archive entries, either formatted as ISO 8601 extended offset date-time + * Timestamp for reproducible output archive entries. + * This is either formatted as ISO 8601 extended offset date-time * (e.g. in UTC such as '2011-12-03T10:15:30Z' or with an offset '2019-10-05T20:37:42+06:00'), - * or as an int representing seconds since the epoch + * or as an integer representing seconds since the epoch * (like SOURCE_DATE_EPOCH). * * @since 3.2.0 @@ -147,8 +145,9 @@ public abstract class AbstractJarMojo implements org.apache.maven.api.plugin.Moj private String outputTimestamp; /** + * Whether to detect multi-release JAR files. * If the JAR contains the {@code META-INF/versions} directory it will be detected as a multi-release JAR file - * ("MRJAR"), adding the {@code Multi-Release: true} attribute to the main section of the JAR MANIFEST.MF. + * ("MRJAR"), adding the {@code Multi-Release: true} attribute to the main section of the JAR {@code MANIFEST.MF}. * * @since 3.4.0 */ @@ -156,44 +155,51 @@ public abstract class AbstractJarMojo implements org.apache.maven.api.plugin.Moj private boolean detectMultiReleaseJar; /** - * The mojo logger + * The MOJO logger. */ @Inject private Log log; /** - * Return the specific output directory to serve as the root for the archive. - * @return get classes directory. + * Creates a new MOJO. + */ + protected AbstractJarMojo() {} + + /** + * {@return the specific output directory to serve as the root for the archive} */ protected abstract Path getClassesDirectory(); /** - * Return the {@link #project MavenProject} + * Return the {@linkplain #project Maven project}. * - * @return the MavenProject. + * @return the Maven project */ protected final Project getProject() { return project; } + /** + * {@return the MOJO logger} + */ protected final Log getLog() { return log; } /** - * Overload this to produce a jar with another classifier, for example a test-jar. - * @return get the classifier. + * {@return the classifier of the JAR file to produce} + * This is usually null or empty for the main artifact, or {@code "tests"} for the JAR file of test code. */ protected abstract String getClassifier(); /** - * Overload this to produce a test-jar, for example. - * @return return the type. + * {@return the type of the JAR file to produce} + * This is usually {@code "jar"} for the main artifact, or {@code "test-jar"} for the JAR file of test code. */ protected abstract String getType(); /** - * Returns the Jar file to generate, based on an optional classifier. + * Returns the JAR file to generate, based on an optional classifier. * * @param basedir the output directory * @param resultFinalName the name of the ear file @@ -201,22 +207,17 @@ protected final Log getLog() { * @return the file to generate */ protected Path getJarFile(Path basedir, String resultFinalName, String classifier) { - if (basedir == null) { - throw new IllegalArgumentException("basedir is not allowed to be null"); - } - if (resultFinalName == null) { - throw new IllegalArgumentException("finalName is not allowed to be null"); - } - - String fileName = resultFinalName + (hasClassifier() ? "-" + classifier : "") + ".jar"; - + Objects.requireNonNull(basedir, "basedir is not allowed to be null"); + Objects.requireNonNull(resultFinalName, "finalName is not allowed to be null"); + String fileName = resultFinalName + (hasClassifier(classifier) ? '-' + classifier : "") + ".jar"; return basedir.resolve(fileName); } /** * Generates the JAR. - * @return The instance of File for the created archive file. - * @throws MojoException in case of an error. + * + * @return the path to the created archive file + * @throws MojoException in case of an error */ public Path createArchive() throws MojoException { Path jarFile = getJarFile(outputDirectory, finalName, getClassifier()); @@ -231,9 +232,10 @@ public Path createArchive() throws MojoException { if (detectMultiReleaseJar && Arrays.stream(includedFiles) - .anyMatch(p -> p.startsWith("META-INF" + SEPARATOR + "versions" + SEPARATOR))) { + .anyMatch( + p -> p.startsWith("META-INF" + File.separatorChar + "versions" + File.separatorChar))) { getLog().debug("Adding 'Multi-Release: true' manifest entry."); - archive.addManifestEntry("Multi-Release", "true"); + archive.addManifestEntry(Attributes.Name.MULTI_RELEASE.toString(), "true"); } // May give false positives if the files is named as module descriptor @@ -280,21 +282,23 @@ public Path createArchive() throws MojoException { /** * Generates the JAR. - * @throws MojoException in case of an error. + * + * @throws MojoException in case of an error */ @Override public void execute() throws MojoException { if (skipIfEmpty && isEmpty(getClassesDirectory())) { - getLog().info("Skipping packaging of the " + getType()); + getLog().info(String.format("Skipping packaging of the %s.", getType())); } else { Path jarFile = createArchive(); ProducedArtifact artifact; - if (hasClassifier()) { + String classifier = getClassifier(); + if (hasClassifier(classifier)) { artifact = session.createProducedArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(), - getClassifier(), + classifier, null, getType()); } else { @@ -308,12 +312,12 @@ public void execute() throws MojoException { } } - private boolean isEmpty(Path directory) { + private static boolean isEmpty(Path directory) { if (!Files.isDirectory(directory)) { return true; } try (Stream children = Files.list(directory)) { - return !children.findAny().isPresent(); + return children.findAny().isEmpty(); } catch (IOException e) { throw new MavenArchiverException("Unable to access directory", e); } @@ -327,10 +331,11 @@ private boolean projectHasAlreadySetAnArtifact() { /** * Return {@code true} in case where the classifier is not {@code null} and contains something else than white spaces. * - * @return {@code true} if the classifier is set. + * @param classifier the classifier to verify + * @return {@code true} if the classifier is set */ - protected boolean hasClassifier() { - return getClassifier() != null && !getClassifier().trim().isEmpty(); + private static boolean hasClassifier(String classifier) { + return classifier != null && !classifier.isBlank(); } private String[] getIncludes() { diff --git a/src/main/java/org/apache/maven/plugins/jar/JarMojo.java b/src/main/java/org/apache/maven/plugins/jar/JarMojo.java index 922634c..9bf9028 100644 --- a/src/main/java/org/apache/maven/plugins/jar/JarMojo.java +++ b/src/main/java/org/apache/maven/plugins/jar/JarMojo.java @@ -27,7 +27,6 @@ * Build a JAR from the current project. * * @author Emmanuel Venisse - * @version $Id$ */ @Mojo(name = "jar", defaultPhase = "package") public class JarMojo extends AbstractJarMojo { @@ -38,8 +37,8 @@ public class JarMojo extends AbstractJarMojo { private Path classesDirectory; /** - * Classifier to add to the artifact generated. If given, the artifact will be attached - * as a supplemental artifact. + * Classifier to add to the artifact generated. + * If given, the artifact will be attached as a supplemental artifact. * If not given this will create the main artifact which is the default behavior. * If you try to do that a second time without using a classifier the build will fail. */ diff --git a/src/main/java/org/apache/maven/plugins/jar/TestJarMojo.java b/src/main/java/org/apache/maven/plugins/jar/TestJarMojo.java index e3ec45a..84de519 100644 --- a/src/main/java/org/apache/maven/plugins/jar/TestJarMojo.java +++ b/src/main/java/org/apache/maven/plugins/jar/TestJarMojo.java @@ -28,16 +28,13 @@ * Build a JAR of the test classes for the current project. * * @author Emmanuel Venisse - * @version $Id$ */ -// CHECKSTYLE_OFF: LineLength @Mojo(name = "test-jar", defaultPhase = "package") -// CHECKSTYLE_ON: LineLength public class TestJarMojo extends AbstractJarMojo { /** - * Set this to true to bypass test-jar generation. Its use is NOT RECOMMENDED, but quite - * convenient on occasion. + * Set this to {@code true} to bypass test-jar generation. + * Its use is not recommended, but quite convenient on occasion. */ @Parameter(property = "maven.test.skip") private boolean skip; @@ -84,7 +81,7 @@ protected Path getClassesDirectory() { @Override public void execute() throws MojoException { if (skip) { - getLog().info("Skipping packaging of the test-jar"); + getLog().info("Skipping packaging of the test-jar."); } else { super.execute(); }