From e7fccdc070a2e1e50ad871b6f7bd6bb8770abed2 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 15 Jul 2026 14:35:49 -0400 Subject: [PATCH 1/2] Fix copy and unpack with broken artifact parents Preserve descriptor-based relocation while falling back to direct artifact resolution when an artifact descriptor cannot be read. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 32540765-8b96-4d21-aafb-e09cc896cd8f --- .../repository/broken-parent-artifact-1.0.pom | 34 ++++++++++++ .../copy-broken-parent/invoker.properties | 18 ++++++ src/it/projects/copy-broken-parent/pom.xml | 55 +++++++++++++++++++ .../projects/copy-broken-parent/verify.groovy | 20 +++++++ .../AbstractFromConfigurationMojo.java | 5 +- .../dependency/utils/ResolverUtil.java | 32 ++++++++++- .../dependency/utils/ResolverUtilTest.java | 33 +++++++++++ 7 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 src/it/mrm/repository/broken-parent-artifact-1.0.pom create mode 100644 src/it/projects/copy-broken-parent/invoker.properties create mode 100644 src/it/projects/copy-broken-parent/pom.xml create mode 100644 src/it/projects/copy-broken-parent/verify.groovy diff --git a/src/it/mrm/repository/broken-parent-artifact-1.0.pom b/src/it/mrm/repository/broken-parent-artifact-1.0.pom new file mode 100644 index 000000000..7cc7c88a1 --- /dev/null +++ b/src/it/mrm/repository/broken-parent-artifact-1.0.pom @@ -0,0 +1,34 @@ + + + + 4.0.0 + + + org.apache.maven.its.dependency + missing-parent + 1.0-SNAPSHOT + + + broken-parent-artifact + 1.0 + pom + diff --git a/src/it/projects/copy-broken-parent/invoker.properties b/src/it/projects/copy-broken-parent/invoker.properties new file mode 100644 index 000000000..101be4e95 --- /dev/null +++ b/src/it/projects/copy-broken-parent/invoker.properties @@ -0,0 +1,18 @@ +# 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 process-sources diff --git a/src/it/projects/copy-broken-parent/pom.xml b/src/it/projects/copy-broken-parent/pom.xml new file mode 100644 index 000000000..a4d5b94a1 --- /dev/null +++ b/src/it/projects/copy-broken-parent/pom.xml @@ -0,0 +1,55 @@ + + + + 4.0.0 + + org.apache.maven.its.dependency + copy-broken-parent + 1.0-SNAPSHOT + + + + + maven-dependency-plugin + @project.version@ + + + copy + + copy + + + + + org.apache.maven.its.dependency + broken-parent-artifact + 1.0 + pom + + + + + + + + + diff --git a/src/it/projects/copy-broken-parent/verify.groovy b/src/it/projects/copy-broken-parent/verify.groovy new file mode 100644 index 000000000..713f2705b --- /dev/null +++ b/src/it/projects/copy-broken-parent/verify.groovy @@ -0,0 +1,20 @@ +/* + * 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. + */ + +assert new File(basedir, 'target/dependency/broken-parent-artifact-1.0.pom').isFile() diff --git a/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/AbstractFromConfigurationMojo.java b/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/AbstractFromConfigurationMojo.java index 1c5a55132..f5a42f813 100644 --- a/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/AbstractFromConfigurationMojo.java +++ b/src/main/java/org/apache/maven/plugins/dependency/fromConfiguration/AbstractFromConfigurationMojo.java @@ -46,7 +46,6 @@ import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; -import org.eclipse.aether.resolution.ArtifactDescriptorException; import org.eclipse.aether.resolution.ArtifactResolutionException; import org.sonatype.plexus.build.incremental.BuildContext; @@ -256,11 +255,11 @@ protected Artifact getArtifact(ArtifactItem artifactItem) throws MojoExecutionEx RepositorySystemSession repositorySession = createSystemSessionForLocalRepo(); - org.eclipse.aether.artifact.Artifact resolvedArtifact = resolverUtil.resolveArtifact( + org.eclipse.aether.artifact.Artifact resolvedArtifact = resolverUtil.resolveArtifactWithFallback( artifact, getProject().getRemoteProjectRepositories(), repositorySession); return RepositoryUtils.toArtifact(resolvedArtifact); - } catch (ArtifactResolutionException | ArtifactDescriptorException e) { + } catch (ArtifactResolutionException e) { throw new MojoExecutionException("Unable to find/resolve artifact.", 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 60ca42c97..9616f1a57 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 @@ -133,9 +133,37 @@ public Artifact resolveArtifact( ArtifactDescriptorResult artifactDescriptorResult = repositorySystem.readArtifactDescriptor( session, new ArtifactDescriptorRequest(artifact, repositories, null)); - Artifact artifactToResolve = artifactDescriptorResult.getArtifact(); + return resolveArtifactDirectly(artifactDescriptorResult.getArtifact(), repositories, session); + } - ArtifactRequest request = new ArtifactRequest(artifactToResolve, repositories, null); + /** + * Resolve a given artifact, falling back to its original coordinates when its descriptor cannot be read. + * + * @param artifact an artifact to resolve + * @param repositories remote repositories list + * @param session a repository system session + * @return resolved artifact + * @throws ArtifactResolutionException if the artifact could not be resolved + */ + public Artifact resolveArtifactWithFallback( + Artifact artifact, List repositories, RepositorySystemSession session) + throws ArtifactResolutionException { + try { + return resolveArtifact(artifact, repositories, session); + } catch (ArtifactDescriptorException descriptorException) { + try { + return resolveArtifactDirectly(artifact, repositories, session); + } catch (ArtifactResolutionException resolutionException) { + resolutionException.addSuppressed(descriptorException); + throw resolutionException; + } + } + } + + private Artifact resolveArtifactDirectly( + Artifact artifact, List repositories, RepositorySystemSession session) + throws ArtifactResolutionException { + ArtifactRequest request = new ArtifactRequest(artifact, repositories, null); ArtifactResult result = repositorySystem.resolveArtifact(session, request); return result.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 4359c6949..f24415b0a 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,13 +20,22 @@ import javax.inject.Provider; +import java.util.Collections; import java.util.stream.Stream; import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenSession; +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.RemoteRepository; import org.eclipse.aether.repository.RepositoryPolicy; +import org.eclipse.aether.resolution.ArtifactDescriptorException; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.eclipse.aether.resolution.ArtifactDescriptorResult; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResult; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -39,6 +48,9 @@ import static org.assertj.core.api.Assertions.assertThat; 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.eq; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -56,6 +68,9 @@ class ResolverUtilTest { @Mock private Provider sessionProvider; + @Mock + private RepositorySystem repositorySystem; + @InjectMocks private ResolverUtil resolverUtil; @@ -105,4 +120,22 @@ void prepareRepositoryWithNull() { .isExactlyInstanceOf(NullPointerException.class) .hasMessage("repository must be not null"); } + + @Test + void resolveArtifactWithFallbackWhenDescriptorCannotBeRead() throws Exception { + Artifact artifact = new DefaultArtifact("groupId", "artifactId", null, "jar", "1.0"); + ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); + ArtifactDescriptorException descriptorException = + new ArtifactDescriptorException(new ArtifactDescriptorResult(descriptorRequest)); + ArtifactResult artifactResult = new ArtifactResult(new ArtifactRequest()).setArtifact(artifact); + + when(repositorySystem.readArtifactDescriptor(eq(repositorySystemSession), any(ArtifactDescriptorRequest.class))) + .thenThrow(descriptorException); + when(repositorySystem.resolveArtifact(eq(repositorySystemSession), any(ArtifactRequest.class))) + .thenReturn(artifactResult); + + assertThat(resolverUtil.resolveArtifactWithFallback(artifact, Collections.emptyList(), repositorySystemSession)) + .isSameAs(artifact); + verify(repositorySystem).resolveArtifact(eq(repositorySystemSession), any(ArtifactRequest.class)); + } } From 96baf6ead38e6f0927d9bbcb0f39f3f0763ff482 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Fri, 17 Jul 2026 23:38:10 -0400 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../apache/maven/plugins/dependency/utils/ResolverUtilTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f24415b0a..c2a01163b 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 @@ -136,6 +136,6 @@ void resolveArtifactWithFallbackWhenDescriptorCannotBeRead() throws Exception { assertThat(resolverUtil.resolveArtifactWithFallback(artifact, Collections.emptyList(), repositorySystemSession)) .isSameAs(artifact); + verify(repositorySystem).readArtifactDescriptor(eq(repositorySystemSession), any(ArtifactDescriptorRequest.class)); verify(repositorySystem).resolveArtifact(eq(repositorySystemSession), any(ArtifactRequest.class)); - } }