From 964dcf3c4152f0a31862ff79c4fd0bae429d3f3e Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Sun, 2 Aug 2026 10:30:56 +0200 Subject: [PATCH 1/5] [MDEP-858] Migrate dependency:copy-dependencies installation to Maven Resolver Replace Maven Artifact Transfer installation used by dependency:copy-dependencies in the repository-layout path while preserving Maven local-repository metadata, copied POMs, and snapshot behavior. Fixes #1355. --- .../CopyDependenciesMojo.java | 41 +++------ .../dependency/utils/ResolverUtil.java | 53 +++++++++++ .../TestCopyDependenciesMojo2.java | 42 ++++++--- .../dependency/utils/ResolverUtilTest.java | 90 ++++++++++++++++++- 4 files changed, 184 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/CopyDependenciesMojo.java b/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/CopyDependenciesMojo.java index 6b820480f..064f79fde 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/CopyDependenciesMojo.java +++ b/src/main/java/org/apache/maven/plugins/dependency/fromDependencies/CopyDependenciesMojo.java @@ -22,7 +22,6 @@ import java.io.File; import java.io.IOException; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -43,11 +42,9 @@ import org.apache.maven.plugins.dependency.utils.filters.DestFileFilter; import org.apache.maven.project.MavenProject; import org.apache.maven.project.ProjectBuilder; -import org.apache.maven.project.ProjectBuildingRequest; import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter; -import org.apache.maven.shared.transfer.artifact.install.ArtifactInstaller; -import org.apache.maven.shared.transfer.artifact.install.ArtifactInstallerException; -import org.apache.maven.shared.transfer.repository.RepositoryManager; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.installation.InstallationException; import org.eclipse.aether.resolution.ArtifactDescriptorException; import org.eclipse.aether.resolution.ArtifactResolutionException; import org.eclipse.aether.util.artifact.SubArtifact; @@ -78,10 +75,6 @@ public class CopyDependenciesMojo extends AbstractFromDependenciesMojo { private final CopyUtil copyUtil; - private final ArtifactInstaller installer; - - private final RepositoryManager repositoryManager; - /** * Either append the artifact's baseVersion or uniqueVersion to the filename. Will only be used if * {@link #isStripVersion()} is {@code false}. @@ -108,21 +101,16 @@ public class CopyDependenciesMojo extends AbstractFromDependenciesMojo { protected boolean copySignatures; @Inject - @SuppressWarnings("checkstyle:ParameterNumber") public CopyDependenciesMojo( MavenSession session, BuildContext buildContext, MavenProject project, ResolverUtil resolverUtil, - RepositoryManager repositoryManager, ProjectBuilder projectBuilder, ArtifactHandlerManager artifactHandlerManager, - CopyUtil copyUtil, - ArtifactInstaller installer) { + CopyUtil copyUtil) { super(session, buildContext, project, resolverUtil, projectBuilder, artifactHandlerManager); this.copyUtil = copyUtil; - this.installer = installer; - this.repositoryManager = repositoryManager; } /** @@ -158,10 +146,9 @@ protected void doExecute() throws MojoExecutionException { artifact, isStripVersion(), this.prependGroupId, this.useBaseVersion, this.stripClassifier); } } else { - ProjectBuildingRequest buildingRequest = - repositoryManager.setLocalRepositoryBasedir(session.getProjectBuildingRequest(), outputDirectory); + RepositorySystemSession repositorySystemSession = getResolverUtil().localRepositorySession(outputDirectory); - artifacts.forEach(artifact -> installArtifact(artifact, buildingRequest)); + artifacts.forEach(artifact -> installArtifact(artifact, repositorySystemSession)); } Set skippedArtifacts = dss.getSkippedDependencies(); @@ -179,32 +166,32 @@ protected void doExecute() throws MojoExecutionException { /** * Install the artifact and the corresponding pom if copyPoms=true. */ - private void installArtifact(Artifact artifact, ProjectBuildingRequest buildingRequest) { + private void installArtifact(Artifact artifact, RepositorySystemSession repositorySystemSession) { try { - installer.install(buildingRequest, Collections.singletonList(artifact)); - installBaseSnapshot(artifact, buildingRequest); + getResolverUtil().installArtifact(artifact, repositorySystemSession); + installBaseSnapshot(artifact, repositorySystemSession); if (!"pom".equals(artifact.getType()) && isCopyPom()) { Artifact pomArtifact = getResolvedPomArtifact(artifact); if (pomArtifact != null && pomArtifact.getFile() != null && pomArtifact.getFile().exists()) { - installer.install(buildingRequest, Collections.singletonList(pomArtifact)); - installBaseSnapshot(pomArtifact, buildingRequest); + getResolverUtil().installArtifact(pomArtifact, repositorySystemSession); + installBaseSnapshot(pomArtifact, repositorySystemSession); } } - } catch (ArtifactInstallerException e) { + } catch (InstallationException e) { getLog().warn("unable to install " + artifact, e); } } - private void installBaseSnapshot(Artifact artifact, ProjectBuildingRequest buildingRequest) - throws ArtifactInstallerException { + private void installBaseSnapshot(Artifact artifact, RepositorySystemSession repositorySystemSession) + throws InstallationException { if (artifact.isSnapshot() && !artifact.getBaseVersion().equals(artifact.getVersion())) { String version = artifact.getVersion(); try { artifact.setVersion(artifact.getBaseVersion()); - installer.install(buildingRequest, Collections.singletonList(artifact)); + getResolverUtil().installArtifact(artifact, repositorySystemSession); } finally { artifact.setVersion(version); } diff --git a/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java b/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java index a2eb7b889..f7a2a62d7 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java +++ b/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java @@ -23,6 +23,7 @@ import javax.inject.Provider; import javax.inject.Singleton; +import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -42,6 +43,9 @@ import org.apache.maven.model.ReportPlugin; import org.apache.maven.model.Reporting; import org.apache.maven.project.MavenProject; +import org.apache.maven.project.artifact.ProjectArtifactMetadata; +import org.eclipse.aether.DefaultRepositoryCache; +import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; @@ -52,6 +56,9 @@ import org.eclipse.aether.collection.CollectResult; import org.eclipse.aether.collection.DependencyCollectionException; import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.installation.InstallRequest; +import org.eclipse.aether.installation.InstallationException; +import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.resolution.ArtifactDescriptorException; @@ -63,6 +70,7 @@ import org.eclipse.aether.resolution.DependencyRequest; import org.eclipse.aether.resolution.DependencyResolutionException; import org.eclipse.aether.resolution.DependencyResult; +import org.eclipse.aether.util.artifact.SubArtifact; import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator; /** @@ -82,6 +90,51 @@ public ResolverUtil(RepositorySystem repositorySystem, Provider ma this.mavenSessionProvider = mavenSessionProvider; } + /** + * Returns a copy of the current repository session using the supplied local repository directory. + * + * @param localRepositoryDirectory alternate local repository directory + * @return repository system session + */ + public RepositorySystemSession localRepositorySession(File localRepositoryDirectory) { + Objects.requireNonNull(localRepositoryDirectory, "localRepositoryDirectory"); + RepositorySystemSession repositorySystemSession = + mavenSessionProvider.get().getRepositorySession(); + String contentType = repositorySystemSession + .getLocalRepositoryManager() + .getRepository() + .getContentType(); + if ("enhanced".equals(contentType)) { + contentType = "default"; + } + + DefaultRepositorySystemSession newSession = new DefaultRepositorySystemSession(repositorySystemSession); + newSession.setCache(new DefaultRepositoryCache()); + newSession.setLocalRepositoryManager(repositorySystem.newLocalRepositoryManager( + newSession, new LocalRepository(localRepositoryDirectory, contentType))); + return newSession; + } + + /** + * Installs an artifact into the local repository associated with the supplied repository session. + * + * @param artifact artifact to install + * @param repositorySystemSession repository session containing the target local repository + * @throws InstallationException if the artifact could not be installed + */ + public void installArtifact( + org.apache.maven.artifact.Artifact artifact, RepositorySystemSession repositorySystemSession) + throws InstallationException { + Artifact resolverArtifact = RepositoryUtils.toArtifact(artifact); + InstallRequest installRequest = new InstallRequest().addArtifact(resolverArtifact); + artifact.getMetadataList().stream() + .filter(ProjectArtifactMetadata.class::isInstance) + .map(ProjectArtifactMetadata.class::cast) + .map(metadata -> new SubArtifact(resolverArtifact, "", "pom").setFile(metadata.getFile())) + .forEach(installRequest::addArtifact); + repositorySystem.install(repositorySystemSession, installRequest); + } + /** * Collects the transitive dependencies. * diff --git a/src/test/java/org/apache/maven/plugins/dependency/fromDependencies/TestCopyDependenciesMojo2.java b/src/test/java/org/apache/maven/plugins/dependency/fromDependencies/TestCopyDependenciesMojo2.java index a04ff76be..2cde33e2d 100644 --- a/src/test/java/org/apache/maven/plugins/dependency/fromDependencies/TestCopyDependenciesMojo2.java +++ b/src/test/java/org/apache/maven/plugins/dependency/fromDependencies/TestCopyDependenciesMojo2.java @@ -22,10 +22,12 @@ import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; +import java.util.Collections; import java.util.Set; import org.apache.maven.api.plugin.testing.InjectMojo; @@ -44,19 +46,16 @@ import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; import org.apache.maven.artifact.versioning.VersionRange; import org.apache.maven.bridge.MavenRepositorySystem; -import org.apache.maven.execution.MavenSession; import org.apache.maven.plugins.dependency.testUtils.DependencyArtifactStubFactory; import org.apache.maven.plugins.dependency.utils.DependencyUtil; -import org.apache.maven.project.DefaultProjectBuildingRequest; import org.apache.maven.project.MavenProject; -import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.project.artifact.ProjectArtifactMetadata; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.when; @MojoTest(realRepositorySession = true) class TestCopyDependenciesMojo2 { @@ -66,9 +65,6 @@ class TestCopyDependenciesMojo2 { private DependencyArtifactStubFactory stubFactory; - @Inject - private MavenSession session; - @Inject private MavenProject project; @@ -236,11 +232,6 @@ void testSubPerArtifactAndScope(CopyDependenciesMojo mojo) throws Exception { @Test @InjectMojo(goal = "copy-dependencies") void testRepositoryLayout(CopyDependenciesMojo mojo) throws Exception { - - ProjectBuildingRequest pbr = new DefaultProjectBuildingRequest(); - pbr.setRepositorySession(session.getRepositorySession()); - when(session.getProjectBuildingRequest()).thenReturn(pbr); - String baseVersion = "2.0-SNAPSHOT"; String groupId = "testGroupId"; String artifactId = "expanded-snapshot"; @@ -283,6 +274,33 @@ void testRepositoryLayout(CopyDependenciesMojo mojo) throws Exception { } } + @Test + @InjectMojo(goal = "copy-dependencies") + void testRepositoryLayoutInstallsProjectArtifactPom(CopyDependenciesMojo mojo) throws Exception { + Artifact artifact = stubFactory.createArtifact( + "testGroupId", + "artifact-with-project-pom", + VersionRange.createFromVersion("1.0"), + "compile", + "jar", + null, + false); + File pom = new File(tempDir, "artifact-with-project-pom-1.0.pom"); + Files.write( + pom.toPath(), + Collections.singletonList("4.0.0"), + StandardCharsets.UTF_8); + artifact.addMetadata(new ProjectArtifactMetadata(artifact, pom)); + mojo.getProject().setArtifacts(Collections.singleton(artifact)); + + mojo.useRepositoryLayout = true; + mojo.execute(); + + Path artifactDirectory = mojo.outputDirectory.toPath().resolve("testGroupId/artifact-with-project-pom/1.0"); + assertTrue(Files.isRegularFile(artifactDirectory.resolve("artifact-with-project-pom-1.0.jar"))); + assertTrue(Files.isRegularFile(artifactDirectory.resolve("artifact-with-project-pom-1.0.pom"))); + } + private Artifact createExpandedVersionArtifact( String baseVersion, String groupId, String artifactId, String scope, String type, String classifier) throws IOException { diff --git a/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java b/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java index 6948d753f..e95ed45d4 100644 --- a/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java +++ b/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java @@ -20,15 +20,22 @@ import javax.inject.Provider; +import java.io.File; import java.util.Collections; import java.util.stream.Stream; +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.handler.DefaultArtifactHandler; import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.artifact.ProjectArtifactMetadata; +import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.resolution.ArtifactDescriptorException; @@ -38,10 +45,13 @@ import org.eclipse.aether.resolution.ArtifactResult; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -50,6 +60,7 @@ import static org.assertj.core.api.Assertions.assertThatCode; import static org.junit.jupiter.params.provider.Arguments.of; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -57,6 +68,9 @@ @ExtendWith(MockitoExtension.class) class ResolverUtilTest { + @Mock + private RepositorySystem repositorySystem; + @Mock private MavenExecutionRequest executionRequest; @@ -64,17 +78,20 @@ class ResolverUtilTest { private RepositorySystemSession repositorySystemSession; @Mock - private MavenSession mavenSession; + private LocalRepositoryManager localRepositoryManager; @Mock - private Provider sessionProvider; + private MavenSession mavenSession; @Mock - private RepositorySystem repositorySystem; + private Provider sessionProvider; @InjectMocks private ResolverUtil resolverUtil; + @TempDir + private File tempDir; + public static Stream prepareRepositoryTest() { return Stream.of( @@ -161,4 +178,71 @@ void resolveArtifactWithFallbackWhenDescriptorCannotBeRead() throws Exception { .readArtifactDescriptor(eq(repositorySystemSession), any(ArtifactDescriptorRequest.class)); verify(repositorySystem).resolveArtifact(eq(repositorySystemSession), any(ArtifactRequest.class)); } + + @Test + void installArtifact() throws Exception { + org.apache.maven.artifact.Artifact artifact = new org.apache.maven.artifact.DefaultArtifact( + "org.apache.maven.plugins", "artifact", "1.0", null, "jar", null, new DefaultArtifactHandler("jar")); + artifact.setFile(new File(tempDir, "artifact-1.0.jar")); + Artifact resolverArtifact = RepositoryUtils.toArtifact(artifact); + + resolverUtil.installArtifact(artifact, repositorySystemSession); + + verify(repositorySystem) + .install( + eq(repositorySystemSession), + argThat(request -> request.getArtifacts().equals(Collections.singletonList(resolverArtifact)))); + } + + @Test + void installArtifactWithProjectArtifactMetadata() throws Exception { + org.apache.maven.artifact.Artifact artifact = new org.apache.maven.artifact.DefaultArtifact( + "org.apache.maven.plugins", "artifact", "1.0", null, "jar", null, new DefaultArtifactHandler("jar")); + File jar = new File(tempDir, "artifact-1.0.jar"); + File pom = new File(tempDir, "artifact-1.0.pom"); + artifact.setFile(jar); + artifact.addMetadata(new ProjectArtifactMetadata(artifact, pom)); + + resolverUtil.installArtifact(artifact, repositorySystemSession); + + ArgumentCaptor request = + ArgumentCaptor.forClass(org.eclipse.aether.installation.InstallRequest.class); + verify(repositorySystem).install(eq(repositorySystemSession), request.capture()); + assertThat(request.getValue().getArtifacts()) + .hasSize(2) + .anySatisfy(installed -> assertThat(installed.getFile()).isEqualTo(jar)) + .anySatisfy(installed -> { + assertThat(installed.getExtension()).isEqualTo("pom"); + assertThat(installed.getFile()).isEqualTo(pom); + }); + } + + @ParameterizedTest + @CsvSource({"simple, simple", "enhanced, default"}) + void localRepositorySessionPreservesRepositoryType(String currentType, String expectedType) { + LocalRepository currentRepository = new LocalRepository(tempDir, currentType); + LocalRepositoryManager newLocalRepositoryManager = org.mockito.Mockito.mock(LocalRepositoryManager.class); + when(sessionProvider.get()).thenReturn(mavenSession); + when(mavenSession.getRepositorySession()).thenReturn(repositorySystemSession); + when(repositorySystemSession.getLocalRepositoryManager()).thenReturn(localRepositoryManager); + when(localRepositoryManager.getRepository()).thenReturn(currentRepository); + when(repositorySystem.newLocalRepositoryManager( + any(DefaultRepositorySystemSession.class), any(LocalRepository.class))) + .thenReturn(newLocalRepositoryManager); + + RepositorySystemSession result = resolverUtil.localRepositorySession(new File(tempDir, "alternate")); + + assertThat(result.getLocalRepositoryManager()).isSameAs(newLocalRepositoryManager); + verify(repositorySystem) + .newLocalRepositoryManager( + any(DefaultRepositorySystemSession.class), + argThat(repository -> repository.getContentType().equals(expectedType))); + } + + @Test + void localRepositorySessionRequiresDirectory() { + assertThatCode(() -> resolverUtil.localRepositorySession(null)) + .isExactlyInstanceOf(NullPointerException.class) + .hasMessage("localRepositoryDirectory"); + } } From 98208d7565c94eefee69fae14f52fc88ea987ee5 Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Sun, 2 Aug 2026 11:13:51 +0200 Subject: [PATCH 2/5] [MDEP-858] Migrate dependency:purge-local-repository to Maven Resolver Replace the Maven Artifact Transfer resolvers while preserving dependency filtering, fallback resolution, and aggregated failure reporting. Re-resolve each artifact once because Resolver descriptor resolution also restores its POM. Fixes #1355. --- .../purge-local-repository-bad-dep/pom.xml | 6 + .../verify.groovy | 3 + .../projects/purge-local-repository/pom.xml | 12 ++ .../projects/purge-local-repository/setup.bsh | 39 +++++- .../purge-local-repository/verify.groovy | 12 ++ .../dependency/PurgeLocalRepositoryMojo.java | 117 +++++++----------- .../dependency/utils/ResolverUtil.java | 36 +++++- .../dependency/utils/ResolverUtilTest.java | 53 ++++++++ 8 files changed, 200 insertions(+), 78 deletions(-) diff --git a/src/it/projects/purge-local-repository-bad-dep/pom.xml b/src/it/projects/purge-local-repository-bad-dep/pom.xml index 27526b560..6162d0ca6 100644 --- a/src/it/projects/purge-local-repository-bad-dep/pom.xml +++ b/src/it/projects/purge-local-repository-bad-dep/pom.xml @@ -43,5 +43,11 @@ purge-local-repository 1.0 + + org.apache.maven + maven-model + 2.0.9 + sources + diff --git a/src/it/projects/purge-local-repository-bad-dep/verify.groovy b/src/it/projects/purge-local-repository-bad-dep/verify.groovy index 65b44f164..3d0c55a33 100644 --- a/src/it/projects/purge-local-repository-bad-dep/verify.groovy +++ b/src/it/projects/purge-local-repository-bad-dep/verify.groovy @@ -28,10 +28,13 @@ void checkFilePresence( String path ) checkFilePresence( "org/apache/maven/its/dependency/purge-local-repository/1.0/purge-local-repository-1.0.jar" ) checkFilePresence( "org/apache/maven/its/dependency/purge-local-repository/1.0/purge-local-repository-1.0.pom" ) +checkFilePresence( "org/apache/maven/maven-model/2.0.9/maven-model-2.0.9-sources.jar" ) String buildLog = new File( basedir, "build.log" ).getText( "UTF-8" ) assert buildLog.contains( 'Unable to resolve artifact: org.apache.maven.its.dependency:i-do-not-exist:jar:1.0' ) assert buildLog.contains( 'Purging artifact: org.apache.maven.its.dependency:purge-local-repository:jar:1.0' ) assert buildLog.contains( 'Resolving artifact: org.apache.maven.its.dependency:purge-local-repository:jar:1.0' ) +assert buildLog.contains( 'Purging artifact: org.apache.maven:maven-model:jar:sources:2.0.9' ) +assert buildLog.contains( 'Resolving artifact: org.apache.maven:maven-model:jar:sources:2.0.9' ) return true diff --git a/src/it/projects/purge-local-repository/pom.xml b/src/it/projects/purge-local-repository/pom.xml index da7b3fa72..e9efbaf81 100644 --- a/src/it/projects/purge-local-repository/pom.xml +++ b/src/it/projects/purge-local-repository/pom.xml @@ -42,6 +42,18 @@ purged 1.0 + + org.apache.maven.its.dependency + not-purged-test + 1.0 + test + + + org.apache.maven.its.dependency + not-purged-provided + 1.0 + provided + diff --git a/src/it/projects/purge-local-repository/setup.bsh b/src/it/projects/purge-local-repository/setup.bsh index 92b835d1a..fa8544a83 100644 --- a/src/it/projects/purge-local-repository/setup.bsh +++ b/src/it/projects/purge-local-repository/setup.bsh @@ -19,9 +19,42 @@ import java.io.*; -File purgedJar = new File( localRepositoryPath, "org/apache/maven/its/dependency/purged/1.0/purged-1.0.jar" ); +void createJar( String artifactId ) +{ + File jar = new File( localRepositoryPath, + "org/apache/maven/its/dependency/" + artifactId + "/1.0/" + artifactId + "-1.0.jar" ); + jar.getParentFile().mkdirs(); + jar.createNewFile(); +} -purgedJar.getParentFile().mkdirs(); -purgedJar.createNewFile(); +createJar( "purged" ); +createJar( "not-purged-test" ); +createJar( "not-purged-provided" ); +createJar( "not-purged-optional" ); + +File purgedPom = new File( localRepositoryPath, + "org/apache/maven/its/dependency/purged/1.0/purged-1.0.pom" ); +PrintWriter writer = new PrintWriter( purgedPom, "UTF-8" ); +try +{ + writer.println( "" ); + writer.println( " 4.0.0" ); + writer.println( " org.apache.maven.its.dependency" ); + writer.println( " purged" ); + writer.println( " 1.0" ); + writer.println( " " ); + writer.println( " " ); + writer.println( " org.apache.maven.its.dependency" ); + writer.println( " not-purged-optional" ); + writer.println( " 1.0" ); + writer.println( " true" ); + writer.println( " " ); + writer.println( " " ); + writer.println( "" ); +} +finally +{ + writer.close(); +} return true; diff --git a/src/it/projects/purge-local-repository/verify.groovy b/src/it/projects/purge-local-repository/verify.groovy index 7c97d11a0..71c2d66d6 100644 --- a/src/it/projects/purge-local-repository/verify.groovy +++ b/src/it/projects/purge-local-repository/verify.groovy @@ -26,7 +26,19 @@ void checkFileAbsence( String path ) } } +void checkFilePresence( String path ) +{ + File depJar = new File( localRepositoryPath, path ) + if ( !depJar.exists() ) + { + throw new Exception( "Dependency jar was purged: " + depJar ) + } +} + checkFileAbsence( "org/apache/maven/its/dependency/purged/1.0/purged-1.0.jar" ) +checkFilePresence( "org/apache/maven/its/dependency/not-purged-test/1.0/not-purged-test-1.0.jar" ) +checkFilePresence( "org/apache/maven/its/dependency/not-purged-provided/1.0/not-purged-provided-1.0.jar" ) +checkFilePresence( "org/apache/maven/its/dependency/not-purged-optional/1.0/not-purged-optional-1.0.jar" ) String buildLog = new File( basedir, "build.log" ).getText( "UTF-8" ) assert buildLog.contains( 'Deleting 1 transitive dependency for project test from ' ) diff --git a/src/main/java/org/apache/maven/plugins/dependency/PurgeLocalRepositoryMojo.java b/src/main/java/org/apache/maven/plugins/dependency/PurgeLocalRepositoryMojo.java index c5cfc6e85..8325af296 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/PurgeLocalRepositoryMojo.java +++ b/src/main/java/org/apache/maven/plugins/dependency/PurgeLocalRepositoryMojo.java @@ -28,10 +28,11 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import org.apache.maven.RepositoryUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.ArtifactUtils; -import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactResolutionException; import org.apache.maven.artifact.resolver.filter.ArtifactFilter; @@ -44,6 +45,7 @@ import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.dependency.utils.ResolverUtil; import org.apache.maven.project.MavenProject; import org.apache.maven.shared.artifact.filter.resolve.AbstractFilter; import org.apache.maven.shared.artifact.filter.resolve.AndFilter; @@ -53,16 +55,14 @@ import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter; import org.apache.maven.shared.artifact.filter.resolve.TransformableFilter; import org.apache.maven.shared.artifact.filter.resolve.transform.ArtifactIncludeFilterTransformer; -import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate; -import org.apache.maven.shared.transfer.artifact.TransferUtils; -import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver; -import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException; -import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult; -import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver; -import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolverException; +import org.apache.maven.shared.artifact.filter.resolve.transform.EclipseAetherFilterTransformer; import org.apache.maven.shared.utils.logging.MessageBuilder; import org.apache.maven.shared.utils.logging.MessageUtils; import org.codehaus.plexus.util.FileUtils; +import org.eclipse.aether.artifact.ArtifactTypeRegistry; +import org.eclipse.aether.graph.DependencyFilter; +import org.eclipse.aether.resolution.ArtifactDescriptorException; +import org.eclipse.aether.resolution.DependencyResolutionException; /** * When run on a project, remove the project dependencies from the local repository, and optionally re-resolve them. @@ -87,20 +87,7 @@ public class PurgeLocalRepositoryMojo extends AbstractMojo { private final MavenSession session; - /** - * Artifact handler manager. - */ - private final ArtifactHandlerManager artifactHandlerManager; - - /** - * The dependency resolver. - */ - private final DependencyResolver dependencyResolver; - - /** - * The artifact resolver used to re-resolve dependencies, if that option is enabled. - */ - private final ArtifactResolver artifactResolver; + private final ResolverUtil resolverUtil; /** * The Maven projects in the reactor. @@ -221,17 +208,10 @@ public class PurgeLocalRepositoryMojo extends AbstractMojo { private boolean skip; @Inject - public PurgeLocalRepositoryMojo( - MavenProject project, - MavenSession session, - ArtifactHandlerManager artifactHandlerManager, - DependencyResolver dependencyResolver, - ArtifactResolver artifactResolver) { + public PurgeLocalRepositoryMojo(MavenProject project, MavenSession session, ResolverUtil resolverUtil) { this.session = session; this.project = project; - this.artifactHandlerManager = artifactHandlerManager; - this.dependencyResolver = dependencyResolver; - this.artifactResolver = artifactResolver; + this.resolverUtil = resolverUtil; } /** @@ -521,18 +501,31 @@ private List parseIncludes(String theInclude) { private Set getFilteredResolvedArtifacts( MavenProject theProject, List dependencies, TransformableFilter filter) { - try { - Iterable results = dependencyResolver.resolveDependencies( - session.getProjectBuildingRequest(), theProject.getModel(), filter); - - Set resolvedArtifacts = new LinkedHashSet<>(); - - for (ArtifactResult artResult : results) { - resolvedArtifacts.add(artResult.getArtifact()); - } + ArtifactTypeRegistry artifactTypeRegistry = + session.getRepositorySession().getArtifactTypeRegistry(); + List resolverDependencies = dependencies.stream() + .map(dependency -> RepositoryUtils.toDependency(dependency, artifactTypeRegistry)) + .collect(Collectors.toList()); + List managedDependencies = theProject.getDependencyManagement() == null + ? null + : theProject.getDependencyManagement().getDependencies().stream() + .map(dependency -> RepositoryUtils.toDependency(dependency, artifactTypeRegistry)) + .collect(Collectors.toList()); + DependencyFilter dependencyFilter = filter.transform(new EclipseAetherFilterTransformer()); - return resolvedArtifacts; - } catch (DependencyResolverException e) { + try { + return resolverUtil + .resolveDependenciesForArtifact( + RepositoryUtils.toArtifact(theProject.getArtifact()), + resolverDependencies, + managedDependencies, + theProject.getRemoteProjectRepositories(), + dependencyFilter) + .stream() + .map(RepositoryUtils::toArtifact) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } catch (DependencyResolutionException e) { + getLog().debug("Unable to resolve all dependencies for: " + getProjectKey(theProject), e); getLog().info("Unable to resolve all dependencies for: " + getProjectKey(theProject) + ". Falling back to non-transitive mode for initial artifact resolution."); } @@ -541,23 +534,16 @@ private Set getFilteredResolvedArtifacts( ArtifactFilter artifactFilter = filter.transform(new ArtifactIncludeFilterTransformer()); - for (Dependency dependency : dependencies) { - DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate(); - coordinate.setGroupId(dependency.getGroupId()); - coordinate.setArtifactId(dependency.getArtifactId()); - coordinate.setVersion(dependency.getVersion()); - coordinate.setExtension(artifactHandlerManager - .getArtifactHandler(dependency.getType()) - .getExtension()); + for (org.eclipse.aether.graph.Dependency dependency : resolverDependencies) { + org.eclipse.aether.artifact.Artifact coordinate = dependency.getArtifact(); try { - Artifact artifact = artifactResolver - .resolveArtifact(session.getProjectBuildingRequest(), coordinate) - .getArtifact(); + Artifact artifact = RepositoryUtils.toArtifact( + resolverUtil.resolveArtifact(coordinate, theProject.getRemoteProjectRepositories())); if (artifactFilter.include(artifact)) { resolvedArtifacts.add(artifact); } - } catch (ArtifactResolverException e) { - getLog().debug("Unable to resolve artifact: " + coordinate); + } catch (org.eclipse.aether.resolution.ArtifactResolutionException | ArtifactDescriptorException e) { + getLog().debug("Unable to resolve artifact: " + coordinate, e); } } return resolvedArtifacts; @@ -606,27 +592,14 @@ private void purgeArtifacts(MavenProject theProject, Set artifacts) { private void reResolveArtifacts(MavenProject theProject, Set artifacts) throws ArtifactResolutionException { - // Always need to re-resolve the poms in case they were purged along with the artifact - // because Maven 2 will not automatically re-resolve them when resolving the artifact - for (Artifact artifact : artifacts) { - verbose("Resolving artifact: " + artifact.getId()); - - try { - // CHECKSTYLE_OFF: LineLength - artifactResolver.resolveArtifact( - session.getProjectBuildingRequest(), TransferUtils.toArtifactCoordinate(artifact)); - // CHECKSTYLE_ON: LineLength - } catch (ArtifactResolverException e) { - verbose(e.getMessage()); - } - } - List missingArtifacts = new ArrayList<>(); for (Artifact artifact : artifacts) { + verbose("Resolving artifact: " + artifact.getId()); try { - artifactResolver.resolveArtifact(session.getProjectBuildingRequest(), artifact); - } catch (ArtifactResolverException e) { + resolverUtil.resolveArtifact( + RepositoryUtils.toArtifact(artifact), theProject.getRemoteProjectRepositories()); + } catch (org.eclipse.aether.resolution.ArtifactResolutionException | ArtifactDescriptorException e) { verbose(e.getMessage()); missingArtifacts.add(artifact); } diff --git a/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java b/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java index f7a2a62d7..6cb01b8e1 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java +++ b/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java @@ -56,6 +56,7 @@ import org.eclipse.aether.collection.CollectResult; import org.eclipse.aether.collection.DependencyCollectionException; import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.installation.InstallRequest; import org.eclipse.aether.installation.InstallationException; import org.eclipse.aether.repository.LocalRepository; @@ -289,12 +290,41 @@ public List resolveDependenciesForArtifact( List managedDependencies, List remoteProjectRepositories) throws DependencyResolutionException { - MavenSession session = mavenSessionProvider.get(); - CollectRequest collectRequest = new CollectRequest(dependencies, managedDependencies, remoteProjectRepositories); collectRequest.setRootArtifact(rootArtifact); - DependencyRequest request = new DependencyRequest(collectRequest, null); + return resolveDependencies(collectRequest, null); + } + + /** + * Resolve transitive dependencies for artifact with managed dependencies. + * + * @param rootArtifact a root artifact to resolve + * @param dependencies a list of dependencies for artifact + * @param managedDependencies a list of managed dependencies for artifact + * @param remoteProjectRepositories remote repositories list + * @param dependencyFilter dependency filter, or {@code null} + * @return Resolved dependencies + * @throws DependencyResolutionException if the dependency tree could not be built or any dependency artifact could + * not be resolved + */ + public List resolveDependenciesForArtifact( + Artifact rootArtifact, + List dependencies, + List managedDependencies, + List remoteProjectRepositories, + DependencyFilter dependencyFilter) + throws DependencyResolutionException { + CollectRequest collectRequest = + new CollectRequest(new Dependency(rootArtifact, null), dependencies, remoteProjectRepositories); + collectRequest.setManagedDependencies(managedDependencies); + return resolveDependencies(collectRequest, dependencyFilter); + } + + private List resolveDependencies(CollectRequest collectRequest, DependencyFilter dependencyFilter) + throws DependencyResolutionException { + MavenSession session = mavenSessionProvider.get(); + DependencyRequest request = new DependencyRequest(collectRequest, dependencyFilter); DependencyResult result = repositorySystem.resolveDependencies(session.getRepositorySession(), request); return result.getArtifactResults().stream() .map(ArtifactResult::getArtifact) diff --git a/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java b/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java index e95ed45d4..ab059a747 100644 --- a/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java +++ b/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java @@ -34,6 +34,7 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.RemoteRepository; @@ -43,6 +44,8 @@ import org.eclipse.aether.resolution.ArtifactDescriptorResult; import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.resolution.DependencyRequest; +import org.eclipse.aether.resolution.DependencyResult; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; @@ -80,6 +83,9 @@ class ResolverUtilTest { @Mock private LocalRepositoryManager localRepositoryManager; + @Mock + private DependencyFilter dependencyFilter; + @Mock private MavenSession mavenSession; @@ -245,4 +251,51 @@ void localRepositorySessionRequiresDirectory() { .isExactlyInstanceOf(NullPointerException.class) .hasMessage("localRepositoryDirectory"); } + + @Test + void resolveDependenciesForArtifactWithFilter() throws Exception { + Artifact rootArtifact = new DefaultArtifact("org.apache.maven.plugins", "artifact", "jar", "1.0"); + DependencyResult dependencyResult = new DependencyResult(new DependencyRequest()); + when(sessionProvider.get()).thenReturn(mavenSession); + when(mavenSession.getRepositorySession()).thenReturn(repositorySystemSession); + when(repositorySystem.resolveDependencies(eq(repositorySystemSession), any(DependencyRequest.class))) + .thenReturn(dependencyResult); + + resolverUtil.resolveDependenciesForArtifact( + rootArtifact, + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + dependencyFilter); + + verify(repositorySystem) + .resolveDependencies( + eq(repositorySystemSession), + argThat(request -> request.getFilter() == dependencyFilter + && request.getCollectRequest().getRootArtifact() == null + && request.getCollectRequest() + .getRoot() + .getArtifact() + .equals(rootArtifact))); + } + + @Test + void resolveDependenciesForArtifactWithoutFilterDoesNotResolveRoot() throws Exception { + Artifact rootArtifact = new DefaultArtifact("org.apache.maven.plugins", "artifact", "jar", "1.0"); + DependencyResult dependencyResult = new DependencyResult(new DependencyRequest()); + when(sessionProvider.get()).thenReturn(mavenSession); + when(mavenSession.getRepositorySession()).thenReturn(repositorySystemSession); + when(repositorySystem.resolveDependencies(eq(repositorySystemSession), any(DependencyRequest.class))) + .thenReturn(dependencyResult); + + resolverUtil.resolveDependenciesForArtifact( + rootArtifact, Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + + verify(repositorySystem) + .resolveDependencies( + eq(repositorySystemSession), + argThat(request -> request.getFilter() == null + && request.getCollectRequest().getRoot() == null + && request.getCollectRequest().getRootArtifact().equals(rootArtifact))); + } } From dda0353400bbe09a689662676371ba0e8bfec649 Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Sun, 2 Aug 2026 11:35:03 +0200 Subject: [PATCH 3/5] [MDEP-858] Remove Maven Artifact Transfer dependency Drop the unused DependableCoordinate interface from ArtifactItem and remove the now-unused Maven Artifact Transfer dependency. Keep the artifact configuration getters unchanged. Fixes #1355. --- pom.xml | 15 --------------- .../fromConfiguration/ArtifactItem.java | 8 +------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 900d746f6..25ceff08f 100644 --- a/pom.xml +++ b/pom.xml @@ -238,21 +238,6 @@ under the License. maven-common-artifact-filters 3.4.0 - - org.apache.maven.shared - maven-artifact-transfer - 0.13.1 - - - org.apache.maven - maven-artifact - - - org.apache.maven - maven-core - - - org.apache.maven.shared maven-shared-utils diff --git a/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/ArtifactItem.java b/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/ArtifactItem.java index b697533ce..8426185d3 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/ArtifactItem.java +++ b/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/ArtifactItem.java @@ -25,7 +25,6 @@ import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.dependency.utils.DependencyUtil; -import org.apache.maven.shared.transfer.dependencies.DependableCoordinate; import org.codehaus.plexus.components.io.filemappers.FileMapper; /** @@ -34,7 +33,7 @@ * @author Brian Fox * @since 1.0 */ -public class ArtifactItem implements DependableCoordinate { +public class ArtifactItem { /** * Group ID of artifact. */ @@ -142,7 +141,6 @@ private String filterEmptyString(String in) { /** * @return returns the artifact ID */ - @Override public String getArtifactId() { return artifactId; } @@ -157,7 +155,6 @@ public void setArtifactId(String theArtifact) { /** * @return returns the group ID */ - @Override public String getGroupId() { return groupId; } @@ -172,7 +169,6 @@ public void setGroupId(String groupId) { /** * @return returns the type */ - @Override public String getType() { return type; } @@ -187,7 +183,6 @@ public void setType(String type) { /** * @return returns the version */ - @Override public String getVersion() { return version; } @@ -209,7 +204,6 @@ public String getBaseVersion() { /** * @return classifier */ - @Override public String getClassifier() { return classifier; } From 6ddd8df3a5c4f9c7b2233c2c40a816c804298fa5 Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Sun, 9 Aug 2026 23:29:27 +0200 Subject: [PATCH 4/5] [MDEP-858] Preserve get repository refresh policy Keep dependency:get compatible with its historical behavior by applying the always-update policy to user-supplied repositories. Leave the existing session-controlled policy available to other ResolverUtil callers. Fixes #1355. --- .../maven/plugins/dependency/GetMojo.java | 3 +- .../dependency/utils/ResolverUtil.java | 44 +++++++++++++++++-- .../maven/plugins/dependency/TestGetMojo.java | 25 +++++++++++ .../dependency/utils/ResolverUtilTest.java | 13 ++++++ 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/dependency/GetMojo.java b/src/main/java/org/apache/maven/plugins/dependency/GetMojo.java index fb8096c65..0601adf49 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/GetMojo.java +++ b/src/main/java/org/apache/maven/plugins/dependency/GetMojo.java @@ -31,6 +31,7 @@ import org.apache.maven.plugins.dependency.utils.ResolverUtil; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.resolution.ArtifactDescriptorException; import org.eclipse.aether.resolution.ArtifactResolutionException; import org.eclipse.aether.resolution.DependencyResolutionException; @@ -101,7 +102,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { List repositories; try { artifact = resolverUtil.createArtifactFromParams(paramArtifact); - repositories = resolverUtil.remoteRepositories(remoteRepositories); + repositories = resolverUtil.remoteRepositories(remoteRepositories, RepositoryPolicy.UPDATE_POLICY_ALWAYS); } catch (IllegalArgumentException e) { throw new MojoFailureException(e.getMessage(), e); } diff --git a/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java b/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java index 6cb01b8e1..240ba941b 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java +++ b/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java @@ -375,6 +375,23 @@ private Artifact toArtifact(Plugin plugin) { * @return a list of remote repositories */ public List remoteRepositories(List repositories) { + if (repositories == null || repositories.isEmpty()) { + return remoteRepositories(repositories, null); + } + MavenSession mavenSession = mavenSessionProvider.get(); + String updatePolicy = + mavenSession.getRequest().isUpdateSnapshots() ? RepositoryPolicy.UPDATE_POLICY_ALWAYS : null; + return remoteRepositories(repositories, updatePolicy); + } + + /** + * Prepare a remote repositories list for given descriptions and update policy. + * + * @param repositories remote repositories descriptions + * @param updatePolicy repository update policy, or {@code null} to use the Resolver default + * @return a list of remote repositories + */ + public List remoteRepositories(List repositories, String updatePolicy) { MavenSession mavenSession = mavenSessionProvider.get(); List projectRepositories = mavenSession.getCurrentProject().getRemoteProjectRepositories(); @@ -382,8 +399,9 @@ public List remoteRepositories(List repositories) { return projectRepositories; } - List repositoriesList = - repositories.stream().map(this::prepareRemoteRepository).collect(Collectors.toList()); + List repositoriesList = repositories.stream() + .map(repository -> prepareRemoteRepository(repository, updatePolicy)) + .collect(Collectors.toList()); repositoriesList = repositorySystem.newResolutionRepositories(mavenSession.getRepositorySession(), repositoriesList); @@ -394,8 +412,28 @@ public List remoteRepositories(List repositories) { // protected for testing purpose protected RemoteRepository prepareRemoteRepository(String repository) { + String[] items = parseRemoteRepository(repository); + MavenSession mavenSession = mavenSessionProvider.get(); + String updatePolicy = + mavenSession.getRequest().isUpdateSnapshots() ? RepositoryPolicy.UPDATE_POLICY_ALWAYS : null; + return prepareRemoteRepository(repository, items, updatePolicy); + } + + // protected for testing purpose + protected RemoteRepository prepareRemoteRepository(String repository, String updatePolicy) { + return prepareRemoteRepository(repository, parseRemoteRepository(repository), updatePolicy); + } + + private String[] parseRemoteRepository(String repository) { String[] items = Objects.requireNonNull(repository, "repository must be not null") .split("::"); + if (items.length > 3) { + throw new IllegalArgumentException("Invalid repository: " + repository); + } + return items; + } + + private RemoteRepository prepareRemoteRepository(String repository, String[] items, String updatePolicy) { String id = "temp"; String type = null; String url; @@ -427,8 +465,6 @@ protected RemoteRepository prepareRemoteRepository(String repository) { if (checksumPolicy == null) { checksumPolicy = RepositoryPolicy.CHECKSUM_POLICY_WARN; } - String updatePolicy = - mavenSession.getRequest().isUpdateSnapshots() ? RepositoryPolicy.UPDATE_POLICY_ALWAYS : null; RepositoryPolicy repositoryPolicy = new RepositoryPolicy(true, updatePolicy, checksumPolicy); RemoteRepository.Builder builder = new RemoteRepository.Builder(id, type, url); diff --git a/src/test/java/org/apache/maven/plugins/dependency/TestGetMojo.java b/src/test/java/org/apache/maven/plugins/dependency/TestGetMojo.java index 34ad79879..8d7bf33ef 100644 --- a/src/test/java/org/apache/maven/plugins/dependency/TestGetMojo.java +++ b/src/test/java/org/apache/maven/plugins/dependency/TestGetMojo.java @@ -34,12 +34,14 @@ import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.dependency.utils.ResolverUtil; import org.apache.maven.settings.Proxy; import org.apache.maven.settings.Server; import org.apache.maven.settings.Settings; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.util.repository.AuthenticationBuilder; import org.eclipse.aether.util.repository.DefaultAuthenticationSelector; import org.eclipse.aether.util.repository.DefaultProxySelector; @@ -62,6 +64,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @MojoTest(realRepositorySession = true) @@ -188,6 +194,25 @@ void testRemoteRepositories(GetMojo mojo) throws Exception { mojo.execute(); } + @Test + void testExplicitRemoteRepositoriesAlwaysRefresh() throws Exception { + ResolverUtil resolverUtil = mock(ResolverUtil.class); + when(resolverUtil.remoteRepositories(anyList(), eq(RepositoryPolicy.UPDATE_POLICY_ALWAYS))) + .thenReturn(Collections.emptyList()); + GetMojo mojo = new GetMojo(resolverUtil); + setRemoteRepositories(mojo, "central::default::https://repo.maven.apache.org/maven2"); + mojo.setGroupId("org.apache.maven"); + mojo.setArtifactId("maven-model"); + mojo.setVersion("2.0.9"); + + mojo.execute(); + + verify(resolverUtil) + .remoteRepositories( + Collections.singletonList("central::default::https://repo.maven.apache.org/maven2"), + RepositoryPolicy.UPDATE_POLICY_ALWAYS); + } + /** * Test that neither an artifact nor a complete GAV is a failure rather than an attempted resolution. */ diff --git a/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java b/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java index ab059a747..477401a47 100644 --- a/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java +++ b/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java @@ -158,6 +158,19 @@ void createArtifactFromInvalidString(String artifact) { .hasMessageContaining("Invalid artifact format: " + artifact); } + @Test + void prepareRepositoryUsesExplicitUpdatePolicy() { + when(sessionProvider.get()).thenReturn(mavenSession); + when(mavenSession.getRepositorySession()).thenReturn(repositorySystemSession); + + RemoteRepository remoteRepository = resolverUtil.prepareRemoteRepository( + "central::https://repo.maven.apache.org", RepositoryPolicy.UPDATE_POLICY_ALWAYS); + + assertThat(remoteRepository.getPolicy(false).getUpdatePolicy()) + .isEqualTo(RepositoryPolicy.UPDATE_POLICY_ALWAYS); + assertThat(remoteRepository.getPolicy(true).getUpdatePolicy()).isEqualTo(RepositoryPolicy.UPDATE_POLICY_ALWAYS); + } + @Test void prepareRepositoryWithNull() { assertThatCode(() -> resolverUtil.prepareRemoteRepository(null)) From a79a2ec63cf4ea04f3e5605eea6713e6035ffca6 Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Mon, 10 Aug 2026 00:24:46 +0200 Subject: [PATCH 5/5] [MDEP-858] Preserve artifact coordinate fallbacks Use separately configured packaging and classifier values when the artifact string omits those segments. Keep packaging and classifier values from explicit artifact coordinates at higher precedence. Fixes #1355. --- .../dependency/utils/ResolverUtil.java | 9 ++-- .../dependency/utils/ResolverUtilTest.java | 46 +++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java b/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java index 240ba941b..16e2457a5 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java +++ b/src/main/java/org/apache/maven/plugins/dependency/utils/ResolverUtil.java @@ -483,7 +483,7 @@ private RemoteRepository prepareRemoteRepository(String repository, String[] ite public Artifact createArtifactFromParams(ParamArtifact paramArtifact) { Objects.requireNonNull(paramArtifact); if (paramArtifact.getArtifact() != null) { - return createArtifactFromString(paramArtifact.getArtifact()); + return createArtifactFromString(paramArtifact); } else { ArtifactType artifactType = getArtifactType(paramArtifact.getPackaging()); return new DefaultArtifact( @@ -496,16 +496,17 @@ public Artifact createArtifactFromParams(ParamArtifact paramArtifact) { } } - private Artifact createArtifactFromString(String artifact) { + private Artifact createArtifactFromString(ParamArtifact paramArtifact) { // groupId:artifactId:version[:packaging[:classifier]]. + String artifact = paramArtifact.getArtifact(); String[] items = artifact.split(":"); if (items.length < 3 || items.length > 5) { throw new IllegalArgumentException("Invalid artifact format: " + artifact + ", expected groupId:artifactId:version[:packaging[:classifier]]"); } - ArtifactType artifactType = getArtifactType(items.length > 3 ? items[3] : null); - String classifier = items.length > 4 ? items[4] : null; + ArtifactType artifactType = getArtifactType(items.length > 3 ? items[3] : paramArtifact.getPackaging()); + String classifier = items.length > 4 ? items[4] : paramArtifact.getClassifier(); return new DefaultArtifact(items[0], items[1], classifier, artifactType.getExtension(), items[2], artifactType); } diff --git a/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java b/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java index 477401a47..6f28c0714 100644 --- a/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java +++ b/src/test/java/org/apache/maven/plugins/dependency/utils/ResolverUtilTest.java @@ -33,6 +33,8 @@ import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.ArtifactType; +import org.eclipse.aether.artifact.ArtifactTypeRegistry; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.repository.LocalRepository; @@ -80,6 +82,12 @@ class ResolverUtilTest { @Mock private RepositorySystemSession repositorySystemSession; + @Mock + private ArtifactTypeRegistry artifactTypeRegistry; + + @Mock + private ArtifactType artifactType; + @Mock private LocalRepositoryManager localRepositoryManager; @@ -158,6 +166,36 @@ void createArtifactFromInvalidString(String artifact) { .hasMessageContaining("Invalid artifact format: " + artifact); } + @Test + void createArtifactFromShortCoordinatesUsesSeparatePackagingAndClassifier() { + prepareArtifactTypeRegistry(); + ParamArtifact paramArtifact = new ParamArtifact(); + paramArtifact.setArtifact("groupId:artifactId:1.0"); + paramArtifact.setPackaging("test-jar"); + paramArtifact.setClassifier("tests"); + + Artifact artifact = resolverUtil.createArtifactFromParams(paramArtifact); + + assertThat(artifact.getExtension()).isEqualTo("jar"); + assertThat(artifact.getClassifier()).isEqualTo("tests"); + verify(artifactTypeRegistry).get("test-jar"); + } + + @Test + void createArtifactFromExplicitCoordinatesOverridesSeparatePackagingAndClassifier() { + prepareArtifactTypeRegistry(); + ParamArtifact paramArtifact = new ParamArtifact(); + paramArtifact.setArtifact("groupId:artifactId:1.0:jar:sources"); + paramArtifact.setPackaging("test-jar"); + paramArtifact.setClassifier("tests"); + + Artifact artifact = resolverUtil.createArtifactFromParams(paramArtifact); + + assertThat(artifact.getExtension()).isEqualTo("jar"); + assertThat(artifact.getClassifier()).isEqualTo("sources"); + verify(artifactTypeRegistry).get("jar"); + } + @Test void prepareRepositoryUsesExplicitUpdatePolicy() { when(sessionProvider.get()).thenReturn(mavenSession); @@ -311,4 +349,12 @@ void resolveDependenciesForArtifactWithoutFilterDoesNotResolveRoot() throws Exce && request.getCollectRequest().getRoot() == null && request.getCollectRequest().getRootArtifact().equals(rootArtifact))); } + + private void prepareArtifactTypeRegistry() { + when(sessionProvider.get()).thenReturn(mavenSession); + when(mavenSession.getRepositorySession()).thenReturn(repositorySystemSession); + when(repositorySystemSession.getArtifactTypeRegistry()).thenReturn(artifactTypeRegistry); + when(artifactTypeRegistry.get(any())).thenReturn(artifactType); + when(artifactType.getExtension()).thenReturn("jar"); + } }