From 86847382ee69f6f6f65b77058c0bfbcdae98d7bd Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 13:40:50 +0100 Subject: [PATCH 01/11] Remote repository intent Add new API to reflect the "intent" with given remote repository. WIP --- .../aether/repository/RemoteRepository.java | 114 +++++++++++++++--- .../impl/DefaultRemoteRepositoryManager.java | 7 +- .../impl/DefaultRepositorySystem.java | 1 + 3 files changed, 101 insertions(+), 21 deletions(-) diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java index 99cec7d2e..3b80f8dd5 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java @@ -18,6 +18,8 @@ */ package org.eclipse.aether.repository; +import org.eclipse.aether.RepositorySystemSession; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -32,6 +34,28 @@ * A repository on a remote server. */ public final class RemoteRepository implements ArtifactRepository { + /** + * The intent this repository is to be used for. Newly created repositories are usually "bare", and before + * their actual use (caller or repository system) adapts them, equip with auth/proxy info and even mirrors, if + * environment is configured for them. Note: "bare" does not always mean "without authentication", as client + * code may create with all required properties, but {@link org.eclipse.aether.RepositorySystem} will process + * them anyway, marking their "intent". + *

+ * Important consequence: the change of {@link Intent} on repository may affect the use cases when + * they are used as keys (they are suitable for that). To use {@link RemoteRepository} instances as key, + * you should use instances returned by method {@link #toBareRemoteRepository()}, that returns "normalized" + * repository instances usable as keys. Also, in "key usage case" two instances of remote repository are + * considered equal if following stands: {@code Objects.equals(r1.toBareRemoteRepository(), r2.toBareRemoteRepository())}. + * + * @see org.eclipse.aether.RepositorySystem#newResolutionRepositories(RepositorySystemSession, List) + * @see org.eclipse.aether.RepositorySystem#newDeploymentRepository(RepositorySystemSession, RemoteRepository) + * @since 2.0.14 + */ + public enum Intent { + BARE, + RESOLUTION, + DEPLOYMENT + } private static final Pattern URL_PATTERN = Pattern.compile("([^:/]+(:[^:/]{2,}+(?=://))?):(//([^@/]*@)?([^/:]+))?.*"); @@ -60,6 +84,10 @@ public final class RemoteRepository implements ArtifactRepository { private final boolean blocked; + private final Intent intent; + + private final int hashCode; + RemoteRepository(Builder builder) { if (builder.prototype != null) { id = (builder.delta & Builder.ID) != 0 ? builder.id : builder.prototype.id; @@ -80,6 +108,7 @@ public final class RemoteRepository implements ArtifactRepository { mirroredRepositories = (builder.delta & Builder.MIRRORED) != 0 ? copy(builder.mirroredRepositories) : builder.prototype.mirroredRepositories; + intent = (builder.delta & Builder.INTENT) != 0 ? builder.intent : builder.prototype.intent; } else { id = builder.id; type = builder.type; @@ -91,17 +120,31 @@ public final class RemoteRepository implements ArtifactRepository { repositoryManager = builder.repositoryManager; blocked = builder.blocked; mirroredRepositories = copy(builder.mirroredRepositories); + intent = builder.intent; } Matcher m = URL_PATTERN.matcher(url); if (m.matches()) { - protocol = m.group(1); - String host = m.group(5); - this.host = (host != null) ? host : ""; + String h = m.group(5); + this.host = (h != null) ? h : ""; + this.protocol = m.group(1); } else { - protocol = ""; - host = ""; + this.host = ""; + this.protocol = ""; } + + this.hashCode = Objects.hash( + id, + type, + url, // host, protocol derived from url + releasePolicy, + snapshotPolicy, + proxy, + authentication, + mirroredRepositories, + repositoryManager, + blocked, + intent); } private static List copy(List repos) { @@ -111,10 +154,12 @@ private static List copy(List repos) { return Collections.unmodifiableList(Arrays.asList(repos.toArray(new RemoteRepository[0]))); } + @Override public String getId() { return id; } + @Override public String getContentType() { return type; } @@ -203,6 +248,16 @@ public boolean isBlocked() { return blocked; } + /** + * Returns the intent this repository is prepared for. + * + * @return The intent this repository is prepared for. + * @since 2.0.14 + */ + public Intent getIntent() { + return intent; + } + @Override public String toString() { StringBuilder buffer = new StringBuilder(256); @@ -225,6 +280,7 @@ public String toString() { if (isBlocked()) { buffer.append(", blocked"); } + buffer.append(", ").append(getIntent().name()).append(")"); buffer.append(")"); return buffer.toString(); } @@ -248,26 +304,26 @@ public boolean equals(Object obj) { && Objects.equals(proxy, that.proxy) && Objects.equals(authentication, that.authentication) && Objects.equals(mirroredRepositories, that.mirroredRepositories) - && repositoryManager == that.repositoryManager; + && repositoryManager == that.repositoryManager + && blocked == that.blocked + && intent == that.intent; } @Override public int hashCode() { - int hash = 17; - hash = hash * 31 + hash(url); - hash = hash * 31 + hash(type); - hash = hash * 31 + hash(id); - hash = hash * 31 + hash(releasePolicy); - hash = hash * 31 + hash(snapshotPolicy); - hash = hash * 31 + hash(proxy); - hash = hash * 31 + hash(authentication); - hash = hash * 31 + hash(mirroredRepositories); - hash = hash * 31 + (repositoryManager ? 1 : 0); - return hash; + return hashCode; } - private static int hash(Object obj) { - return obj != null ? obj.hashCode() : 0; + /** + * Makes "bare" repository out of this instance, usable as keys within one single session. Cross session use + * of these is not recommended, instead see other means. + */ + public RemoteRepository toBareRemoteRepository() { + return new Builder(this) + .setIntent(Intent.BARE) + .setProxy(null) + .setAuthentication(null) + .build(); } /** @@ -286,7 +342,8 @@ public static final class Builder { AUTH = 0x0040, MIRRORED = 0x0080, REPOMAN = 0x0100, - BLOCKED = 0x0200; + BLOCKED = 0x0200, + INTENT = 0x0400; int delta; @@ -312,6 +369,8 @@ public static final class Builder { boolean blocked; + Intent intent = Intent.BARE; + /** * Creates a new repository builder. * @@ -545,5 +604,20 @@ public Builder setBlocked(boolean blocked) { } return this; } + + /** + * Marks the intent for this repository. + * + * @param intent the intent with this remote repository. + * @return This builder for chaining, never {@code null}. + * @since 2.0.14 + */ + public Builder setIntent(Intent intent) { + this.intent = intent; + if (prototype != null) { + delta(INTENT, this.intent, prototype.intent); + } + return this; + } } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManager.java index 6995ded82..3ad72aade 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManager.java @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.List; import java.util.ListIterator; +import java.util.stream.Collectors; import org.eclipse.aether.RepositoryCache; import org.eclipse.aether.RepositorySystemSession; @@ -160,7 +161,11 @@ public List aggregateRepositories( result.add(repository); } - return result; + return result.stream() + .map(r -> new RemoteRepository.Builder(r) + .setIntent(RemoteRepository.Intent.RESOLUTION) + .build()) + .collect(Collectors.toList()); } private void logMirror(RepositorySystemSession session, RemoteRepository original, RemoteRepository mirror) { diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java index 90003a7c2..4ffbfb239 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java @@ -455,6 +455,7 @@ public RemoteRepository newDeploymentRepository(RepositorySystemSession session, builder.setAuthentication(auth); Proxy proxy = session.getProxySelector().getProxy(repository); builder.setProxy(proxy); + builder.setIntent(RemoteRepository.Intent.DEPLOYMENT); return builder.build(); } From 617873f011db2841d05e9072954053faf1c3788f Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 13:53:20 +0100 Subject: [PATCH 02/11] Javadoc --- .../aether/repository/RemoteRepository.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java index 3b80f8dd5..e8895f437 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java @@ -18,8 +18,6 @@ */ package org.eclipse.aether.repository; -import org.eclipse.aether.RepositorySystemSession; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -32,6 +30,8 @@ /** * A repository on a remote server. + *

+ * If use of instances of this class are meant to be used as keys, see {@link #toBareRemoteRepository()} method. */ public final class RemoteRepository implements ArtifactRepository { /** @@ -315,14 +315,25 @@ public int hashCode() { } /** - * Makes "bare" repository out of this instance, usable as keys within one single session. Cross session use - * of these is not recommended, instead see other means. + * Makes "bare" repository out of this instance, usable as keys within one single session, by applying following + * changes to repository (returns new instance): + *

+ * These properties are managed by repository system, based on configuration. See {@link org.eclipse.aether.RepositorySystem} + * and (internal component) {@code org.eclipse.aether.impl.RemoteRepositoryManager}. */ public RemoteRepository toBareRemoteRepository() { return new Builder(this) .setIntent(Intent.BARE) .setProxy(null) .setAuthentication(null) + .setMirroredRepositories(null) + .setRepositoryManager(false) .build(); } From 26566ad11afcaf8674e4bd69309f865c9dc79a31 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 15:42:47 +0100 Subject: [PATCH 03/11] Add since --- .../java/org/eclipse/aether/repository/RemoteRepository.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java index e8895f437..8b359cea4 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java @@ -326,6 +326,8 @@ public int hashCode() { * * These properties are managed by repository system, based on configuration. See {@link org.eclipse.aether.RepositorySystem} * and (internal component) {@code org.eclipse.aether.impl.RemoteRepositoryManager}. + * + * @since 2.0.14 */ public RemoteRepository toBareRemoteRepository() { return new Builder(this) From 1918ea4127dfc61a53d3d166bb52d4085117be09 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 16:43:59 +0100 Subject: [PATCH 04/11] Use new method --- .../RemoteRepositoryFilterSourceSupport.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java index bb4c1e3ff..e2a4f0b2c 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java @@ -83,22 +83,12 @@ protected Path getBasedir( * used {@link org.eclipse.aether.RepositorySystem#newResolutionRepositories(RepositorySystemSession, List)} beforehand. * The hash/equalTo method factors in all these as well, but from our perspective, they do not matter. So we make all * key remote repositories back to "bare". - * Ignored properties of normalized repositories: - *
    - *
  • proxy - is environment dependent
  • - *
  • authentication - is environment and/or user dependent
  • - *
  • mirrored repositories - is environment dependent (within same session does not change)
  • - *
  • repository manager - is environment dependent (within same session does not change)
  • - *
+ * + * @see RemoteRepository#toBareRemoteRepository() */ protected RemoteRepository normalizeRemoteRepository( RepositorySystemSession session, RemoteRepository remoteRepository) { - return new RemoteRepository.Builder(remoteRepository) - .setProxy(null) - .setAuthentication(null) - .setMirroredRepositories(null) - .setRepositoryManager(false) - .build(); + return remoteRepository.toBareRemoteRepository(); } /** From 8566a1b4ab9f573fbf419c7bb62ee777b842f699 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 16:50:38 +0100 Subject: [PATCH 05/11] Tidy up --- .../internal/impl/DefaultRepositorySystem.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java index 4ffbfb239..542fa5fa4 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java @@ -441,8 +441,8 @@ public List newResolutionRepositories( validateSession(session); validateRepositories(repositories); repositorySystemValidator.validateRemoteRepositories(session, repositories); - repositories = remoteRepositoryManager.aggregateRepositories(session, new ArrayList<>(), repositories, true); - return repositories; + + return remoteRepositoryManager.aggregateRepositories(session, new ArrayList<>(), repositories, true); } @Override @@ -450,13 +450,14 @@ public RemoteRepository newDeploymentRepository(RepositorySystemSession session, validateSession(session); requireNonNull(repository, "repository cannot be null"); repositorySystemValidator.validateRemoteRepositories(session, Collections.singletonList(repository)); - RemoteRepository.Builder builder = new RemoteRepository.Builder(repository); + Authentication auth = session.getAuthenticationSelector().getAuthentication(repository); - builder.setAuthentication(auth); Proxy proxy = session.getProxySelector().getProxy(repository); - builder.setProxy(proxy); - builder.setIntent(RemoteRepository.Intent.DEPLOYMENT); - return builder.build(); + return new RemoteRepository.Builder(repository) + .setAuthentication(auth) + .setProxy(proxy) + .setIntent(RemoteRepository.Intent.DEPLOYMENT) + .build(); } @Override From 343aabfec3ac6d5f16c22ec1855b550d62b99fdc Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 16:52:47 +0100 Subject: [PATCH 06/11] Cut the text --- .../impl/filter/RemoteRepositoryFilterSourceSupport.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java index e2a4f0b2c..c5f8fb21c 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java @@ -79,10 +79,7 @@ protected Path getBasedir( } /** - * We use remote repositories as keys, but they may fly in as "bare" or as "equipped" (w/ auth and proxy) if caller - * used {@link org.eclipse.aether.RepositorySystem#newResolutionRepositories(RepositorySystemSession, List)} beforehand. - * The hash/equalTo method factors in all these as well, but from our perspective, they do not matter. So we make all - * key remote repositories back to "bare". + * We use remote repositories as keys, so normalize them. * * @see RemoteRepository#toBareRemoteRepository() */ From b52a9f7b2c6f0463c8b9a53b0922bfaa7cab7bea Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 16:55:49 +0100 Subject: [PATCH 07/11] Reformat --- .../impl/filter/RemoteRepositoryFilterSourceSupport.java | 1 - 1 file changed, 1 deletion(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java index c5f8fb21c..3f0b54a22 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Path; -import java.util.List; import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; From 9419c2a467558955139f8d3d7f6f254b173e4667 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 17:01:44 +0100 Subject: [PATCH 08/11] Collapse --- .../filter/PrefixesRemoteRepositoryFilterSource.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java index 854560a59..e680570e3 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java @@ -339,16 +339,15 @@ private Path resolvePrefixesFromRemoteRepository( session, Collections.emptyList(), Collections.singletonList(remoteRepository), true) .get(0); // retrieve prefix as metadata from repository - MetadataRequest request = - new MetadataRequest(new DefaultMetadata(PREFIX_FILE_TYPE, Metadata.Nature.RELEASE_OR_SNAPSHOT)); - request.setRepository(prepared); - request.setDeleteLocalCopyIfMissing(true); - request.setFavorLocalRepository(true); MetadataResult result = mr.resolveMetadata( new DefaultRepositorySystemSession(session) .setTransferListener(null) .setConfigProperty(CONFIG_PROP_SKIPPED, Boolean.TRUE.toString()), - Collections.singleton(request)) + Collections.singleton(new MetadataRequest( + new DefaultMetadata(PREFIX_FILE_TYPE, Metadata.Nature.RELEASE_OR_SNAPSHOT)) + .setRepository(prepared) + .setDeleteLocalCopyIfMissing(true) + .setFavorLocalRepository(true))) .get(0); if (result.isResolved()) { return result.getMetadata().getPath(); From f7580ffcb9eb4288a76f7687e445a83dc29ed45f Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 17:02:38 +0100 Subject: [PATCH 09/11] Add since --- .../impl/filter/RemoteRepositoryFilterSourceSupport.java | 1 + 1 file changed, 1 insertion(+) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java index 3f0b54a22..438c5eaa8 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java @@ -80,6 +80,7 @@ protected Path getBasedir( /** * We use remote repositories as keys, so normalize them. * + * @since 2.0.14 * @see RemoteRepository#toBareRemoteRepository() */ protected RemoteRepository normalizeRemoteRepository( From d52eceefcdfaae1b990e71c5ec20f23fec3bb637 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 17:13:15 +0100 Subject: [PATCH 10/11] Move everything into session --- .../GroupIdRemoteRepositoryFilterSource.java | 58 ++++++++++++------- .../PrefixesRemoteRepositoryFilterSource.java | 28 +++++---- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java index 844616bcf..3d6643876 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java @@ -33,6 +33,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -154,23 +155,34 @@ public final class GroupIdRemoteRepositoryFilterSource extends RemoteRepositoryF private final PathProcessor pathProcessor; - private final ConcurrentHashMap rules; - - private final ConcurrentHashMap ruleFiles; - - private final ConcurrentHashMap> recordedRules; - - private final AtomicBoolean onShutdownHandlerRegistered; - @Inject public GroupIdRemoteRepositoryFilterSource( RepositorySystemLifecycle repositorySystemLifecycle, PathProcessor pathProcessor) { this.repositorySystemLifecycle = requireNonNull(repositorySystemLifecycle); this.pathProcessor = requireNonNull(pathProcessor); - this.rules = new ConcurrentHashMap<>(); - this.ruleFiles = new ConcurrentHashMap<>(); - this.recordedRules = new ConcurrentHashMap<>(); - this.onShutdownHandlerRegistered = new AtomicBoolean(false); + } + + @SuppressWarnings("unchecked") + private ConcurrentMap rules(RepositorySystemSession session) { + return (ConcurrentMap) + session.getData().computeIfAbsent(getClass().getName() + ".rules", ConcurrentHashMap::new); + } + + @SuppressWarnings("unchecked") + private ConcurrentMap ruleFiles(RepositorySystemSession session) { + return (ConcurrentMap) + session.getData().computeIfAbsent(getClass().getName() + ".ruleFiles", ConcurrentHashMap::new); + } + + @SuppressWarnings("unchecked") + private ConcurrentMap> recordedRules(RepositorySystemSession session) { + return (ConcurrentMap>) + session.getData().computeIfAbsent(getClass().getName() + ".recordedRules", ConcurrentHashMap::new); + } + + private AtomicBoolean onShutdownHandlerRegistered(RepositorySystemSession session) { + return (AtomicBoolean) session.getData() + .computeIfAbsent(getClass().getName() + ".onShutdownHandlerRegistered", AtomicBoolean::new); } @Override @@ -206,8 +218,8 @@ public RemoteRepositoryFilter getRemoteRepositoryFilter(RepositorySystemSession @Override public void postProcess(RepositorySystemSession session, List artifactResults) { if (isEnabled(session) && isRecord(session)) { - if (onShutdownHandlerRegistered.compareAndSet(false, true)) { - repositorySystemLifecycle.addOnSystemEndedHandler(this::saveRecordedLines); + if (onShutdownHandlerRegistered(session).compareAndSet(false, true)) { + repositorySystemLifecycle.addOnSystemEndedHandler(() -> saveRecordedLines(session)); } for (ArtifactResult artifactResult : artifactResults) { if (artifactResult.isResolved() && artifactResult.getRepository() instanceof RemoteRepository) { @@ -216,10 +228,11 @@ public void postProcess(RepositorySystemSession session, List ar ruleFile(session, remoteRepository); // populate it; needed for save String line = "=" + artifactResult.getArtifact().getGroupId(); RemoteRepository normalized = normalizeRemoteRepository(session, remoteRepository); - recordedRules + recordedRules(session) .computeIfAbsent(normalized, k -> new TreeSet<>()) .add(line); - rules.compute(normalized, (k, v) -> { + rules(session) + .compute(normalized, (k, v) -> { if (v == null || v == GroupTree.SENTINEL) { v = new GroupTree(""); } @@ -233,7 +246,7 @@ public void postProcess(RepositorySystemSession session, List ar } private Path ruleFile(RepositorySystemSession session, RemoteRepository remoteRepository) { - return ruleFiles.computeIfAbsent(normalizeRemoteRepository(session, remoteRepository), r -> getBasedir( + return ruleFiles(session).computeIfAbsent(normalizeRemoteRepository(session, remoteRepository), r -> getBasedir( session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, false) .resolve(GROUP_ID_FILE_PREFIX + RepositoryIdHelper.cachedIdToPathSegment(session).apply(remoteRepository) @@ -241,8 +254,9 @@ private Path ruleFile(RepositorySystemSession session, RemoteRepository remoteRe } private GroupTree cacheRules(RepositorySystemSession session, RemoteRepository remoteRepository) { - return rules.computeIfAbsent( - normalizeRemoteRepository(session, remoteRepository), r -> loadRepositoryRules(session, r)); + return rules(session) + .computeIfAbsent( + normalizeRemoteRepository(session, remoteRepository), r -> loadRepositoryRules(session, r)); } private GroupTree loadRepositoryRules(RepositorySystemSession session, RemoteRepository remoteRepository) { @@ -315,10 +329,10 @@ private boolean isRecord(RepositorySystemSession session) { /** * On-close handler that saves recorded rules, if any. */ - private void saveRecordedLines() { + private void saveRecordedLines(RepositorySystemSession session) { ArrayList exceptions = new ArrayList<>(); - for (Map.Entry entry : ruleFiles.entrySet()) { - Set recorded = recordedRules.get(entry.getKey()); + for (Map.Entry entry : ruleFiles(session).entrySet()) { + Set recorded = recordedRules(session).get(entry.getKey()); if (recorded != null && !recorded.isEmpty()) { try { ArrayList result = new ArrayList<>(); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java index e680570e3..f29b77fee 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java @@ -26,6 +26,7 @@ import java.nio.file.Path; import java.util.Collections; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.function.Supplier; import org.eclipse.aether.DefaultRepositorySystemSession; @@ -191,10 +192,6 @@ public final class PrefixesRemoteRepositoryFilterSource extends RemoteRepository private final RepositoryLayoutProvider repositoryLayoutProvider; - private final ConcurrentHashMap prefixes; - - private final ConcurrentHashMap layouts; - @Inject public PrefixesRemoteRepositoryFilterSource( Supplier metadataResolver, @@ -203,8 +200,18 @@ public PrefixesRemoteRepositoryFilterSource( this.metadataResolver = requireNonNull(metadataResolver); this.remoteRepositoryManager = requireNonNull(remoteRepositoryManager); this.repositoryLayoutProvider = requireNonNull(repositoryLayoutProvider); - this.prefixes = new ConcurrentHashMap<>(); - this.layouts = new ConcurrentHashMap<>(); + } + + @SuppressWarnings("unchecked") + private ConcurrentMap prefixes(RepositorySystemSession session) { + return (ConcurrentMap) + session.getData().computeIfAbsent(getClass().getName() + ".prefixes", ConcurrentHashMap::new); + } + + @SuppressWarnings("unchecked") + private ConcurrentMap layouts(RepositorySystemSession session) { + return (ConcurrentMap) + session.getData().computeIfAbsent(getClass().getName() + ".layouts", ConcurrentHashMap::new); } @Override @@ -243,7 +250,7 @@ public RemoteRepositoryFilter getRemoteRepositoryFilter(RepositorySystemSession * @return the layout instance of {@code null} if layout not supported. */ private RepositoryLayout cacheLayout(RepositorySystemSession session, RemoteRepository remoteRepository) { - return layouts.computeIfAbsent(normalizeRemoteRepository(session, remoteRepository), r -> { + return layouts(session).computeIfAbsent(normalizeRemoteRepository(session, remoteRepository), r -> { try { return repositoryLayoutProvider.newRepositoryLayout(session, remoteRepository); } catch (NoRepositoryLayoutException e) { @@ -254,9 +261,10 @@ private RepositoryLayout cacheLayout(RepositorySystemSession session, RemoteRepo private PrefixTree cachePrefixTree( RepositorySystemSession session, Path basedir, RemoteRepository remoteRepository) { - return prefixes.computeIfAbsent( - normalizeRemoteRepository(session, remoteRepository), - r -> loadPrefixTree(session, basedir, remoteRepository)); + return prefixes(session) + .computeIfAbsent( + normalizeRemoteRepository(session, remoteRepository), + r -> loadPrefixTree(session, basedir, remoteRepository)); } private PrefixTree loadPrefixTree( From 760488d2483c088bac4aad0749217f2c329a8190 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 17:21:06 +0100 Subject: [PATCH 11/11] Do not use null, sentinel is must due concurrent map --- .../PrefixesRemoteRepositoryFilterSource.java | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java index f29b77fee..9a4b2c301 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java @@ -22,9 +22,11 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Supplier; @@ -41,6 +43,7 @@ import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.MetadataRequest; import org.eclipse.aether.resolution.MetadataResult; +import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter; import org.eclipse.aether.spi.connector.layout.RepositoryLayout; import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider; @@ -245,16 +248,16 @@ public RemoteRepositoryFilter getRemoteRepositoryFilter(RepositorySystemSession } /** - * Caches layout instances for remote repository. In case of unknown layout it returns {@code null}. + * Caches layout instances for remote repository. In case of unknown layout it returns {@link #NOT_SUPPORTED}. * - * @return the layout instance of {@code null} if layout not supported. + * @return the layout instance or {@link #NOT_SUPPORTED} if layout not supported. */ private RepositoryLayout cacheLayout(RepositorySystemSession session, RemoteRepository remoteRepository) { return layouts(session).computeIfAbsent(normalizeRemoteRepository(session, remoteRepository), r -> { try { return repositoryLayoutProvider.newRepositoryLayout(session, remoteRepository); } catch (NoRepositoryLayoutException e) { - return null; + return NOT_SUPPORTED; } }); } @@ -378,7 +381,7 @@ private PrefixesFilter(RepositorySystemSession session, Path basedir) { @Override public Result acceptArtifact(RemoteRepository remoteRepository, Artifact artifact) { RepositoryLayout repositoryLayout = cacheLayout(session, remoteRepository); - if (repositoryLayout == null) { + if (repositoryLayout == NOT_SUPPORTED) { return new SimpleResult(true, "Unsupported layout: " + remoteRepository); } return acceptPrefix( @@ -389,7 +392,7 @@ public Result acceptArtifact(RemoteRepository remoteRepository, Artifact artifac @Override public Result acceptMetadata(RemoteRepository remoteRepository, Metadata metadata) { RepositoryLayout repositoryLayout = cacheLayout(session, remoteRepository); - if (repositoryLayout == null) { + if (repositoryLayout == NOT_SUPPORTED) { return new SimpleResult(true, "Unsupported layout: " + remoteRepository); } return acceptPrefix( @@ -399,7 +402,7 @@ public Result acceptMetadata(RemoteRepository remoteRepository, Metadata metadat private Result acceptPrefix(RemoteRepository repository, String path) { PrefixTree prefixTree = cachePrefixTree(session, basedir, repository); - if (PrefixTree.SENTINEL == prefixTree) { + if (prefixTree == PrefixTree.SENTINEL) { return NOT_PRESENT_RESULT; } if (prefixTree.acceptedPath(path)) { @@ -412,4 +415,36 @@ private Result acceptPrefix(RemoteRepository repository, String path) { private static final RemoteRepositoryFilter.Result NOT_PRESENT_RESULT = new SimpleResult(true, "Prefix file not present"); + + private static final RepositoryLayout NOT_SUPPORTED = new RepositoryLayout() { + @Override + public List getChecksumAlgorithmFactories() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasChecksums(Artifact artifact) { + throw new UnsupportedOperationException(); + } + + @Override + public URI getLocation(Artifact artifact, boolean upload) { + throw new UnsupportedOperationException(); + } + + @Override + public URI getLocation(Metadata metadata, boolean upload) { + throw new UnsupportedOperationException(); + } + + @Override + public List getChecksumLocations(Artifact artifact, boolean upload, URI location) { + throw new UnsupportedOperationException(); + } + + @Override + public List getChecksumLocations(Metadata metadata, boolean upload, URI location) { + throw new UnsupportedOperationException(); + } + }; }