This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/master by this push:
new f6343606de Introduce RepositoryAwareRequest interface to consolidate
repository handling (#11238)
f6343606de is described below
commit f6343606dea8ddda99c01c14890d541c7585fcef
Author: Tamas Cservenak <[email protected]>
AuthorDate: Fri Oct 10 09:30:49 2025 +0200
Introduce RepositoryAwareRequest interface to consolidate repository
handling (#11238)
This change introduces a new RepositoryAwareRequest interface that
consolidates
repository handling across multiple Maven service request types, addressing
issues with duplicate repositories being passed to resolvers.
Key changes:
* Add RepositoryAwareRequest interface with repository validation:
- Provides getRepositories() method and validate() logic
- Prevents duplicate repositories and null entries
- Consolidates common repository functionality
* Refactor service request interfaces to extend RepositoryAwareRequest:
- ArtifactResolverRequest, DependencyResolverRequest, ModelBuilderRequest
- ProjectBuilderRequest, VersionRangeResolverRequest,
VersionResolverRequest
- Apply repository validation in all implementations
* Fix repository leakage in DefaultProjectBuilder:
- Store project-specific repositories in BuildSession
- Implement proper repository merging based on strategy
- Prevent cross-contamination between sibling projects
* Update resolvers to use toResolvingRepositories() for consistent handling
* Add RepositoryLeakageTest to verify proper isolation between projects
This eliminates duplicate repositories being sent to resolvers and ensures
consistent repository handling across all Maven service requests.
---
.../api/services/ArtifactResolverRequest.java | 7 +-
.../api/services/DependencyResolverRequest.java | 7 +-
.../maven/api/services/ModelBuilderRequest.java | 7 +-
.../maven/api/services/ProjectBuilderRequest.java | 4 +-
.../maven/api/services/RepositoryAwareRequest.java | 117 +++++++++++
.../api/services/VersionRangeResolverRequest.java | 7 +-
.../maven/api/services/VersionResolverRequest.java | 7 +-
.../maven/project/DefaultProjectBuilder.java | 48 ++++-
.../maven/project/RepositoryLeakageTest.java | 214 +++++++++++++++++++++
.../org/apache/maven/impl/AbstractSession.java | 6 +
.../apache/maven/impl/DefaultArtifactResolver.java | 2 +-
.../maven/impl/DefaultDependencyResolver.java | 2 +-
.../maven/impl/DefaultVersionRangeResolver.java | 2 +-
.../apache/maven/impl/DefaultVersionResolver.java | 2 +-
.../org/apache/maven/impl/InternalSession.java | 2 +
15 files changed, 398 insertions(+), 36 deletions(-)
diff --git
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java
index fb012fab30..7e832a95e4 100644
---
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java
+++
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java
@@ -40,14 +40,11 @@
*/
@Experimental
@Immutable
-public interface ArtifactResolverRequest extends Request<Session> {
+public interface ArtifactResolverRequest extends RepositoryAwareRequest {
@Nonnull
Collection<? extends ArtifactCoordinates> getCoordinates();
- @Nullable
- List<RemoteRepository> getRepositories();
-
@Nonnull
static ArtifactResolverRequestBuilder builder() {
return new ArtifactResolverRequestBuilder();
@@ -127,7 +124,7 @@ private static class DefaultArtifactResolverRequest extends
BaseRequest<Session>
@Nonnull List<RemoteRepository> repositories) {
super(session, trace);
this.coordinates = List.copyOf(requireNonNull(coordinates,
"coordinates cannot be null"));
- this.repositories = repositories;
+ this.repositories = validate(repositories);
}
@Nonnull
diff --git
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
index e9b3ab956b..5be250824d 100644
---
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
+++
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
@@ -55,7 +55,7 @@
*/
@Experimental
@Immutable
-public interface DependencyResolverRequest extends Request<Session> {
+public interface DependencyResolverRequest extends RepositoryAwareRequest {
enum RequestType {
COLLECT,
@@ -119,9 +119,6 @@ enum RequestType {
@Nullable
Version getTargetVersion();
- @Nullable
- List<RemoteRepository> getRepositories();
-
@Nonnull
static DependencyResolverRequestBuilder builder() {
return new DependencyResolverRequestBuilder();
@@ -479,7 +476,7 @@ public String toString() {
this.pathScope = requireNonNull(pathScope, "pathScope cannot
be null");
this.pathTypeFilter = (pathTypeFilter != null) ?
pathTypeFilter : DEFAULT_FILTER;
this.targetVersion = targetVersion;
- this.repositories = repositories;
+ this.repositories = validate(repositories);
if (verbose && requestType != RequestType.COLLECT) {
throw new IllegalArgumentException("verbose cannot only be
true when collecting dependencies");
}
diff --git
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java
index 14141a6d0c..826ffe8fc4 100644
---
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java
+++
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java
@@ -43,7 +43,7 @@
*/
@Experimental
@Immutable
-public interface ModelBuilderRequest extends Request<Session> {
+public interface ModelBuilderRequest extends RepositoryAwareRequest {
/**
* The possible request types for building a model.
@@ -133,9 +133,6 @@ enum RepositoryMerging {
@Nonnull
RepositoryMerging getRepositoryMerging();
- @Nullable
- List<RemoteRepository> getRepositories();
-
@Nullable
ModelTransformer getLifecycleBindingsInjector();
@@ -338,7 +335,7 @@ private static class DefaultModelBuilderRequest extends
BaseRequest<Session> imp
systemProperties != null ?
Map.copyOf(systemProperties) : session.getSystemProperties();
this.userProperties = userProperties != null ?
Map.copyOf(userProperties) : session.getUserProperties();
this.repositoryMerging = repositoryMerging;
- this.repositories = repositories != null ?
List.copyOf(repositories) : null;
+ this.repositories = repositories != null ?
List.copyOf(validate(repositories)) : null;
this.lifecycleBindingsInjector = lifecycleBindingsInjector;
}
diff --git
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderRequest.java
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderRequest.java
index 82129b4f1b..307ee19559 100644
---
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderRequest.java
+++
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderRequest.java
@@ -43,7 +43,7 @@
*/
@Experimental
@Immutable
-public interface ProjectBuilderRequest extends Request<Session> {
+public interface ProjectBuilderRequest extends RepositoryAwareRequest {
/**
* Gets the path to the project to build.
@@ -265,7 +265,7 @@ private static class DefaultProjectBuilderRequest extends
BaseRequest<Session>
this.allowStubModel = allowStubModel;
this.recursive = recursive;
this.processPlugins = processPlugins;
- this.repositories = repositories;
+ this.repositories = validate(repositories);
}
@Nonnull
diff --git
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/RepositoryAwareRequest.java
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/RepositoryAwareRequest.java
new file mode 100644
index 0000000000..f948ecdea4
--- /dev/null
+++
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/RepositoryAwareRequest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.api.services;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.maven.api.RemoteRepository;
+import org.apache.maven.api.Session;
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Immutable;
+import org.apache.maven.api.annotations.Nullable;
+
+/**
+ * Base interface for service requests that involve remote repository
operations.
+ * This interface provides common functionality for requests that need to
specify
+ * and validate remote repositories for artifact resolution, dependency
collection,
+ * model building, and other Maven operations.
+ *
+ * <p>Implementations of this interface can specify a list of remote
repositories
+ * to be used during the operation. If no repositories are specified (null),
+ * the session's default remote repositories will be used. The repositories
+ * are validated to ensure they don't contain duplicates or null entries.
+ *
+ * <p>Remote repositories are used for:
+ * <ul>
+ * <li>Resolving artifacts and their metadata</li>
+ * <li>Downloading parent POMs and dependency POMs</li>
+ * <li>Retrieving version information and ranges</li>
+ * <li>Accessing plugin artifacts and their dependencies</li>
+ * </ul>
+ *
+ * <p>Repository validation ensures data integrity by:
+ * <ul>
+ * <li>Preventing duplicate repositories that could cause confusion</li>
+ * <li>Rejecting null repository entries that would cause failures</li>
+ * <li>Maintaining consistent repository ordering for reproducible
builds</li>
+ * </ul>
+ *
+ * @since 4.0.0
+ * @see RemoteRepository
+ * @see Session#getRemoteRepositories()
+ */
+@Experimental
+@Immutable
+public interface RepositoryAwareRequest extends Request<Session> {
+
+ /**
+ * Returns the list of remote repositories to be used for this request.
+ *
+ * <p>If this method returns {@code null}, the session's default remote
repositories
+ * will be used. If a non-null list is returned, it will be used instead
of the
+ * session's repositories, allowing for request-specific repository
configuration.
+ *
+ * <p>The returned list should not contain duplicate repositories (based
on their
+ * equality) or null entries, as these will cause validation failures when
the
+ * request is processed.
+ *
+ * @return the list of remote repositories to use, or {@code null} to use
session defaults
+ * @see Session#getRemoteRepositories()
+ */
+ @Nullable
+ List<RemoteRepository> getRepositories();
+
+ /**
+ * Validates a list of remote repositories to ensure data integrity.
+ *
+ * <p>This method performs the following validations:
+ * <ul>
+ * <li>Allows null input (returns null)</li>
+ * <li>Ensures no duplicate repositories exist in the list</li>
+ * <li>Ensures no null repository entries exist in the list</li>
+ * </ul>
+ *
+ * <p>Duplicate detection is based on the {@code
RemoteRepository#equals(Object)}
+ * method, which typically compares repository IDs and URLs.
+ *
+ * @param repositories the list of repositories to validate, may be {@code
null}
+ * @return the same list if validation passes, or {@code null} if input
was {@code null}
+ * @throws IllegalArgumentException if the list contains duplicate
repositories
+ * @throws IllegalArgumentException if the list contains null repository
entries
+ */
+ default List<RemoteRepository> validate(List<RemoteRepository>
repositories) {
+ if (repositories == null) {
+ return null;
+ }
+ HashSet<RemoteRepository> set = new HashSet<>(repositories);
+ if (repositories.size() != set.size()) {
+ throw new IllegalArgumentException(
+ "Repository list contains duplicate entries. Each
repository must be unique based on its ID and URL. "
+ + "Found " + repositories.size() + " repositories
but only " + set.size()
+ + " unique entries.");
+ }
+ if (repositories.stream().anyMatch(Objects::isNull)) {
+ throw new IllegalArgumentException(
+ "Repository list contains null entries. All repository
entries must be non-null RemoteRepository instances.");
+ }
+ return repositories;
+ }
+}
diff --git
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverRequest.java
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverRequest.java
index 52abe9e89a..2f69c574a3 100644
---
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverRequest.java
+++
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverRequest.java
@@ -36,14 +36,11 @@
* @since 4.0.0
*/
@Experimental
-public interface VersionRangeResolverRequest extends Request<Session> {
+public interface VersionRangeResolverRequest extends RepositoryAwareRequest {
@Nonnull
ArtifactCoordinates getArtifactCoordinates();
- @Nullable
- List<RemoteRepository> getRepositories();
-
@Nonnull
static VersionRangeResolverRequest build(
@Nonnull Session session, @Nonnull ArtifactCoordinates
artifactCoordinates) {
@@ -111,7 +108,7 @@ private static class DefaultVersionResolverRequest extends
BaseRequest<Session>
@Nullable List<RemoteRepository> repositories) {
super(session, trace);
this.artifactCoordinates = artifactCoordinates;
- this.repositories = repositories;
+ this.repositories = validate(repositories);
}
@Nonnull
diff --git
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverRequest.java
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverRequest.java
index c8dee58a8f..b510dcc2de 100644
---
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverRequest.java
+++
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverRequest.java
@@ -36,14 +36,11 @@
* @since 4.0.0
*/
@Experimental
-public interface VersionResolverRequest extends Request<Session> {
+public interface VersionResolverRequest extends RepositoryAwareRequest {
@Nonnull
ArtifactCoordinates getArtifactCoordinates();
- @Nullable
- List<RemoteRepository> getRepositories();
-
@Nonnull
static VersionResolverRequest build(@Nonnull Session session, @Nonnull
ArtifactCoordinates artifactCoordinates) {
return builder()
@@ -113,7 +110,7 @@ private static class DefaultVersionResolverRequest extends
BaseRequest<Session>
@Nullable List<RemoteRepository> repositories) {
super(session, trace);
this.artifactCoordinates = artifactCoordinates;
- this.repositories = repositories;
+ this.repositories = validate(repositories);
}
@Nonnull
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
index ecf97e4b1b..1de9eeccc5 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
@@ -184,7 +184,7 @@ public ProjectBuildingResult build(Artifact artifact,
ProjectBuildingRequest req
public ProjectBuildingResult build(Artifact artifact, boolean
allowStubModel, ProjectBuildingRequest request)
throws ProjectBuildingException {
try (BuildSession bs = new BuildSession(request)) {
- return bs.build(false, artifact, allowStubModel);
+ return bs.build(false, artifact, allowStubModel,
request.getRemoteRepositories());
}
}
@@ -318,6 +318,18 @@ class BuildSession implements AutoCloseable {
private final ModelBuilder.ModelBuilderSession modelBuilderSession;
private final Map<String, MavenProject> projectIndex = new
ConcurrentHashMap<>(256);
+ // Store computed repositories per project to avoid leakage between
projects
+ private final Map<String, List<ArtifactRepository>>
projectRepositories = new ConcurrentHashMap<>();
+
+ /**
+ * Get the effective repositories for a project. If project-specific
repositories
+ * have been computed and stored, use those; otherwise fall back to
request repositories.
+ */
+ private List<ArtifactRepository> getEffectiveRepositories(String
projectId) {
+ List<ArtifactRepository> stored =
projectRepositories.get(projectId);
+ return stored != null ? stored : request.getRemoteRepositories();
+ }
+
BuildSession(ProjectBuildingRequest request) {
this.request = request;
InternalSession session =
InternalSession.from(request.getRepositorySession());
@@ -429,7 +441,8 @@ ProjectBuildingResult build(boolean parent, Path pomFile,
ModelSource modelSourc
}
}
- ProjectBuildingResult build(boolean parent, Artifact artifact, boolean
allowStubModel)
+ ProjectBuildingResult build(
+ boolean parent, Artifact artifact, boolean allowStubModel,
List<ArtifactRepository> repositories)
throws ProjectBuildingException {
org.eclipse.aether.artifact.Artifact pomArtifact =
RepositoryUtils.toArtifact(artifact);
pomArtifact = ArtifactDescriptorUtils.toPomArtifact(pomArtifact);
@@ -438,9 +451,10 @@ ProjectBuildingResult build(boolean parent, Artifact
artifact, boolean allowStub
try {
ArtifactCoordinates coordinates =
session.createArtifactCoordinates(session.getArtifact(pomArtifact));
+ // Use provided repositories if available, otherwise fall back
to request repositories
ArtifactResolverRequest req = ArtifactResolverRequest.builder()
.session(session)
- .repositories(request.getRemoteRepositories().stream()
+ .repositories(repositories.stream()
.map(RepositoryUtils::toRepo)
.map(session::getRemoteRepository)
.toList())
@@ -850,7 +864,30 @@ private void initParent(MavenProject project,
ModelBuilderResult result) {
// remote repositories with those found in the pom.xml,
along with the existing externally
// defined repositories.
//
-
request.getRemoteRepositories().addAll(project.getRemoteArtifactRepositories());
+ // Compute merged repositories for this project and store
in session
+ // instead of mutating the shared request to avoid leakage
between projects
+ List<ArtifactRepository> mergedRepositories;
+ switch (request.getRepositoryMerging()) {
+ case POM_DOMINANT -> {
+ LinkedHashSet<ArtifactRepository> reposes =
+ new
LinkedHashSet<>(project.getRemoteArtifactRepositories());
+ reposes.addAll(request.getRemoteRepositories());
+ mergedRepositories = List.copyOf(reposes);
+ }
+ case REQUEST_DOMINANT -> {
+ LinkedHashSet<ArtifactRepository> reposes =
+ new
LinkedHashSet<>(request.getRemoteRepositories());
+
reposes.addAll(project.getRemoteArtifactRepositories());
+ mergedRepositories = List.copyOf(reposes);
+ }
+ default -> throw new IllegalArgumentException(
+ "Unsupported repository merging: " +
request.getRepositoryMerging());
+ }
+
+ // Store the computed repositories for this project in
BuildSession storage
+ // to avoid mutating the shared request and causing
leakage between projects
+ projectRepositories.put(project.getId(),
mergedRepositories);
+
Path parentPomFile = parentModel.getPomFile();
if (parentPomFile != null) {
project.setParentFile(parentPomFile.toFile());
@@ -870,7 +907,8 @@ private void initParent(MavenProject project,
ModelBuilderResult result) {
} else {
Artifact parentArtifact = project.getParentArtifact();
try {
- parent = build(true, parentArtifact,
false).getProject();
+ parent = build(true, parentArtifact, false,
getEffectiveRepositories(project.getId()))
+ .getProject();
} catch (ProjectBuildingException e) {
// MNG-4488 where let invalid parents slide on by
if (logger.isDebugEnabled()) {
diff --git
a/impl/maven-core/src/test/java/org/apache/maven/project/RepositoryLeakageTest.java
b/impl/maven-core/src/test/java/org/apache/maven/project/RepositoryLeakageTest.java
new file mode 100644
index 0000000000..b5ff18f872
--- /dev/null
+++
b/impl/maven-core/src/test/java/org/apache/maven/project/RepositoryLeakageTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.project;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.codehaus.plexus.testing.PlexusTest;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Test to verify that repositories from one project don't leak to sibling
projects.
+ */
+@PlexusTest
+public class RepositoryLeakageTest extends AbstractMavenProjectTestCase {
+
+ @Test
+ @SuppressWarnings("checkstyle:MethodLength")
+ public void testRepositoryLeakageBetweenSiblings() throws Exception {
+ // Create a temporary directory structure for our test
+ Path tempDir = Files.createTempDirectory("maven-repo-leakage-test");
+
+ try {
+ // Create parent POM
+ Path parentPom = tempDir.resolve("pom.xml");
+ Files.writeString(
+ parentPom,
+ """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>test</groupId>
+ <artifactId>parent</artifactId>
+ <version>1.0</version>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>child1</module>
+ <module>child2</module>
+ </modules>
+ </project>
+ """);
+
+ // Create child1 with specific repository
+ Path child1Dir = tempDir.resolve("child1");
+ Files.createDirectories(child1Dir);
+ Path child1Pom = child1Dir.resolve("pom.xml");
+ Files.writeString(
+ child1Pom,
+ """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>test</groupId>
+ <artifactId>parent</artifactId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>child1</artifactId>
+
+ <repositories>
+ <repository>
+ <id>child1-repo</id>
+ <url>https://child1.example.com/repo</url>
+ </repository>
+ </repositories>
+ </project>
+ """);
+
+ // Create child2 with different repository
+ Path child2Dir = tempDir.resolve("child2");
+ Files.createDirectories(child2Dir);
+ Path child2Pom = child2Dir.resolve("pom.xml");
+ Files.writeString(
+ child2Pom,
+ """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>test</groupId>
+ <artifactId>parent</artifactId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>child2</artifactId>
+
+ <repositories>
+ <repository>
+ <id>child2-repo</id>
+ <url>https://child2.example.com/repo</url>
+ </repository>
+ </repositories>
+ </project>
+ """);
+
+ // Create a shared ProjectBuildingRequest
+ ProjectBuildingRequest sharedRequest = newBuildingRequest();
+
+ // Build child1 first
+ ProjectBuildingResult result1 =
projectBuilder.build(child1Pom.toFile(), sharedRequest);
+ MavenProject child1Project = result1.getProject();
+
+ // Capture repositories after building child1
+
+ // Build child2 using the same shared request
+ ProjectBuildingResult result2 =
projectBuilder.build(child2Pom.toFile(), sharedRequest);
+ MavenProject child2Project = result2.getProject();
+
+ // Capture repositories after building child2
+ List<ArtifactRepository> repositoriesAfterChild2 =
List.copyOf(sharedRequest.getRemoteRepositories());
+
+ // Verify that child1 has its own repository
+ boolean child1HasOwnRepo =
child1Project.getRemoteArtifactRepositories().stream()
+ .anyMatch(repo -> "child1-repo".equals(repo.getId()));
+ assertTrue(child1HasOwnRepo, "Child1 should have its own
repository");
+
+ // Verify that child2 has its own repository
+ boolean child2HasOwnRepo =
child2Project.getRemoteArtifactRepositories().stream()
+ .anyMatch(repo -> "child2-repo".equals(repo.getId()));
+ assertTrue(child2HasOwnRepo, "Child2 should have its own
repository");
+
+ // Print debug information
+ System.out.println("=== REPOSITORY LEAKAGE TEST RESULTS ===");
+ System.out.println(
+ "Repositories in shared request after building child2: " +
repositoriesAfterChild2.size());
+ repositoriesAfterChild2.forEach(
+ repo -> System.out.println(" - " + repo.getId() + " (" +
repo.getUrl() + ")"));
+
+ System.out.println("Child1 project repositories:");
+ child1Project
+ .getRemoteArtifactRepositories()
+ .forEach(repo -> System.out.println(" - " + repo.getId()
+ " (" + repo.getUrl() + ")"));
+
+ System.out.println("Child2 project repositories:");
+ child2Project
+ .getRemoteArtifactRepositories()
+ .forEach(repo -> System.out.println(" - " + repo.getId()
+ " (" + repo.getUrl() + ")"));
+ System.out.println("=======================================");
+
+ // Check for leakage: child2 should NOT have child1's repository
+ boolean child2HasChild1Repo =
child2Project.getRemoteArtifactRepositories().stream()
+ .anyMatch(repo -> "child1-repo".equals(repo.getId()));
+ assertFalse(child2HasChild1Repo, "Child2 should NOT have child1's
repository (leakage detected!)");
+
+ // Check for leakage in the shared request
+ boolean sharedRequestHasChild1Repo =
+ repositoriesAfterChild2.stream().anyMatch(repo ->
"child1-repo".equals(repo.getId()));
+ boolean sharedRequestHasChild2Repo =
+ repositoriesAfterChild2.stream().anyMatch(repo ->
"child2-repo".equals(repo.getId()));
+
+ // Print debug information
+ /*
+ System.out.println("Repositories after child1: " +
repositoriesAfterChild1.size());
+ repositoriesAfterChild1.forEach(repo -> System.out.println(" - "
+ repo.getId() + ": " + repo.getUrl()));
+
+ System.out.println("Repositories after child2: " +
repositoriesAfterChild2.size());
+ repositoriesAfterChild2.forEach(repo -> System.out.println(" - "
+ repo.getId() + ": " + repo.getUrl()));
+ */
+
+ // The shared request should not accumulate repositories from both
children
+ if (sharedRequestHasChild1Repo && sharedRequestHasChild2Repo) {
+ fail("REPOSITORY LEAKAGE DETECTED: Shared request contains
repositories from both children!");
+ }
+
+ } finally {
+ // Clean up
+ deleteRecursively(tempDir.toFile());
+ }
+ }
+
+ private void deleteRecursively(File file) {
+ if (file.isDirectory()) {
+ File[] children = file.listFiles();
+ if (children != null) {
+ for (File child : children) {
+ deleteRecursively(child);
+ }
+ }
+ }
+ file.delete();
+ }
+}
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java
index 642e8610d1..7992df53b0 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java
@@ -279,6 +279,12 @@ public
List<org.eclipse.aether.repository.RemoteRepository> toRepositories(List<
return repositories == null ? null : map(repositories,
this::toRepository);
}
+ @Override
+ public List<org.eclipse.aether.repository.RemoteRepository>
toResolvingRepositories(
+ List<RemoteRepository> repositories) {
+ return getRepositorySystem().newResolutionRepositories(getSession(),
toRepositories(repositories));
+ }
+
@Override
public org.eclipse.aether.repository.RemoteRepository
toRepository(RemoteRepository repository) {
if (repository instanceof DefaultRemoteRepository
defaultRemoteRepository) {
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactResolver.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactResolver.java
index 3614056b28..9f22790f39 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactResolver.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactResolver.java
@@ -91,7 +91,7 @@ protected ArtifactResolverResult
doResolve(ArtifactResolverRequest request) {
InternalSession session = InternalSession.from(request.getSession());
RequestTraceHelper.ResolverTrace trace =
RequestTraceHelper.enter(session, request);
try {
- List<RemoteRepository> repositories = session.toRepositories(
+ List<RemoteRepository> repositories =
session.toResolvingRepositories(
request.getRepositories() != null ?
request.getRepositories() : session.getRemoteRepositories());
List<ResolverRequest> requests = new ArrayList<>();
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java
index 278d7feb7e..d29c3f369a 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java
@@ -152,7 +152,7 @@ public DependencyResolverResult collect(@Nonnull
DependencyResolverRequest reque
.setRoot(root != null ? session.toDependency(root, false)
: null)
.setDependencies(session.toDependencies(dependencies,
false))
.setManagedDependencies(session.toDependencies(managedDependencies, true))
-
.setRepositories(session.toRepositories(remoteRepositories))
+
.setRepositories(session.toResolvingRepositories(remoteRepositories))
.setRequestContext(trace.context())
.setTrace(trace.trace());
collectRequest.setResolutionScope(resolutionScope);
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionRangeResolver.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionRangeResolver.java
index df182d976a..b0097d5248 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionRangeResolver.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionRangeResolver.java
@@ -69,7 +69,7 @@ public VersionRangeResolverResult
doResolve(VersionRangeResolverRequest request)
session.getSession(),
new VersionRangeRequest(
session.toArtifact(request.getArtifactCoordinates()),
- session.toRepositories(
+ session.toResolvingRepositories(
request.getRepositories() != null
? request.getRepositories()
:
session.getRemoteRepositories()),
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionResolver.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionResolver.java
index c80a1d24ad..1f233f604b 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionResolver.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionResolver.java
@@ -61,7 +61,7 @@ protected VersionResolverResult
doResolve(VersionResolverRequest request) throws
try {
VersionRequest req = new VersionRequest(
session.toArtifact(request.getArtifactCoordinates()),
- session.toRepositories(
+ session.toResolvingRepositories(
request.getRepositories() != null
? request.getRepositories()
: session.getRemoteRepositories()),
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java
index b3ce36d47b..7d9945077f 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java
@@ -101,6 +101,8 @@ <REQ extends Request<?>, REP extends Result<REQ>> List<REP>
requests(
List<org.eclipse.aether.repository.RemoteRepository>
toRepositories(List<RemoteRepository> repositories);
+ List<org.eclipse.aether.repository.RemoteRepository>
toResolvingRepositories(List<RemoteRepository> repositories);
+
org.eclipse.aether.repository.RemoteRepository
toRepository(RemoteRepository repository);
org.eclipse.aether.repository.LocalRepository toRepository(LocalRepository
repository);