From 78a98f3fe9e9b560224429ef8edb2a614edc9f80 Mon Sep 17 00:00:00 2001 From: Abhinav Tarigoppula Date: Tue, 4 Aug 2026 20:27:33 +0530 Subject: [PATCH 1/3] Match maven-shared-archive-resources on the last path segment createArchiver() identified the shared archive resources directory with String.endsWith on the whole path, so any resource directory whose path merely ends with those characters matched too - for example /var/lib/tmp-maven-shared-archive-resources-extra would be treated as the shared directory and its contents added to the source archive. Compare the last path segment instead, via Path.getFileName(), and reuse the resolved Path for addDirectory rather than parsing the string twice. getFileName() returns null for a root path, so that case is guarded. Fixes #307 --- .../maven/plugins/source/AbstractSourceJarMojo.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/source/AbstractSourceJarMojo.java b/src/main/java/org/apache/maven/plugins/source/AbstractSourceJarMojo.java index 802a1fd..af6f082 100644 --- a/src/main/java/org/apache/maven/plugins/source/AbstractSourceJarMojo.java +++ b/src/main/java/org/apache/maven/plugins/source/AbstractSourceJarMojo.java @@ -58,6 +58,12 @@ public abstract class AbstractSourceJarMojo implements Mojo { private static final String[] DEFAULT_EXCLUDES = new String[] {}; + /** + * Name of the directory produced by maven-remote-resources-plugin whose contents are added to + * the source archive. Matched against the last path segment, not the whole path. + */ + private static final String SHARED_ARCHIVE_RESOURCES = "maven-shared-archive-resources"; + /** * List of files to include. Specified as fileset patterns which are relative to the input directory whose contents * is being packaged into the JAR. @@ -431,10 +437,12 @@ protected MavenArchiver createArchiver() throws MojoException { project.getBuild().getResources(); for (org.apache.maven.api.model.Resource r : resources) { - if (r.getDirectory().endsWith("maven-shared-archive-resources")) { + Path resourceDirectory = Paths.get(r.getDirectory()); + Path directoryName = resourceDirectory.getFileName(); + if (directoryName != null && SHARED_ARCHIVE_RESOURCES.equals(directoryName.toString())) { addDirectory( archiver.getArchiver(), - Paths.get(r.getDirectory()), + resourceDirectory, getCombinedIncludes(null), getCombinedExcludes(null)); } From e319dd458f807869350033b2b57d7ba9b8c4a82a Mon Sep 17 00:00:00 2001 From: Abhinav Tarigoppula Date: Wed, 5 Aug 2026 05:33:02 +0530 Subject: [PATCH 2/3] Inline the directory name literal Used in only one place, so the constant did not earn its keep. --- .../maven/plugins/source/AbstractSourceJarMojo.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/source/AbstractSourceJarMojo.java b/src/main/java/org/apache/maven/plugins/source/AbstractSourceJarMojo.java index af6f082..d660355 100644 --- a/src/main/java/org/apache/maven/plugins/source/AbstractSourceJarMojo.java +++ b/src/main/java/org/apache/maven/plugins/source/AbstractSourceJarMojo.java @@ -58,12 +58,6 @@ public abstract class AbstractSourceJarMojo implements Mojo { private static final String[] DEFAULT_EXCLUDES = new String[] {}; - /** - * Name of the directory produced by maven-remote-resources-plugin whose contents are added to - * the source archive. Matched against the last path segment, not the whole path. - */ - private static final String SHARED_ARCHIVE_RESOURCES = "maven-shared-archive-resources"; - /** * List of files to include. Specified as fileset patterns which are relative to the input directory whose contents * is being packaged into the JAR. @@ -439,7 +433,7 @@ protected MavenArchiver createArchiver() throws MojoException { for (org.apache.maven.api.model.Resource r : resources) { Path resourceDirectory = Paths.get(r.getDirectory()); Path directoryName = resourceDirectory.getFileName(); - if (directoryName != null && SHARED_ARCHIVE_RESOURCES.equals(directoryName.toString())) { + if (directoryName != null && "maven-shared-archive-resources".equals(directoryName.toString())) { addDirectory( archiver.getArchiver(), resourceDirectory, From 4c92d96c97aa1050cf832edb269f41484e34f478 Mon Sep 17 00:00:00 2001 From: Abhinav Tarigoppula Date: Wed, 5 Aug 2026 18:36:51 +0530 Subject: [PATCH 3/3] Add tests for the shared archive resources directory match createArchiver() adds a resource directory to the source archive only when that directory is the one produced by maven-remote-resources-plugin. Nothing covered which directories it selects, so neither the previous suffix match nor the last-segment match was pinned by a test. Drive createArchiver() with a stubbed project and a mock JarArchiver and assert the file sets it receives: the directory named maven-shared-archive-resources is added, a directory whose name merely ends with that text is not, and when both are present only the former is added. The latter two fail against the suffix match. --- .../source/AbstractSourceJarMojoTest.java | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/test/java/org/apache/maven/plugins/source/AbstractSourceJarMojoTest.java diff --git a/src/test/java/org/apache/maven/plugins/source/AbstractSourceJarMojoTest.java b/src/test/java/org/apache/maven/plugins/source/AbstractSourceJarMojoTest.java new file mode 100644 index 0000000..bea5362 --- /dev/null +++ b/src/test/java/org/apache/maven/plugins/source/AbstractSourceJarMojoTest.java @@ -0,0 +1,109 @@ +/* + * 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. + */ +package org.apache.maven.plugins.source; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.maven.api.Project; +import org.apache.maven.api.model.Build; +import org.apache.maven.api.model.Resource; +import org.apache.maven.api.plugin.Log; +import org.codehaus.plexus.archiver.FileSet; +import org.codehaus.plexus.archiver.jar.JarArchiver; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.ArgumentCaptor; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +class AbstractSourceJarMojoTest { + + private static final String SHARED_ARCHIVE_RESOURCES = "maven-shared-archive-resources"; + + @Test + void testSharedArchiveResourcesDirectoryIsAdded(@TempDir Path tempDir) throws Exception { + Path sharedArchiveResources = Files.createDirectory(tempDir.resolve(SHARED_ARCHIVE_RESOURCES)); + + JarArchiver jarArchiver = mock(JarArchiver.class); + AbstractSourceJarMojo mojo = mojoWithResourceDirectories(jarArchiver, sharedArchiveResources); + + mojo.createArchiver(); + + ArgumentCaptor fileSet = ArgumentCaptor.forClass(FileSet.class); + verify(jarArchiver).addFileSet(fileSet.capture()); + assertEquals(sharedArchiveResources.toFile(), fileSet.getValue().getDirectory()); + } + + /** + * A directory is the shared archive resources directory only when that is its name. A path that merely ends with + * those characters, such as one produced by appending a suffix to an unrelated directory, is a different directory + * and must not be added. + */ + @Test + void testDirectoryEndingWithSharedArchiveResourcesIsNotAdded(@TempDir Path tempDir) throws Exception { + Path suffixedName = Files.createDirectory(tempDir.resolve("tmp-" + SHARED_ARCHIVE_RESOURCES)); + + JarArchiver jarArchiver = mock(JarArchiver.class); + AbstractSourceJarMojo mojo = mojoWithResourceDirectories(jarArchiver, suffixedName); + + mojo.createArchiver(); + + verifyNoInteractions(jarArchiver); + } + + @Test + void testOnlySharedArchiveResourcesDirectoryIsAddedWhenBothArePresent(@TempDir Path tempDir) throws Exception { + Path suffixedName = Files.createDirectory(tempDir.resolve("tmp-" + SHARED_ARCHIVE_RESOURCES)); + Path sharedArchiveResources = Files.createDirectory(tempDir.resolve(SHARED_ARCHIVE_RESOURCES)); + + JarArchiver jarArchiver = mock(JarArchiver.class); + AbstractSourceJarMojo mojo = mojoWithResourceDirectories(jarArchiver, suffixedName, sharedArchiveResources); + + mojo.createArchiver(); + + ArgumentCaptor fileSet = ArgumentCaptor.forClass(FileSet.class); + verify(jarArchiver).addFileSet(fileSet.capture()); + assertEquals(sharedArchiveResources.toFile(), fileSet.getValue().getDirectory()); + } + + private static AbstractSourceJarMojo mojoWithResourceDirectories(JarArchiver jarArchiver, Path... directories) { + List resources = Arrays.stream(directories) + .map(directory -> + Resource.newBuilder().directory(directory.toString()).build()) + .collect(Collectors.toList()); + + Project project = mock(Project.class); + when(project.getBuild()) + .thenReturn(Build.newBuilder().resources(resources).build()); + + AbstractSourceJarMojo mojo = new SourceJarMojo(); + mojo.project = project; + mojo.jarArchiver = jarArchiver; + mojo.log = mock(Log.class); + return mojo; + } +}