From 978568530bee80aefb03ea75582146e69f5f2379 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Wed, 19 Nov 2025 14:57:48 +0100 Subject: [PATCH 01/28] GURK New opt-in feature for enhanced local repository: produce globally unique repository keys. WIP --- .../aether/repository/LocalRepository.java | 3 +- .../repository/WorkspaceRepository.java | 3 +- ...DefaultLocalPathPrefixComposerFactory.java | 5 ++- ...EnhancedLocalRepositoryManagerFactory.java | 45 ++++++++++++++++++- ...LocalPathPrefixComposerFactorySupport.java | 7 +++ .../impl/SimpleLocalRepositoryManager.java | 2 +- .../util/repository/RepositoryIdHelper.java | 38 +++++++++++++++- src/site/markdown/configuration.md | 1 + 8 files changed, 97 insertions(+), 7 deletions(-) diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java index 44ae964b0d..5e7b24c713 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java @@ -31,6 +31,7 @@ * the repository. */ public final class LocalRepository implements ArtifactRepository { + public static final String ID = "local"; private final Path basePath; @@ -108,7 +109,7 @@ public String getContentType() { @Override public String getId() { - return "local"; + return ID; } /** diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceRepository.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceRepository.java index e128af2094..a922c0b079 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceRepository.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceRepository.java @@ -27,6 +27,7 @@ * the contained artifacts is handled by a {@link WorkspaceReader}. */ public final class WorkspaceRepository implements ArtifactRepository { + public static final String ID = "workspace"; private final String type; @@ -65,7 +66,7 @@ public String getContentType() { } public String getId() { - return "workspace"; + return ID; } /** diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java index 2a18ce049b..509776c102 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java @@ -25,7 +25,8 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.ArtifactRepository; -import org.eclipse.aether.util.repository.RepositoryIdHelper; + +import static org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory.repositoryKeyFunction; /** * Default local path prefix composer factory: it fully reuses {@link LocalPathPrefixComposerFactorySupport} class @@ -48,7 +49,7 @@ public LocalPathPrefixComposer createComposer(RepositorySystemSession session) { isSplitRemoteRepositoryLast(session), getReleasesPrefix(session), getSnapshotsPrefix(session), - RepositoryIdHelper.cachedIdToPathSegment(session)); + repositoryKeyFunction(session)); } /** diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index b00f1dff94..ebc24849a2 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -22,11 +22,15 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.util.function.Function; + import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.NoLocalRepositoryManagerException; +import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; import org.eclipse.aether.util.ConfigUtils; import org.eclipse.aether.util.repository.RepositoryIdHelper; @@ -58,6 +62,25 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan public static final String DEFAULT_TRACKING_FILENAME = "_remote.repositories"; + /** + * Make enhanced repository use "globally unique repository keys" (repository keys are used for designating + * cached metadata, artifact availability tracking and split repository prefix production). By default, this + * option is disabled. If enabled, repository keys produced by enhanced repository will be way different + * that those produced with previous versions or without this option enabled. Ideally, you may want to + * use empty local repository to populate with new repository key contained metadata, Interoperability between + * enabled and disabled affects only metadata and split repository (ie. split repository may not find existing + * caches, and may opt to re-download them). + * + * @since 2.0.14 + * @configurationSource {@link RepositorySystemSession#getConfigProperties()} + * @configurationType {@link java.lang.Boolean} + * @configurationDefaultValue {@link #DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS} + */ + public static final String CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS = + CONFIG_PROPS_PREFIX + "globallyUniqueRepositoryKeys"; + + public static final boolean DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS = false; + private float priority = 10.0f; private final LocalPathComposer localPathComposer; @@ -66,6 +89,26 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan private final LocalPathPrefixComposerFactory localPathPrefixComposerFactory; + static Function repositoryKeyFunction(RepositorySystemSession session) { + Function idToPathSegmentFunction = + RepositoryIdHelper.cachedIdToPathSegment(session); + Function repositoryKeyFunction = idToPathSegmentFunction; + boolean globallyUniqueRepositoryKeys = ConfigUtils.getBoolean( + session, DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS); + Function globallyUniqueRepositoryKeyFunction = + RepositoryIdHelper.cachedRemoteRepositoryUniqueId(session); + if (globallyUniqueRepositoryKeys) { + repositoryKeyFunction = r -> { + if (r instanceof RemoteRepository) { + return globallyUniqueRepositoryKeyFunction.apply((RemoteRepository) r); + } else { + return idToPathSegmentFunction.apply(r); + } + }; + } + return repositoryKeyFunction; + } + @Inject public EnhancedLocalRepositoryManagerFactory( final LocalPathComposer localPathComposer, @@ -94,7 +137,7 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local return new EnhancedLocalRepositoryManager( repository.getBasePath(), localPathComposer, - RepositoryIdHelper.cachedIdToPathSegment(session), + repositoryKeyFunction(session), trackingFilename, trackingFileManager, localPathPrefixComposerFactory.createComposer(session)); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java index b86223b53a..8a70cebd15 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java @@ -220,6 +220,13 @@ protected String getSnapshotsPrefix(RepositorySystemSession session) { session, DEFAULT_SNAPSHOTS_PREFIX, CONFIG_PROP_SNAPSHOTS_PREFIX, R1_CONF_PROP_SNAPSHOTS_PREFIX); } + protected boolean isGloballyUniqueRepositoryKeys(RepositorySystemSession session) { + return ConfigUtils.getBoolean( + session, + EnhancedLocalRepositoryManagerFactory.DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, + EnhancedLocalRepositoryManagerFactory.CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS); + } + /** * Support class for composers: it defines protected members for all the predefined configuration values and * provides default implementation for methods. Implementors may change it's behaviour by overriding methods. diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java index ea751d41a9..eee54341ef 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java @@ -85,7 +85,7 @@ public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository repos @Override public String getPathForLocalMetadata(Metadata metadata) { requireNonNull(metadata, "metadata cannot be null"); - return localPathComposer.getPathForMetadata(metadata, "local"); + return localPathComposer.getPathForMetadata(metadata, LocalRepository.ID); } @Override diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index edcf42e8e0..ed4c2b482e 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -67,6 +67,39 @@ private RepositoryIdHelper() {} && !remoteRepository.isRepositoryManager() && !remoteRepository.isBlocked(); + /** + * Returns same instance of (session cached) function for session. + * + * @since 2.0.14 + */ + @SuppressWarnings("unchecked") + public static Function cachedRemoteRepositoryUniqueId(RepositorySystemSession session) { + requireNonNull(session, "session"); + return (Function) session.getData() + .computeIfAbsent( + RepositoryIdHelper.class.getSimpleName() + "-remoteRepositoryUniqueIdFunction", + () -> cachedRemoteRepositoryUniqueIdFunction(session)); + } + + /** + * Returns new instance of function backed by cached or uncached (if session has no cache set) + * {@link #remoteRepositoryUniqueId(RemoteRepository)} method call. + */ + @SuppressWarnings("unchecked") + private static Function cachedRemoteRepositoryUniqueIdFunction( + RepositorySystemSession session) { + if (session.getCache() != null) { + return repository -> ((ConcurrentHashMap) session.getCache() + .computeIfAbsent( + session, + RepositoryIdHelper.class.getSimpleName() + "-remoteRepositoryUniqueIdCache", + ConcurrentHashMap::new)) + .computeIfAbsent(repository, id -> remoteRepositoryUniqueId(repository)); + } else { + return RepositoryIdHelper::remoteRepositoryUniqueId; // uncached + } + } + /** * Creates unique repository id for given {@link RemoteRepository}. For Maven Central this method will return * string "central", while for any other remote repository it will return string created as @@ -96,7 +129,10 @@ public static String remoteRepositoryUniqueId(RemoteRepository repository) { buffer.append(", disabled"); } if (repository.isRepositoryManager()) { - buffer.append(", managed("); + buffer.append(", managed"); + } + if (!repository.getMirroredRepositories().isEmpty()) { + buffer.append(", mirrorOf("); for (RemoteRepository mirroredRepo : repository.getMirroredRepositories()) { buffer.append(remoteRepositoryUniqueId(mirroredRepo)); } diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index e30b9316e0..b510c1fdee 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -71,6 +71,7 @@ To modify this file, edit the template and regenerate. | `"aether.generator.sigstore.publicStaging"` | `Boolean` | Whether Sigstore should use public staging sigstage.dev instead of public default sigstore.dev . | `false` | 2.0.2 | No | Session Configuration | | `"aether.interactive"` | `Boolean` | A flag indicating whether interaction with the user is allowed. | `false` | | No | Session Configuration | | `"aether.layout.maven2.checksumAlgorithms"` | `String` | Comma-separated list of checksum algorithms with which checksums are validated (downloaded) and generated (uploaded) with this layout. Resolver by default supports following algorithms: MD5, SHA-1, SHA-256 and SHA-512. New algorithms can be added by implementing ChecksumAlgorithmFactory component. | `"SHA-1,MD5"` | 1.8.0 | Yes | Session Configuration | +| `"aether.lrm.enhanced.globallyUniqueRepositoryKeys"` | `Boolean` | Make enhanced repository use "globally unique repository keys" (repository keys are used for designating cached metadata, artifact availability tracking and split repository prefix production). By default, this option is disabled. If enabled, repository keys produced by enhanced repository will be way different that those produced with previous versions or without this option enabled. Ideally, you may want to use empty local repository to populate with new repository key contained metadata, Interoperability between enabled and disabled affects only metadata and split repository (ie. split repository may not find existing caches, and may opt to re-download them). | `false` | 2.0.14 | No | Session Configuration | | `"aether.lrm.enhanced.localPrefix"` | `String` | The prefix to use for locally installed artifacts. | `"installed"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.releasesPrefix"` | `String` | The prefix to use for release artifacts. | `"releases"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.remotePrefix"` | `String` | The prefix to use for remotely cached artifacts. | `"cached"` | 1.8.1 | No | Session Configuration | From 6307d5416f06c7717bb64bbb7aeabb6ec5bcb21c Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Wed, 19 Nov 2025 15:01:34 +0100 Subject: [PATCH 02/28] Tidy up + javadoc --- .../impl/EnhancedLocalRepositoryManagerFactory.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index ebc24849a2..fb57821959 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -89,15 +89,21 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan private final LocalPathPrefixComposerFactory localPathPrefixComposerFactory; + /** + * Method that based on configuration returns the "repository key function". Used by {@link EnhancedLocalRepositoryManagerFactory} + * and {@link LocalPathPrefixComposerFactory}. + * + * @since 2.0.14 + */ static Function repositoryKeyFunction(RepositorySystemSession session) { Function idToPathSegmentFunction = RepositoryIdHelper.cachedIdToPathSegment(session); Function repositoryKeyFunction = idToPathSegmentFunction; boolean globallyUniqueRepositoryKeys = ConfigUtils.getBoolean( session, DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS); - Function globallyUniqueRepositoryKeyFunction = - RepositoryIdHelper.cachedRemoteRepositoryUniqueId(session); if (globallyUniqueRepositoryKeys) { + Function globallyUniqueRepositoryKeyFunction = + RepositoryIdHelper.cachedRemoteRepositoryUniqueId(session); repositoryKeyFunction = r -> { if (r instanceof RemoteRepository) { return globallyUniqueRepositoryKeyFunction.apply((RemoteRepository) r); From e5166231d2273c4795bd85c4158941a54cc2c9f3 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Wed, 19 Nov 2025 15:40:17 +0100 Subject: [PATCH 03/28] WIP --- .../util/repository/RepositoryIdHelper.java | 77 +++++++------------ 1 file changed, 26 insertions(+), 51 deletions(-) diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index ed4c2b482e..16d91594e3 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -46,27 +46,6 @@ public final class RepositoryIdHelper { private RepositoryIdHelper() {} - private static final String CENTRAL_REPOSITORY_ID = "central"; - private static final Collection CENTRAL_URLS = Collections.unmodifiableList(Arrays.asList( - "https://repo.maven.apache.org/maven2", - "https://repo1.maven.org/maven2", - "https://maven-central.storage-download.googleapis.com/maven2")); - private static final Predicate CENTRAL_DIRECT_ONLY = - remoteRepository -> CENTRAL_REPOSITORY_ID.equals(remoteRepository.getId()) - && "https".equals(remoteRepository.getProtocol().toLowerCase(Locale.ENGLISH)) - && CENTRAL_URLS.stream().anyMatch(remoteUrl -> { - String rurl = remoteRepository.getUrl().toLowerCase(Locale.ENGLISH); - if (rurl.endsWith("/")) { - rurl = rurl.substring(0, rurl.length() - 1); - } - return rurl.equals(remoteUrl); - }) - && remoteRepository.getPolicy(false).isEnabled() - && !remoteRepository.getPolicy(true).isEnabled() - && remoteRepository.getMirroredRepositories().isEmpty() - && !remoteRepository.isRepositoryManager() - && !remoteRepository.isBlocked(); - /** * Returns same instance of (session cached) function for session. * @@ -110,40 +89,36 @@ private static Function cachedRemoteRepositoryUniqueId * This method is costly, so should be invoked sparingly, or cache results if needed. */ public static String remoteRepositoryUniqueId(RemoteRepository repository) { - if (CENTRAL_DIRECT_ONLY.test(repository)) { - return CENTRAL_REPOSITORY_ID; + StringBuilder buffer = new StringBuilder(256); + buffer.append(repository.getId()); + buffer.append(" (").append(repository.getUrl()); + buffer.append(", ").append(repository.getContentType()); + boolean r = repository.getPolicy(false).isEnabled(), + s = repository.getPolicy(true).isEnabled(); + if (r && s) { + buffer.append(", releases+snapshots"); + } else if (r) { + buffer.append(", releases"); + } else if (s) { + buffer.append(", snapshots"); } else { - StringBuilder buffer = new StringBuilder(256); - buffer.append(repository.getId()); - buffer.append(" (").append(repository.getUrl()); - buffer.append(", ").append(repository.getContentType()); - boolean r = repository.getPolicy(false).isEnabled(), - s = repository.getPolicy(true).isEnabled(); - if (r && s) { - buffer.append(", releases+snapshots"); - } else if (r) { - buffer.append(", releases"); - } else if (s) { - buffer.append(", snapshots"); - } else { - buffer.append(", disabled"); - } - if (repository.isRepositoryManager()) { - buffer.append(", managed"); - } - if (!repository.getMirroredRepositories().isEmpty()) { - buffer.append(", mirrorOf("); - for (RemoteRepository mirroredRepo : repository.getMirroredRepositories()) { - buffer.append(remoteRepositoryUniqueId(mirroredRepo)); - } - buffer.append(")"); - } - if (repository.isBlocked()) { - buffer.append(", blocked"); + buffer.append(", disabled"); + } + if (repository.isRepositoryManager()) { + buffer.append(", managed"); + } + if (!repository.getMirroredRepositories().isEmpty()) { + buffer.append(", mirrorOf("); + for (RemoteRepository mirroredRepo : repository.getMirroredRepositories()) { + buffer.append(remoteRepositoryUniqueId(mirroredRepo)); } buffer.append(")"); - return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(buffer.toString()); } + if (repository.isBlocked()) { + buffer.append(", blocked"); + } + buffer.append(")"); + return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(buffer.toString()); } /** From cfe231d64fc3c67c7030d2c61b24dfae69e0a668 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Wed, 19 Nov 2025 15:41:14 +0100 Subject: [PATCH 04/28] Remove unsed --- .../impl/LocalPathPrefixComposerFactorySupport.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java index 8a70cebd15..b86223b53a 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java @@ -220,13 +220,6 @@ protected String getSnapshotsPrefix(RepositorySystemSession session) { session, DEFAULT_SNAPSHOTS_PREFIX, CONFIG_PROP_SNAPSHOTS_PREFIX, R1_CONF_PROP_SNAPSHOTS_PREFIX); } - protected boolean isGloballyUniqueRepositoryKeys(RepositorySystemSession session) { - return ConfigUtils.getBoolean( - session, - EnhancedLocalRepositoryManagerFactory.DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, - EnhancedLocalRepositoryManagerFactory.CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS); - } - /** * Support class for composers: it defines protected members for all the predefined configuration values and * provides default implementation for methods. Implementors may change it's behaviour by overriding methods. From ab265fa51d36bb2f1fc0b67cc7927eb720acf5e5 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Wed, 19 Nov 2025 15:51:02 +0100 Subject: [PATCH 05/28] WIP --- .../eclipse/aether/util/repository/RepositoryIdHelper.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index 16d91594e3..982a57fc0b 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -18,13 +18,8 @@ */ package org.eclipse.aether.util.repository; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; -import java.util.function.Predicate; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.ArtifactRepository; From f11bcf7792195d32815ce14478fe7697075619c7 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Wed, 19 Nov 2025 21:04:56 +0100 Subject: [PATCH 06/28] WIP --- ...DefaultLocalPathPrefixComposerFactory.java | 6 +- .../impl/EnhancedLocalRepositoryManager.java | 7 +- ...EnhancedLocalRepositoryManagerFactory.java | 19 +-- ...LocalPathPrefixComposerFactorySupport.java | 15 +- .../impl/SimpleLocalRepositoryManager.java | 37 +---- .../SimpleLocalRepositoryManagerFactory.java | 2 +- .../util/repository/RepositoryIdHelper.java | 141 ++++++++---------- 7 files changed, 92 insertions(+), 135 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java index 509776c102..08defd8d64 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java @@ -21,10 +21,12 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.util.function.BiFunction; import java.util.function.Function; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.ArtifactRepository; +import org.eclipse.aether.repository.RemoteRepository; import static org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory.repositoryKeyFunction; @@ -67,7 +69,7 @@ private DefaultLocalPathPrefixComposer( boolean splitRemoteRepositoryLast, String releasesPrefix, String snapshotsPrefix, - Function idToPathSegmentFunction) { + BiFunction repositoryKeyFunction) { super( split, localPrefix, @@ -78,7 +80,7 @@ private DefaultLocalPathPrefixComposer( splitRemoteRepositoryLast, releasesPrefix, snapshotsPrefix, - idToPathSegmentFunction); + repositoryKeyFunction); } } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java index e55150f3fe..c1b80999fd 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java @@ -27,12 +27,11 @@ import java.util.Map; import java.util.Objects; import java.util.Properties; -import java.util.function.Function; +import java.util.function.BiFunction; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.metadata.Metadata; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.LocalArtifactRegistration; import org.eclipse.aether.repository.LocalArtifactRequest; import org.eclipse.aether.repository.LocalArtifactResult; @@ -72,11 +71,11 @@ class EnhancedLocalRepositoryManager extends SimpleLocalRepositoryManager { EnhancedLocalRepositoryManager( Path basedir, LocalPathComposer localPathComposer, - Function idToPathSegmentFunction, + BiFunction repositoryKeyFunction, String trackingFilename, TrackingFileManager trackingFileManager, LocalPathPrefixComposer localPathPrefixComposer) { - super(basedir, "enhanced", localPathComposer, idToPathSegmentFunction); + super(basedir, "enhanced", localPathComposer, repositoryKeyFunction); this.trackingFilename = requireNonNull(trackingFilename); this.trackingFileManager = requireNonNull(trackingFileManager); this.localPathPrefixComposer = requireNonNull(localPathPrefixComposer); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index fb57821959..c4d030bdd7 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -22,6 +22,7 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.util.function.BiFunction; import java.util.function.Function; import org.eclipse.aether.ConfigurationProperties; @@ -95,24 +96,14 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan * * @since 2.0.14 */ - static Function repositoryKeyFunction(RepositorySystemSession session) { - Function idToPathSegmentFunction = - RepositoryIdHelper.cachedIdToPathSegment(session); - Function repositoryKeyFunction = idToPathSegmentFunction; + static BiFunction repositoryKeyFunction(RepositorySystemSession session) { boolean globallyUniqueRepositoryKeys = ConfigUtils.getBoolean( session, DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS); if (globallyUniqueRepositoryKeys) { - Function globallyUniqueRepositoryKeyFunction = - RepositoryIdHelper.cachedRemoteRepositoryUniqueId(session); - repositoryKeyFunction = r -> { - if (r instanceof RemoteRepository) { - return globallyUniqueRepositoryKeyFunction.apply((RemoteRepository) r); - } else { - return idToPathSegmentFunction.apply(r); - } - }; + return RepositoryIdHelper::globallyUniqueRepositoryKey; + } else { + return RepositoryIdHelper::simpleRepositoryKey; } - return repositoryKeyFunction; } @Inject diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java index b86223b53a..e22fcc8017 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java @@ -18,6 +18,7 @@ */ package org.eclipse.aether.internal.impl; +import java.util.function.BiFunction; import java.util.function.Function; import org.eclipse.aether.RepositorySystemSession; @@ -244,7 +245,7 @@ protected abstract static class LocalPathPrefixComposerSupport implements LocalP protected final String snapshotsPrefix; - protected final Function idToPathSegmentFunction; + protected final BiFunction repositoryKeyFunction; protected LocalPathPrefixComposerSupport( boolean split, @@ -256,7 +257,7 @@ protected LocalPathPrefixComposerSupport( boolean splitRemoteRepositoryLast, String releasesPrefix, String snapshotsPrefix, - Function idToPathSegmentFunction) { + BiFunction repositoryKeyFunction) { this.split = split; this.localPrefix = localPrefix; this.splitLocal = splitLocal; @@ -266,7 +267,7 @@ protected LocalPathPrefixComposerSupport( this.splitRemoteRepositoryLast = splitRemoteRepositoryLast; this.releasesPrefix = releasesPrefix; this.snapshotsPrefix = snapshotsPrefix; - this.idToPathSegmentFunction = idToPathSegmentFunction; + this.repositoryKeyFunction = repositoryKeyFunction; } @Override @@ -288,13 +289,13 @@ public String getPathPrefixForRemoteArtifact(Artifact artifact, RemoteRepository } String result = remotePrefix; if (!splitRemoteRepositoryLast && splitRemoteRepository) { - result += "/" + idToPathSegmentFunction.apply(repository); + result += "/" + repositoryKeyFunction.apply(repository, null); } if (splitRemote) { result += "/" + (artifact.isSnapshot() ? snapshotsPrefix : releasesPrefix); } if (splitRemoteRepositoryLast && splitRemoteRepository) { - result += "/" + idToPathSegmentFunction.apply(repository); + result += "/" + repositoryKeyFunction.apply(repository, null); } return result; } @@ -318,13 +319,13 @@ public String getPathPrefixForRemoteMetadata(Metadata metadata, RemoteRepository } String result = remotePrefix; if (!splitRemoteRepositoryLast && splitRemoteRepository) { - result += "/" + idToPathSegmentFunction.apply(repository); + result += "/" + repositoryKeyFunction.apply(repository, null); } if (splitRemote) { result += "/" + (isSnapshot(metadata) ? snapshotsPrefix : releasesPrefix); } if (splitRemoteRepositoryLast && splitRemoteRepository) { - result += "/" + idToPathSegmentFunction.apply(repository); + result += "/" + repositoryKeyFunction.apply(repository, null); } return result; } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java index eee54341ef..82c1f0ad72 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java @@ -23,6 +23,7 @@ import java.util.Objects; import java.util.SortedSet; import java.util.TreeSet; +import java.util.function.BiFunction; import java.util.function.Function; import org.eclipse.aether.RepositorySystemSession; @@ -51,17 +52,17 @@ class SimpleLocalRepositoryManager implements LocalRepositoryManager { private final LocalPathComposer localPathComposer; - private final Function idToPathSegmentFunction; + private final BiFunction repositoryKeyFunction; SimpleLocalRepositoryManager( Path basePath, String type, LocalPathComposer localPathComposer, - Function idToPathSegmentFunction) { + BiFunction repositoryKeyFunction) { requireNonNull(basePath, "base directory cannot be null"); repository = new LocalRepository(basePath.toAbsolutePath(), type); this.localPathComposer = requireNonNull(localPathComposer); - this.idToPathSegmentFunction = requireNonNull(idToPathSegmentFunction); + this.repositoryKeyFunction = requireNonNull(repositoryKeyFunction); } @Override @@ -101,35 +102,7 @@ public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository repos * of the remote repository (as it may change). */ protected String getRepositoryKey(RemoteRepository repository, String context) { - String key; - - if (repository.isRepositoryManager()) { - // repository serves dynamic contents, take request parameters into account for key - - StringBuilder buffer = new StringBuilder(128); - - buffer.append(idToPathSegmentFunction.apply(repository)); - - buffer.append('-'); - - SortedSet subKeys = new TreeSet<>(); - for (RemoteRepository mirroredRepo : repository.getMirroredRepositories()) { - subKeys.add(mirroredRepo.getId()); - } - - StringDigestUtil sha1 = StringDigestUtil.sha1(); - sha1.update(context); - for (String subKey : subKeys) { - sha1.update(subKey); - } - buffer.append(sha1.digest()); - - key = buffer.toString(); - } else { - key = idToPathSegmentFunction.apply(repository); - } - - return key; + return repositoryKeyFunction.apply(repository, context); } @Override diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java index c1e4ccd21d..3e02825e50 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java @@ -65,7 +65,7 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local repository.getBasePath(), "simple", localPathComposer, - RepositoryIdHelper.cachedIdToPathSegment(session)); + RepositoryIdHelper::simpleRepositoryKey); } else { throw new NoLocalRepositoryManagerException(repository); } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index 982a57fc0b..a836517b93 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -18,7 +18,11 @@ */ package org.eclipse.aether.util.repository; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; import java.util.function.Function; import org.eclipse.aether.RepositorySystemSession; @@ -42,36 +46,47 @@ public final class RepositoryIdHelper { private RepositoryIdHelper() {} /** - * Returns same instance of (session cached) function for session. + * Simple {@code repositoryKey} function (classic). Returns {@link RemoteRepository#getId()}, unless + * {@link RemoteRepository#isRepositoryManager()} returns {@code true}, in which case this method creates + * unique identifier based on ID and current configuration of the remote repository (as it may change). * * @since 2.0.14 - */ - @SuppressWarnings("unchecked") - public static Function cachedRemoteRepositoryUniqueId(RepositorySystemSession session) { - requireNonNull(session, "session"); - return (Function) session.getData() - .computeIfAbsent( - RepositoryIdHelper.class.getSimpleName() + "-remoteRepositoryUniqueIdFunction", - () -> cachedRemoteRepositoryUniqueIdFunction(session)); + **/ + public static String simpleRepositoryKey(RemoteRepository repository, String context) { + String key; + if (repository.isRepositoryManager()) { + StringBuilder buffer = new StringBuilder(128); + buffer.append(idToPathSegment(repository)); + buffer.append('-'); + SortedSet subKeys = new TreeSet<>(); + for (RemoteRepository mirroredRepo : repository.getMirroredRepositories()) { + subKeys.add(mirroredRepo.getId()); + } + StringDigestUtil sha1 = StringDigestUtil.sha1(); + sha1.update(context); + for (String subKey : subKeys) { + sha1.update(subKey); + } + buffer.append(sha1.digest()); + key = buffer.toString(); + } else { + key = idToPathSegment(repository); + } + return key; } /** - * Returns new instance of function backed by cached or uncached (if session has no cache set) - * {@link #remoteRepositoryUniqueId(RemoteRepository)} method call. - */ - @SuppressWarnings("unchecked") - private static Function cachedRemoteRepositoryUniqueIdFunction( - RepositorySystemSession session) { - if (session.getCache() != null) { - return repository -> ((ConcurrentHashMap) session.getCache() - .computeIfAbsent( - session, - RepositoryIdHelper.class.getSimpleName() + "-remoteRepositoryUniqueIdCache", - ConcurrentHashMap::new)) - .computeIfAbsent(repository, id -> remoteRepositoryUniqueId(repository)); - } else { - return RepositoryIdHelper::remoteRepositoryUniqueId; // uncached + * Globally unique {@code repositoryKey} function. + * + * @since 2.0.14 + **/ + public static String globallyUniqueRepositoryKey(RemoteRepository repository, String context) { + String id = idToPathSegment(repository); + String description = remoteRepositoryDescription(repository); + if (context != null && !context.isEmpty()) { + description += context; } + return id + "-" + StringDigestUtil.sha1(description); } /** @@ -84,6 +99,31 @@ private static Function cachedRemoteRepositoryUniqueId * This method is costly, so should be invoked sparingly, or cache results if needed. */ public static String remoteRepositoryUniqueId(RemoteRepository repository) { + return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(remoteRepositoryDescription(repository)); + } + + /** + * This method returns the passed in {@link ArtifactRepository#getId()} value, modifying it if needed, making sure that + * returned repository ID is "path segment" safe. Ideally, this method should never modify repository ID, as + * Maven validation prevents use of illegal FS characters in them, but we found in Maven Central several POMs that + * define remote repositories with illegal FS characters in their ID. + */ + private static String idToPathSegment(ArtifactRepository repository) { + if (repository instanceof RemoteRepository) { + return PathUtils.stringToPathSegment(repository.getId()); + } else { + return repository.getId(); + } + } + + /** + * Creates unique string for given {@link RemoteRepository}. Ignores following properties: + *
    + *
  • {@link RemoteRepository#getAuthentication()}
  • + *
  • {@link RemoteRepository#getProxy()}
  • + *
+ */ + private static String remoteRepositoryDescription(RemoteRepository repository) { StringBuilder buffer = new StringBuilder(256); buffer.append(repository.getId()); buffer.append(" (").append(repository.getUrl()); @@ -105,7 +145,7 @@ public static String remoteRepositoryUniqueId(RemoteRepository repository) { if (!repository.getMirroredRepositories().isEmpty()) { buffer.append(", mirrorOf("); for (RemoteRepository mirroredRepo : repository.getMirroredRepositories()) { - buffer.append(remoteRepositoryUniqueId(mirroredRepo)); + buffer.append(remoteRepositoryDescription(mirroredRepo)); } buffer.append(")"); } @@ -113,55 +153,6 @@ public static String remoteRepositoryUniqueId(RemoteRepository repository) { buffer.append(", blocked"); } buffer.append(")"); - return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(buffer.toString()); - } - - /** - * Returns same instance of (session cached) function for session. - */ - @SuppressWarnings("unchecked") - public static Function cachedIdToPathSegment(RepositorySystemSession session) { - requireNonNull(session, "session"); - return (Function) session.getData() - .computeIfAbsent( - RepositoryIdHelper.class.getSimpleName() + "-idToPathSegmentFunction", - () -> cachedIdToPathSegmentFunction(session)); - } - - /** - * Returns new instance of function backed by cached or uncached (if session has no cache set) - * {@link #idToPathSegment(ArtifactRepository)} method call. - */ - @SuppressWarnings("unchecked") - private static Function cachedIdToPathSegmentFunction(RepositorySystemSession session) { - if (session.getCache() != null) { - return repository -> ((ConcurrentHashMap) session.getCache() - .computeIfAbsent( - session, - RepositoryIdHelper.class.getSimpleName() + "-idToPathSegmentCache", - ConcurrentHashMap::new)) - .computeIfAbsent(repository.getId(), id -> idToPathSegment(repository)); - } else { - return RepositoryIdHelper::idToPathSegment; // uncached - } - } - - /** - * This method returns the passed in {@link ArtifactRepository#getId()} value, modifying it if needed, making sure that - * returned repository ID is "path segment" safe. Ideally, this method should never modify repository ID, as - * Maven validation prevents use of illegal FS characters in them, but we found in Maven Central several POMs that - * define remote repositories with illegal FS characters in their ID. - *

- * This method is simplistic on purpose, and if frequently used, best if results are cached (per session), - * see {@link #cachedIdToPathSegment(RepositorySystemSession)} method. - * - * @see #cachedIdToPathSegment(RepositorySystemSession) - */ - private static String idToPathSegment(ArtifactRepository repository) { - if (repository instanceof RemoteRepository) { - return PathUtils.stringToPathSegment(repository.getId()); - } else { - return repository.getId(); - } + return buffer.toString(); } } From d2d4a5a6cf00b5c27f8279ce08c37d8b6181e4a8 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 20 Nov 2025 18:51:45 +0100 Subject: [PATCH 07/28] Last bits --- .../aether/repository/LocalRepository.java | 15 ++++--- .../repository/WorkspaceRepository.java | 9 +++-- .../resolver/examples/resolver/Resolver.java | 3 +- .../examples/resolver/ResolverDemo.java | 12 +++--- ...DefaultLocalPathPrefixComposerFactory.java | 2 - ...EnhancedLocalRepositoryManagerFactory.java | 22 +++++++--- ...LocalPathPrefixComposerFactorySupport.java | 2 - .../impl/SimpleLocalRepositoryManager.java | 5 --- .../SimpleLocalRepositoryManagerFactory.java | 5 +-- .../eclipse/aether/internal/impl/Utils.java | 14 +++++++ .../FileTrustedChecksumsSourceSupport.java | 2 +- ...SparseDirectoryTrustedChecksumsSource.java | 6 +-- .../SummaryFileTrustedChecksumsSource.java | 6 +-- .../GroupIdRemoteRepositoryFilterSource.java | 4 +- .../PrefixesRemoteRepositoryFilterSource.java | 7 ++-- .../EnhancedLocalRepositoryManagerTest.java | 4 +- ...hancedSplitLocalRepositoryManagerTest.java | 4 +- .../SimpleLocalRepositoryManagerTest.java | 4 +- .../util/repository/RepositoryIdHelper.java | 40 +++++++++---------- .../repository/RepositoryIdHelperTest.java | 29 +------------- 20 files changed, 91 insertions(+), 104 deletions(-) diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java index 5e7b24c713..6c586c5724 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java @@ -37,14 +37,19 @@ public final class LocalRepository implements ArtifactRepository { private final String type; + private final int hashCode; + /** * Creates a new local repository with the specified base directory and unknown type. * * @param basedir The base directory of the repository, may be {@code null}. + * @deprecated Use {@link LocalRepository(Path)} instead. */ + @Deprecated public LocalRepository(String basedir) { this.basePath = Paths.get(RepositoryUriUtils.toUri(basedir)).toAbsolutePath(); this.type = ""; + this.hashCode = Objects.hash(this.basePath, this.type); } /** @@ -100,6 +105,7 @@ public LocalRepository(File basedir, String type) { public LocalRepository(Path basePath, String type) { this.basePath = basePath; this.type = (type != null) ? type : ""; + this.hashCode = Objects.hash(this.basePath, this.type); } @Override @@ -154,13 +160,6 @@ public boolean equals(Object obj) { @Override public int hashCode() { - int hash = 17; - hash = hash * 31 + hash(basePath); - hash = hash * 31 + hash(type); - return hash; - } - - private static int hash(Object obj) { - return obj != null ? obj.hashCode() : 0; + return hashCode; } } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceRepository.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceRepository.java index a922c0b079..9b0b440218 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceRepository.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceRepository.java @@ -18,6 +18,7 @@ */ package org.eclipse.aether.repository; +import java.util.Objects; import java.util.UUID; /** @@ -33,6 +34,8 @@ public final class WorkspaceRepository implements ArtifactRepository { private final Object key; + private final int hashCode; + /** * Creates a new workspace repository of type {@code "workspace"} and a random key. */ @@ -59,6 +62,7 @@ public WorkspaceRepository(String type) { public WorkspaceRepository(String type, Object key) { this.type = (type != null) ? type : ""; this.key = (key != null) ? key : UUID.randomUUID().toString().replace("-", ""); + this.hashCode = Objects.hash(type, key); } public String getContentType() { @@ -100,9 +104,6 @@ public boolean equals(Object obj) { @Override public int hashCode() { - int hash = 17; - hash = hash * 31 + getKey().hashCode(); - hash = hash * 31 + getContentType().hashCode(); - return hash; + return hashCode; } } diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/resolver/Resolver.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/resolver/Resolver.java index b44f4011bd..8b5e308f78 100644 --- a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/resolver/Resolver.java +++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/resolver/Resolver.java @@ -21,6 +21,7 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import org.apache.maven.resolver.examples.util.Booter; import org.eclipse.aether.RepositorySystem; @@ -55,7 +56,7 @@ public class Resolver { private final LocalRepository localRepository; - public Resolver(String[] args, String remoteRepository, String localRepository) { + public Resolver(String[] args, String remoteRepository, Path localRepository) { this.args = args; this.remoteRepository = remoteRepository; this.repositorySystem = Booter.newRepositorySystem(Booter.selectFactory(args)); diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/resolver/ResolverDemo.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/resolver/ResolverDemo.java index 7c0e70a467..eb8193bbdc 100644 --- a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/resolver/ResolverDemo.java +++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/resolver/ResolverDemo.java @@ -19,6 +19,7 @@ package org.apache.maven.resolver.examples.resolver; import java.io.File; +import java.nio.file.Paths; import java.util.List; import org.eclipse.aether.artifact.Artifact; @@ -37,7 +38,8 @@ public static void main(String[] args) throws Exception { System.out.println("------------------------------------------------------------"); System.out.println(ResolverDemo.class.getSimpleName()); - Resolver resolver = new Resolver(args, "https://repo.maven.apache.org/maven2/", "target/resolver-demo-repo"); + Resolver resolver = + new Resolver(args, "https://repo.maven.apache.org/maven2/", Paths.get("target/resolver-demo-repo")); ResolverResult result = resolver.resolve("junit", "junit", "4.13.2"); System.out.println("Result:"); @@ -47,8 +49,8 @@ public static void main(String[] args) throws Exception { } public void resolve(String[] args) throws DependencyResolutionException { - Resolver resolver = - new Resolver(args, "http://localhost:8081/nexus/content/groups/public", "target/aether-repo"); + Resolver resolver = new Resolver( + args, "http://localhost:8081/nexus/content/groups/public", Paths.get("target/aether-repo")); ResolverResult result = resolver.resolve("com.mycompany.app", "super-app", "1.0"); @@ -66,8 +68,8 @@ public void resolve(String[] args) throws DependencyResolutionException { } public void installAndDeploy(String[] args) throws InstallationException, DeploymentException { - Resolver resolver = - new Resolver(args, "http://localhost:8081/nexus/content/groups/public", "target/aether-repo"); + Resolver resolver = new Resolver( + args, "http://localhost:8081/nexus/content/groups/public", Paths.get("target/aether-repo")); Artifact artifact = new DefaultArtifact("com.mycompany.super", "super-core", "jar", "0.1-SNAPSHOT"); artifact = artifact.setFile(new File("jar-from-whatever-process.jar")); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java index 08defd8d64..74c488dda7 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java @@ -22,10 +22,8 @@ import javax.inject.Singleton; import java.util.function.BiFunction; -import java.util.function.Function; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; import static org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory.repositoryKeyFunction; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index c4d030bdd7..51f01fd3bd 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -22,12 +22,12 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.function.BiFunction; -import java.util.function.Function; import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.NoLocalRepositoryManagerException; @@ -96,11 +96,21 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan * * @since 2.0.14 */ + @SuppressWarnings("unchecked") static BiFunction repositoryKeyFunction(RepositorySystemSession session) { - boolean globallyUniqueRepositoryKeys = ConfigUtils.getBoolean( - session, DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS); - if (globallyUniqueRepositoryKeys) { - return RepositoryIdHelper::globallyUniqueRepositoryKey; + if (ConfigUtils.getBoolean( + session, DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS)) { + // this is expensive method; cache it in session (repo -> context -> ID) + return (repository, context) -> ((ConcurrentMap>) + session.getData() + .computeIfAbsent( + EnhancedLocalRepositoryManagerFactory.class.getName() + + ".repositoryKeyFunction", + ConcurrentHashMap::new)) + .computeIfAbsent(repository, k1 -> new ConcurrentHashMap<>()) + .computeIfAbsent( + context == null ? "" : context, + k2 -> RepositoryIdHelper.globallyUniqueRepositoryKey(repository, context)); } else { return RepositoryIdHelper::simpleRepositoryKey; } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java index e22fcc8017..f26c329aab 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java @@ -19,12 +19,10 @@ package org.eclipse.aether.internal.impl; import java.util.function.BiFunction; -import java.util.function.Function; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.metadata.Metadata; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.util.ConfigUtils; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java index 82c1f0ad72..f1da03db90 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java @@ -21,15 +21,11 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; -import java.util.SortedSet; -import java.util.TreeSet; import java.util.function.BiFunction; -import java.util.function.Function; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.metadata.Metadata; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.LocalArtifactRegistration; import org.eclipse.aether.repository.LocalArtifactRequest; import org.eclipse.aether.repository.LocalArtifactResult; @@ -39,7 +35,6 @@ import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.RemoteRepository; -import org.eclipse.aether.util.StringDigestUtil; import static java.util.Objects.requireNonNull; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java index 3e02825e50..4be3c28333 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java @@ -62,10 +62,7 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local if ("".equals(repository.getContentType()) || "simple".equals(repository.getContentType())) { return new SimpleLocalRepositoryManager( - repository.getBasePath(), - "simple", - localPathComposer, - RepositoryIdHelper::simpleRepositoryKey); + repository.getBasePath(), "simple", localPathComposer, RepositoryIdHelper::simpleRepositoryKey); } else { throw new NoLocalRepositoryManagerException(repository); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java index 87cb48ae12..d869a12ddb 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; @@ -31,6 +33,7 @@ import org.eclipse.aether.impl.OfflineController; import org.eclipse.aether.installation.InstallRequest; import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ResolutionErrorPolicy; import org.eclipse.aether.resolution.ResolutionErrorPolicyRequest; @@ -39,6 +42,7 @@ import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator; import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory; import org.eclipse.aether.transfer.RepositoryOfflineException; +import org.eclipse.aether.util.repository.RepositoryIdHelper; /** * Internal utility methods. @@ -207,4 +211,14 @@ public static void checkOffline( offlineController.checkOffline(session, repository); } } + + /** + * Shared and cached {@link RepositoryIdHelper#idToPathSegment(ArtifactRepository)} method, + */ + @SuppressWarnings("unchecked") + public static String cachedIdToPathSegment(RepositorySystemSession session, ArtifactRepository artifactRepository) { + return ((ConcurrentMap) session.getData() + .computeIfAbsent(Utils.class.getName() + ".cachedIdToPathSegment", ConcurrentHashMap::new)) + .computeIfAbsent(artifactRepository, RepositoryIdHelper::idToPathSegment); + } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java index fc417884cf..5657a585b1 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java @@ -53,7 +53,7 @@ * * @since 1.9.0 */ -abstract class FileTrustedChecksumsSourceSupport implements TrustedChecksumsSource { +public abstract class FileTrustedChecksumsSourceSupport implements TrustedChecksumsSource { protected static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_AETHER + "trustedChecksumsSource."; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java index 1d6af287ba..6162b6c951 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java @@ -34,11 +34,11 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.internal.impl.LocalPathComposer; +import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.io.ChecksumProcessor; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -134,7 +134,7 @@ protected Map doGetTrustedArtifactChecksums( Path checksumPath = basedir.resolve(calculateArtifactPath( originAware, artifact, - RepositoryIdHelper.cachedIdToPathSegment(session).apply(artifactRepository), + Utils.cachedIdToPathSegment(session, artifactRepository), checksumAlgorithmFactory)); if (!Files.isRegularFile(checksumPath)) { @@ -167,7 +167,7 @@ protected Writer doGetTrustedArtifactChecksumsWriter(RepositorySystemSession ses return new SparseDirectoryWriter( getBasedir(session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, true), isOriginAware(session), - RepositoryIdHelper.cachedIdToPathSegment(session)); + r -> Utils.cachedIdToPathSegment(session, r)); } private String calculateArtifactPath( diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java index 85c9be19f5..b6827c5c95 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java @@ -43,11 +43,11 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.LocalPathComposer; +import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -177,7 +177,7 @@ protected Map doGetTrustedArtifactChecksums( Path summaryFile = summaryFile( basedir, originAware, - RepositoryIdHelper.cachedIdToPathSegment(session).apply(artifactRepository), + Utils.cachedIdToPathSegment(session, artifactRepository), checksumAlgorithmFactory.getFileExtension()); ConcurrentHashMap algorithmChecksums = checksums.computeIfAbsent(summaryFile, f -> loadProvidedChecksums(summaryFile)); @@ -199,7 +199,7 @@ protected Writer doGetTrustedArtifactChecksumsWriter(RepositorySystemSession ses checksums, getBasedir(session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, true), isOriginAware(session), - RepositoryIdHelper.cachedIdToPathSegment(session)); + r -> Utils.cachedIdToPathSegment(session, r)); } /** 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 3d6643876f..dd46993319 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 @@ -42,6 +42,7 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.RepositorySystemLifecycle; +import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.internal.impl.filter.ruletree.GroupTree; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.repository.RemoteRepository; @@ -50,7 +51,6 @@ import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -249,7 +249,7 @@ private Path ruleFile(RepositorySystemSession session, RemoteRepository remoteRe 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) + + Utils.cachedIdToPathSegment(session, remoteRepository) + GROUP_ID_FILE_SUFFIX)); } 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 9a4b2c3019..117e621c31 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 @@ -36,6 +36,7 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.MetadataResolver; import org.eclipse.aether.impl.RemoteRepositoryManager; +import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.internal.impl.filter.prefixes.PrefixesSource; import org.eclipse.aether.internal.impl.filter.ruletree.PrefixTree; import org.eclipse.aether.metadata.DefaultMetadata; @@ -49,7 +50,6 @@ import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider; import org.eclipse.aether.transfer.NoRepositoryLayoutException; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -318,9 +318,8 @@ private PrefixTree loadPrefixTree( private Path resolvePrefixesFromLocalConfiguration( RepositorySystemSession session, Path baseDir, RemoteRepository remoteRepository) { - Path filePath = baseDir.resolve(PREFIXES_FILE_PREFIX - + RepositoryIdHelper.cachedIdToPathSegment(session).apply(remoteRepository) - + PREFIXES_FILE_SUFFIX); + Path filePath = baseDir.resolve( + PREFIXES_FILE_PREFIX + Utils.cachedIdToPathSegment(session, remoteRepository) + PREFIXES_FILE_SUFFIX); if (Files.isReadable(filePath)) { return filePath; } else { diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java index 7d8a296cc5..25c6af7474 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java @@ -33,13 +33,13 @@ import org.eclipse.aether.metadata.DefaultMetadata; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.metadata.Metadata.Nature; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.LocalArtifactRegistration; import org.eclipse.aether.repository.LocalArtifactRequest; import org.eclipse.aether.repository.LocalArtifactResult; import org.eclipse.aether.repository.LocalMetadataRequest; import org.eclipse.aether.repository.LocalMetadataResult; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -108,7 +108,7 @@ protected EnhancedLocalRepositoryManager getManager() { return new EnhancedLocalRepositoryManager( basedir.toPath(), new DefaultLocalPathComposer(), - ArtifactRepository::getId, + RepositoryIdHelper::simpleRepositoryKey, "_remote.repositories", trackingFileManager, new DefaultLocalPathPrefixComposerFactory().createComposer(session)); diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java index 4a78ea3026..dbf37e80c2 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java @@ -20,8 +20,8 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -34,7 +34,7 @@ protected EnhancedLocalRepositoryManager getManager() { return new EnhancedLocalRepositoryManager( basedir.toPath(), new DefaultLocalPathComposer(), - ArtifactRepository::getId, + RepositoryIdHelper::simpleRepositoryKey, "_remote.repositories", trackingFileManager, new DefaultLocalPathPrefixComposerFactory().createComposer(session)); diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerTest.java index fbe2409920..5bd6741364 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerTest.java @@ -26,10 +26,10 @@ import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.internal.test.util.TestFileUtils; import org.eclipse.aether.internal.test.util.TestUtils; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.LocalArtifactRequest; import org.eclipse.aether.repository.LocalArtifactResult; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -50,7 +50,7 @@ public class SimpleLocalRepositoryManagerTest { @BeforeEach void setup() throws IOException { manager = new SimpleLocalRepositoryManager( - basedir.toPath(), "simple", new DefaultLocalPathComposer(), ArtifactRepository::getId); + basedir.toPath(), "simple", new DefaultLocalPathComposer(), RepositoryIdHelper::simpleRepositoryKey); session = TestUtils.newSession(); } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index 4afdce3e7f..0722a458ad 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -20,24 +20,20 @@ import java.util.SortedSet; import java.util.TreeSet; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.BiFunction; -import java.util.function.Function; -import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.util.PathUtils; import org.eclipse.aether.util.StringDigestUtil; -import static java.util.Objects.requireNonNull; - /** - * Helper class for {@link ArtifactRepository#getId()} handling. This class provides helper function (cached or uncached) - * to get id of repository as it was originally envisioned: as path safe. While POMs are validated by Maven, there are - * POMs out there that somehow define repositories with unsafe characters in their id. The problem affects mostly + * Helper class for {@link ArtifactRepository#getId()} handling. This class provides helper methods + * to get id of repository as it was originally envisioned: as path safe, unique, etc. While POMs are validated by Maven, + * there are POMs out there that somehow define repositories with unsafe characters in their id. The problem affects mostly * {@link RemoteRepository} instances, as all other implementations have fixed ids that are path safe. + *

+ * Important: multiple of these provided methods are not trivial processing-wise, and some sort of + * caching is warmly recommended. * * @see PathUtils * @since 2.0.11 @@ -49,6 +45,7 @@ private RepositoryIdHelper() {} * Simple {@code repositoryKey} function (classic). Returns {@link RemoteRepository#getId()}, unless * {@link RemoteRepository#isRepositoryManager()} returns {@code true}, in which case this method creates * unique identifier based on ID and current configuration of the remote repository (as it may change). + * This was the default method in Maven 3. * * @since 2.0.14 **/ @@ -76,25 +73,26 @@ public static String simpleRepositoryKey(RemoteRepository repository, String con } /** - * Globally unique {@code repositoryKey} function. + * Globally unique {@code repositoryKey} function. This repository key method returns same results as + * {@link #remoteRepositoryUniqueId(RemoteRepository)} if parameter context is {@code null} or empty string. * + * @see #remoteRepositoryUniqueId(RemoteRepository) * @since 2.0.14 **/ public static String globallyUniqueRepositoryKey(RemoteRepository repository, String context) { - String id = idToPathSegment(repository); String description = remoteRepositoryDescription(repository); if (context != null && !context.isEmpty()) { description += context; } - return id + "-" + StringDigestUtil.sha1(description); + return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(description); } /** - * Creates unique repository id for given {@link RemoteRepository}. For Maven Central this method will return - * string "central", while for any other remote repository it will return string created as - * {@code $(repository.id)-sha1(repository-aspects)}. The key material contains all relevant aspects - * of remote repository, so repository with same ID even if just policy changes (enabled/disabled), will map to - * different string id. The checksum and update policies are not participating in key creation. + * Creates unique repository id for given {@link RemoteRepository}. + * For any remote repository it will return string created as {@code $(repository.id)-sha1(repository-aspects)}. + * The key material contains all relevant aspects of remote repository, so repository with same ID even if just + * policy changes (enabled/disabled), will map to different string id. The checksum and update policies are not + * participating in key creation. *

* This method is costly, so should be invoked sparingly, or cache results if needed. *

@@ -113,7 +111,7 @@ public static String remoteRepositoryUniqueId(RemoteRepository repository) { * Maven validation prevents use of illegal FS characters in them, but we found in Maven Central several POMs that * define remote repositories with illegal FS characters in their ID. */ - private static String idToPathSegment(ArtifactRepository repository) { + public static String idToPathSegment(ArtifactRepository repository) { if (repository instanceof RemoteRepository) { return PathUtils.stringToPathSegment(repository.getId()); } else { @@ -128,7 +126,7 @@ private static String idToPathSegment(ArtifactRepository repository) { *

  • {@link RemoteRepository#getProxy()}
  • * */ - private static String remoteRepositoryDescription(RemoteRepository repository) { + public static String remoteRepositoryDescription(RemoteRepository repository) { StringBuilder buffer = new StringBuilder(256); buffer.append(repository.getId()); buffer.append(" (").append(repository.getUrl()); @@ -157,7 +155,7 @@ private static String remoteRepositoryDescription(RemoteRepository repository) { if (repository.isBlocked()) { buffer.append(", blocked"); } - buffer.append(")"); + buffer.append(", ").append(repository.getIntent().name()).append(")"); return buffer.toString(); } } diff --git a/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java b/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java index bdbc15c1e1..f9c10ee72a 100644 --- a/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java +++ b/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java @@ -28,15 +28,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertSame; public class RepositoryIdHelperTest { @Test - void caching() { + void idToPathSegment() { DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(s -> false); session.setCache(new DefaultRepositoryCache()); // session has cache set - Function safeId = RepositoryIdHelper.cachedIdToPathSegment(session); + Function safeId = RepositoryIdHelper::idToPathSegment; RemoteRepository good = new RemoteRepository.Builder("good", "default", "https://somewhere.com").build(); RemoteRepository bad = new RemoteRepository.Builder("bad/id", "default", "https://somewhere.com").build(); @@ -44,33 +42,10 @@ void caching() { String goodId = good.getId(); String goodFixedId = safeId.apply(good); assertEquals(goodId, goodFixedId); - assertSame(goodFixedId, safeId.apply(good)); String badId = bad.getId(); String badFixedId = safeId.apply(bad); assertNotEquals(badId, badFixedId); assertEquals("bad-SLASH-id", badFixedId); - assertSame(badFixedId, safeId.apply(bad)); - } - - @Test - void nonCaching() { - DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(s -> false); - session.setCache(null); // session has no cache set - Function safeId = RepositoryIdHelper.cachedIdToPathSegment(session); - - RemoteRepository good = new RemoteRepository.Builder("good", "default", "https://somewhere.com").build(); - RemoteRepository bad = new RemoteRepository.Builder("bad/id", "default", "https://somewhere.com").build(); - - String goodId = good.getId(); - String goodFixedId = safeId.apply(good); - assertEquals(goodId, goodFixedId); - assertNotSame(goodFixedId, safeId.apply(good)); - - String badId = bad.getId(); - String badFixedId = safeId.apply(bad); - assertNotEquals(badId, badFixedId); - assertEquals("bad-SLASH-id", badFixedId); - assertNotSame(badFixedId, safeId.apply(bad)); } } From e9cf9319c48fc0416b11458feb3b36df403d4a33 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 21 Nov 2025 13:16:16 +0100 Subject: [PATCH 08/28] Fix helper methods, add TODO --- .../util/repository/RepositoryIdHelper.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index 0722a458ad..cdad560d75 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -44,13 +44,13 @@ private RepositoryIdHelper() {} /** * Simple {@code repositoryKey} function (classic). Returns {@link RemoteRepository#getId()}, unless * {@link RemoteRepository#isRepositoryManager()} returns {@code true}, in which case this method creates - * unique identifier based on ID and current configuration of the remote repository (as it may change). - * This was the default method in Maven 3. + * unique identifier based on ID and current configuration of the remote repository and context. + *

    + * This was the default {@code repositoryKey} method in Maven 3. * * @since 2.0.14 **/ public static String simpleRepositoryKey(RemoteRepository repository, String context) { - String key; if (repository.isRepositoryManager()) { StringBuilder buffer = new StringBuilder(128); buffer.append(idToPathSegment(repository)); @@ -65,23 +65,28 @@ public static String simpleRepositoryKey(RemoteRepository repository, String con sha1.update(subKey); } buffer.append(sha1.digest()); - key = buffer.toString(); + return buffer.toString(); } else { - key = idToPathSegment(repository); + return idToPathSegment(repository); } - return key; } /** - * Globally unique {@code repositoryKey} function. This repository key method returns same results as + * Globally unique {@code repositoryKey} function. This method creates unique identifier based on ID and current + * configuration of the remote repository. If {@link RemoteRepository#isRepositoryManager()} returns {@code true}, + * the passed in {@code context} string is factored in as well. This repository key method returns same results as * {@link #remoteRepositoryUniqueId(RemoteRepository)} if parameter context is {@code null} or empty string. + *

    + * Important: this repository key can be considered "stable" for normal remote repositories (where only + * ID and URL matters). But, for mirror repositories, the key will change if mirror members change. + * TODO: reconsider this? * * @see #remoteRepositoryUniqueId(RemoteRepository) * @since 2.0.14 **/ public static String globallyUniqueRepositoryKey(RemoteRepository repository, String context) { String description = remoteRepositoryDescription(repository); - if (context != null && !context.isEmpty()) { + if (repository.isRepositoryManager() && context != null && !context.isEmpty()) { description += context; } return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(description); @@ -124,6 +129,7 @@ public static String idToPathSegment(ArtifactRepository repository) { *

      *
    • {@link RemoteRepository#getAuthentication()}
    • *
    • {@link RemoteRepository#getProxy()}
    • + *
    • {@link RemoteRepository#getIntent()}
    • *
    */ public static String remoteRepositoryDescription(RemoteRepository repository) { @@ -155,7 +161,7 @@ public static String remoteRepositoryDescription(RemoteRepository repository) { if (repository.isBlocked()) { buffer.append(", blocked"); } - buffer.append(", ").append(repository.getIntent().name()).append(")"); + buffer.append(")"); return buffer.toString(); } } From 8add1c7e396661b2074fbdf3f9590d2fa88b88a7 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 21 Nov 2025 13:31:20 +0100 Subject: [PATCH 09/28] Use cache for cache --- .../EnhancedLocalRepositoryManagerFactory.java | 18 ++++++++++++------ .../eclipse/aether/internal/impl/Utils.java | 11 ++++++++--- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index 51f01fd3bd..9d111053b5 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -98,21 +98,27 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan */ @SuppressWarnings("unchecked") static BiFunction repositoryKeyFunction(RepositorySystemSession session) { - if (ConfigUtils.getBoolean( - session, DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS)) { - // this is expensive method; cache it in session (repo -> context -> ID) + final boolean globallyUniqueRepositoryKeys = ConfigUtils.getBoolean( + session, DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS); + if (session.getCache() != null) { + // both are expensive methods; cache it in session (repo -> context -> ID) return (repository, context) -> ((ConcurrentMap>) - session.getData() + session.getCache() .computeIfAbsent( + session, EnhancedLocalRepositoryManagerFactory.class.getName() + ".repositoryKeyFunction", ConcurrentHashMap::new)) .computeIfAbsent(repository, k1 -> new ConcurrentHashMap<>()) .computeIfAbsent( context == null ? "" : context, - k2 -> RepositoryIdHelper.globallyUniqueRepositoryKey(repository, context)); + k2 -> globallyUniqueRepositoryKeys + ? RepositoryIdHelper.globallyUniqueRepositoryKey(repository, context) + : RepositoryIdHelper.simpleRepositoryKey(repository, context)); } else { - return RepositoryIdHelper::simpleRepositoryKey; + return globallyUniqueRepositoryKeys + ? RepositoryIdHelper::globallyUniqueRepositoryKey + : RepositoryIdHelper::simpleRepositoryKey; } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java index d869a12ddb..7b5d6f2037 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java @@ -217,8 +217,13 @@ public static void checkOffline( */ @SuppressWarnings("unchecked") public static String cachedIdToPathSegment(RepositorySystemSession session, ArtifactRepository artifactRepository) { - return ((ConcurrentMap) session.getData() - .computeIfAbsent(Utils.class.getName() + ".cachedIdToPathSegment", ConcurrentHashMap::new)) - .computeIfAbsent(artifactRepository, RepositoryIdHelper::idToPathSegment); + if (session.getCache() != null) { + return ((ConcurrentMap) session.getCache() + .computeIfAbsent( + session, Utils.class.getName() + ".cachedIdToPathSegment", ConcurrentHashMap::new)) + .computeIfAbsent(artifactRepository, RepositoryIdHelper::idToPathSegment); + } else { + return RepositoryIdHelper.idToPathSegment(artifactRepository); + } } } From cdb8c90dae0038927999893217002d33ac1653e9 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 21 Nov 2025 13:39:20 +0100 Subject: [PATCH 10/28] Don't make Simple unusable; cache simple repository key function as well Signed-off-by: Tamas Cservenak --- .../SimpleLocalRepositoryManagerFactory.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java index 4be3c28333..a5975138ab 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java @@ -22,10 +22,15 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; + import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.NoLocalRepositoryManagerException; +import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; import org.eclipse.aether.util.repository.RepositoryIdHelper; @@ -54,6 +59,7 @@ public SimpleLocalRepositoryManagerFactory(final LocalPathComposer localPathComp this.localPathComposer = requireNonNull(localPathComposer); } + @SuppressWarnings("unchecked") @Override public LocalRepositoryManager newInstance(RepositorySystemSession session, LocalRepository repository) throws NoLocalRepositoryManagerException { @@ -61,8 +67,21 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local requireNonNull(repository, "repository cannot be null"); if ("".equals(repository.getContentType()) || "simple".equals(repository.getContentType())) { + BiFunction repositoryKeyFunction = + RepositoryIdHelper::simpleRepositoryKey; + if (session.getCache() != null) { + repositoryKeyFunction = (r, c) -> ((ConcurrentMap>) + session.getCache() + .computeIfAbsent( + session, + EnhancedLocalRepositoryManagerFactory.class.getName() + + ".repositoryKeyFunction", + ConcurrentHashMap::new)) + .computeIfAbsent(r, k1 -> new ConcurrentHashMap<>()) + .computeIfAbsent(c == null ? "" : c, k2 -> RepositoryIdHelper.simpleRepositoryKey(r, c)); + } return new SimpleLocalRepositoryManager( - repository.getBasePath(), "simple", localPathComposer, RepositoryIdHelper::simpleRepositoryKey); + repository.getBasePath(), "simple", localPathComposer, repositoryKeyFunction); } else { throw new NoLocalRepositoryManagerException(repository); } From 00be04a14219b4f1ce4ff196d16aaa3deb74391c Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 21 Nov 2025 16:23:22 +0100 Subject: [PATCH 11/28] Use enum, add more functions to play. Consider this EXPERIMENTAL. --- ...EnhancedLocalRepositoryManagerFactory.java | 30 ++++----- .../util/repository/RepositoryIdHelper.java | 63 ++++++++++++++++++- 2 files changed, 75 insertions(+), 18 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index 9d111053b5..a8fe5be995 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -64,23 +64,21 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan public static final String DEFAULT_TRACKING_FILENAME = "_remote.repositories"; /** - * Make enhanced repository use "globally unique repository keys" (repository keys are used for designating - * cached metadata, artifact availability tracking and split repository prefix production). By default, this - * option is disabled. If enabled, repository keys produced by enhanced repository will be way different + * Configuration for "repository key" selection. + * Note: repository key functions other than "simple" produce repository keys will be way different * that those produced with previous versions or without this option enabled. Ideally, you may want to - * use empty local repository to populate with new repository key contained metadata, Interoperability between - * enabled and disabled affects only metadata and split repository (ie. split repository may not find existing - * caches, and may opt to re-download them). + * use empty local repository to populate with new repository key contained metadata, * * @since 2.0.14 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} - * @configurationType {@link java.lang.Boolean} + * @configurationType {@link java.lang.String} * @configurationDefaultValue {@link #DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS} */ public static final String CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS = CONFIG_PROPS_PREFIX + "globallyUniqueRepositoryKeys"; - public static final boolean DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS = false; + public static final String DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS = + RepositoryIdHelper.RepositoryKeyType.SIMPLE.name(); private float priority = 10.0f; @@ -98,8 +96,11 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan */ @SuppressWarnings("unchecked") static BiFunction repositoryKeyFunction(RepositorySystemSession session) { - final boolean globallyUniqueRepositoryKeys = ConfigUtils.getBoolean( - session, DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS); + final RepositoryIdHelper.RepositoryKeyType repositoryKeyType = + RepositoryIdHelper.RepositoryKeyType.valueOf(ConfigUtils.getString( + session, DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS)); + final RepositoryIdHelper.RepositoryKeyFunction repositoryKeyFunction = + RepositoryIdHelper.getRepositoryKeyFunction(repositoryKeyType); if (session.getCache() != null) { // both are expensive methods; cache it in session (repo -> context -> ID) return (repository, context) -> ((ConcurrentMap>) @@ -111,14 +112,9 @@ static BiFunction repositoryKeyFunction(Reposi ConcurrentHashMap::new)) .computeIfAbsent(repository, k1 -> new ConcurrentHashMap<>()) .computeIfAbsent( - context == null ? "" : context, - k2 -> globallyUniqueRepositoryKeys - ? RepositoryIdHelper.globallyUniqueRepositoryKey(repository, context) - : RepositoryIdHelper.simpleRepositoryKey(repository, context)); + context == null ? "" : context, k2 -> repositoryKeyFunction.apply(repository, context)); } else { - return globallyUniqueRepositoryKeys - ? RepositoryIdHelper::globallyUniqueRepositoryKey - : RepositoryIdHelper::simpleRepositoryKey; + return repositoryKeyFunction; } } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index cdad560d75..1c9e2dbbad 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -20,6 +20,7 @@ import java.util.SortedSet; import java.util.TreeSet; +import java.util.function.BiFunction; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; @@ -41,6 +42,52 @@ public final class RepositoryIdHelper { private RepositoryIdHelper() {} + /** + * Supported {@code repositoryKey} types. + * + * @since 2.0.14 + */ + public enum RepositoryKeyType { + /** + * The "simple" repository key, was default in Maven 3. + */ + SIMPLE, + /** + * Crafts unique repository key using {@link RemoteRepository#getId()} and {@link RemoteRepository#getUrl()}. + */ + ID_URL, + /** + * Crafts unique repository key using {@link RemoteRepository#getId()} and all the remaining properties of + * {@link RemoteRepository}. + */ + GURK + } + + /** + * The repository key function. + */ + @FunctionalInterface + public interface RepositoryKeyFunction extends BiFunction { + @Override + String apply(RemoteRepository repository, String context); + } + + /** + * Selector method for {@link RepositoryKeyFunction}. + */ + public static RepositoryKeyFunction getRepositoryKeyFunction(RepositoryKeyType keyType) { + switch (keyType) { + case SIMPLE: + return RepositoryIdHelper::simpleRepositoryKey; + case ID_URL: + return RepositoryIdHelper::idAndUrlRepositoryKey; + case GURK: + return RepositoryIdHelper::globallyUniqueRepositoryKey; + default: + throw new IllegalArgumentException("Unknown repository key type: " + keyType.name()); + } + } + /** * Simple {@code repositoryKey} function (classic). Returns {@link RemoteRepository#getId()}, unless * {@link RemoteRepository#isRepositoryManager()} returns {@code true}, in which case this method creates @@ -71,6 +118,20 @@ public static String simpleRepositoryKey(RemoteRepository repository, String con } } + /** + * The ID and URL {@code repositoryKey} function. This method creates unique identifier based on ID and URL + * of the remote repository. + * + * @since 2.0.14 + **/ + private static String idAndUrlRepositoryKey(RemoteRepository repository, String context) { + String seed = repository.getUrl(); + if (repository.isRepositoryManager() && context != null && !context.isEmpty()) { + seed += context; + } + return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(seed); + } + /** * Globally unique {@code repositoryKey} function. This method creates unique identifier based on ID and current * configuration of the remote repository. If {@link RemoteRepository#isRepositoryManager()} returns {@code true}, @@ -84,7 +145,7 @@ public static String simpleRepositoryKey(RemoteRepository repository, String con * @see #remoteRepositoryUniqueId(RemoteRepository) * @since 2.0.14 **/ - public static String globallyUniqueRepositoryKey(RemoteRepository repository, String context) { + private static String globallyUniqueRepositoryKey(RemoteRepository repository, String context) { String description = remoteRepositoryDescription(repository); if (repository.isRepositoryManager() && context != null && !context.isEmpty()) { description += context; From 1a2a6a81db267128dbb2bb4176cabac17c9036d2 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 21 Nov 2025 16:31:02 +0100 Subject: [PATCH 12/28] Rename config --- .../EnhancedLocalRepositoryManagerFactory.java | 16 ++++++---------- .../util/repository/RepositoryIdHelper.java | 7 ++++++- src/site/markdown/configuration.md | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index a8fe5be995..437225b9a7 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -64,7 +64,7 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan public static final String DEFAULT_TRACKING_FILENAME = "_remote.repositories"; /** - * Configuration for "repository key" selection. + * Configuration for "repository key" function. * Note: repository key functions other than "simple" produce repository keys will be way different * that those produced with previous versions or without this option enabled. Ideally, you may want to * use empty local repository to populate with new repository key contained metadata, @@ -72,13 +72,11 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan * @since 2.0.14 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} * @configurationType {@link java.lang.String} - * @configurationDefaultValue {@link #DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS} + * @configurationDefaultValue {@link #DEFAULT_REPOSITORY_KEY_FUNCTION} */ - public static final String CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS = - CONFIG_PROPS_PREFIX + "globallyUniqueRepositoryKeys"; + public static final String CONFIG_PROP_REPOSITORY_KEY_FUNCTION = CONFIG_PROPS_PREFIX + "repositoryKeyFunction"; - public static final String DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS = - RepositoryIdHelper.RepositoryKeyType.SIMPLE.name(); + public static final String DEFAULT_REPOSITORY_KEY_FUNCTION = "simple"; private float priority = 10.0f; @@ -96,11 +94,9 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan */ @SuppressWarnings("unchecked") static BiFunction repositoryKeyFunction(RepositorySystemSession session) { - final RepositoryIdHelper.RepositoryKeyType repositoryKeyType = - RepositoryIdHelper.RepositoryKeyType.valueOf(ConfigUtils.getString( - session, DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS)); final RepositoryIdHelper.RepositoryKeyFunction repositoryKeyFunction = - RepositoryIdHelper.getRepositoryKeyFunction(repositoryKeyType); + RepositoryIdHelper.getRepositoryKeyFunction(ConfigUtils.getString( + session, DEFAULT_REPOSITORY_KEY_FUNCTION, CONFIG_PROP_REPOSITORY_KEY_FUNCTION)); if (session.getCache() != null) { // both are expensive methods; cache it in session (repo -> context -> ID) return (repository, context) -> ((ConcurrentMap>) diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index 1c9e2dbbad..2fa9463f4c 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -18,6 +18,7 @@ */ package org.eclipse.aether.util.repository; +import java.util.Locale; import java.util.SortedSet; import java.util.TreeSet; import java.util.function.BiFunction; @@ -27,6 +28,8 @@ import org.eclipse.aether.util.PathUtils; import org.eclipse.aether.util.StringDigestUtil; +import static java.util.Objects.requireNonNull; + /** * Helper class for {@link ArtifactRepository#getId()} handling. This class provides helper methods * to get id of repository as it was originally envisioned: as path safe, unique, etc. While POMs are validated by Maven, @@ -75,7 +78,9 @@ public interface RepositoryKeyFunction extends BiFunctionsigstage.dev instead of public default sigstore.dev . | `false` | 2.0.2 | No | Session Configuration | | `"aether.interactive"` | `Boolean` | A flag indicating whether interaction with the user is allowed. | `false` | | No | Session Configuration | | `"aether.layout.maven2.checksumAlgorithms"` | `String` | Comma-separated list of checksum algorithms with which checksums are validated (downloaded) and generated (uploaded) with this layout. Resolver by default supports following algorithms: MD5, SHA-1, SHA-256 and SHA-512. New algorithms can be added by implementing ChecksumAlgorithmFactory component. | `"SHA-1,MD5"` | 1.8.0 | Yes | Session Configuration | -| `"aether.lrm.enhanced.globallyUniqueRepositoryKeys"` | `Boolean` | Make enhanced repository use "globally unique repository keys" (repository keys are used for designating cached metadata, artifact availability tracking and split repository prefix production). By default, this option is disabled. If enabled, repository keys produced by enhanced repository will be way different that those produced with previous versions or without this option enabled. Ideally, you may want to use empty local repository to populate with new repository key contained metadata, Interoperability between enabled and disabled affects only metadata and split repository (ie. split repository may not find existing caches, and may opt to re-download them). | `false` | 2.0.14 | No | Session Configuration | | `"aether.lrm.enhanced.localPrefix"` | `String` | The prefix to use for locally installed artifacts. | `"installed"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.releasesPrefix"` | `String` | The prefix to use for release artifacts. | `"releases"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.remotePrefix"` | `String` | The prefix to use for remotely cached artifacts. | `"cached"` | 1.8.1 | No | Session Configuration | +| `"aether.lrm.enhanced.repositoryKeyFunction"` | `String` | Configuration for "repository key" function. Note: repository key functions other than "simple" produce repository keys will be way different that those produced with previous versions or without this option enabled. Ideally, you may want to use empty local repository to populate with new repository key contained metadata, | `"simple"` | 2.0.14 | No | Session Configuration | | `"aether.lrm.enhanced.snapshotsPrefix"` | `String` | The prefix to use for snapshot artifacts. | `"snapshots"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.split"` | `Boolean` | Whether LRM should split local and remote artifacts. | `false` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.splitLocal"` | `Boolean` | Whether locally installed artifacts should be split by version (release/snapshot). | `false` | 1.8.1 | No | Session Configuration | From 467be59ac9267d5d6618f1d26769de759590a4e9 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Mon, 24 Nov 2025 15:25:26 +0100 Subject: [PATCH 13/28] Update --- ...EnhancedLocalRepositoryManagerFactory.java | 4 +- .../util/repository/RepositoryIdHelper.java | 75 +++++++++++-------- src/site/markdown/configuration.md | 2 +- 3 files changed, 47 insertions(+), 34 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index 437225b9a7..3c3e08f5c0 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -64,10 +64,10 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan public static final String DEFAULT_TRACKING_FILENAME = "_remote.repositories"; /** - * Configuration for "repository key" function. + * Experimental: Configuration for "repository key" function. * Note: repository key functions other than "simple" produce repository keys will be way different * that those produced with previous versions or without this option enabled. Ideally, you may want to - * use empty local repository to populate with new repository key contained metadata, + * use empty local repository to populate with new repository key contained metadata. * * @since 2.0.14 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index 2fa9463f4c..1ca9d666d9 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -18,6 +18,8 @@ */ package org.eclipse.aether.util.repository; +import java.util.ArrayList; +import java.util.Comparator; import java.util.Locale; import java.util.SortedSet; import java.util.TreeSet; @@ -59,6 +61,11 @@ public enum RepositoryKeyType { * Crafts unique repository key using {@link RemoteRepository#getId()} and {@link RemoteRepository#getUrl()}. */ ID_URL, + /** + * Crafts normalized unique repository key using {@link RemoteRepository#getId()} and all the remaining properties of + * {@link RemoteRepository} ignoring actual list of mirrors, if any (but mirrors are split). + */ + NGURK, /** * Crafts unique repository key using {@link RemoteRepository#getId()} and all the remaining properties of * {@link RemoteRepository}. @@ -86,6 +93,8 @@ public static RepositoryKeyFunction getRepositoryKeyFunction(String keyTypeStrin return RepositoryIdHelper::simpleRepositoryKey; case ID_URL: return RepositoryIdHelper::idAndUrlRepositoryKey; + case NGURK: + return RepositoryIdHelper::normalizedGloballyUniqueRepositoryKey; case GURK: return RepositoryIdHelper::globallyUniqueRepositoryKey; default: @@ -98,7 +107,8 @@ public static RepositoryKeyFunction getRepositoryKeyFunction(String keyTypeStrin * {@link RemoteRepository#isRepositoryManager()} returns {@code true}, in which case this method creates * unique identifier based on ID and current configuration of the remote repository and context. *

    - * This was the default {@code repositoryKey} method in Maven 3. + * This was the default {@code repositoryKey} method in Maven 3. Is exposed (others key methods are private) as + * it is directly used by "simple" LRM. * * @since 2.0.14 **/ @@ -137,43 +147,38 @@ private static String idAndUrlRepositoryKey(RemoteRepository repository, String return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(seed); } + /** + * Normalized globally unique {@code repositoryKey} function. This method creates unique identifier based on ID and current + * configuration of the remote repository ignoring mirrors (it records the fact repository is a mirror, but ignores + * mirrored repositories). If {@link RemoteRepository#isRepositoryManager()} returns {@code true}, the passed in + * {@code context} string is factored in as well. + * + * @since 2.0.14 + **/ + private static String normalizedGloballyUniqueRepositoryKey(RemoteRepository repository, String context) { + String seed = remoteRepositoryDescription(repository, false); + if (repository.isRepositoryManager() && context != null && !context.isEmpty()) { + seed += context; + } + return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(seed); + } + /** * Globally unique {@code repositoryKey} function. This method creates unique identifier based on ID and current * configuration of the remote repository. If {@link RemoteRepository#isRepositoryManager()} returns {@code true}, - * the passed in {@code context} string is factored in as well. This repository key method returns same results as - * {@link #remoteRepositoryUniqueId(RemoteRepository)} if parameter context is {@code null} or empty string. + * the passed in {@code context} string is factored in as well. *

    * Important: this repository key can be considered "stable" for normal remote repositories (where only * ID and URL matters). But, for mirror repositories, the key will change if mirror members change. - * TODO: reconsider this? * - * @see #remoteRepositoryUniqueId(RemoteRepository) * @since 2.0.14 **/ private static String globallyUniqueRepositoryKey(RemoteRepository repository, String context) { - String description = remoteRepositoryDescription(repository); + String seed = remoteRepositoryDescription(repository, true); if (repository.isRepositoryManager() && context != null && !context.isEmpty()) { - description += context; + seed += context; } - return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(description); - } - - /** - * Creates unique repository id for given {@link RemoteRepository}. - * For any remote repository it will return string created as {@code $(repository.id)-sha1(repository-aspects)}. - * The key material contains all relevant aspects of remote repository, so repository with same ID even if just - * policy changes (enabled/disabled), will map to different string id. The checksum and update policies are not - * participating in key creation. - *

    - * This method is costly, so should be invoked sparingly, or cache results if needed. - *

    - * Important:Do not use this method, or at least do consider when do you want to use it, as it - * totally disconnects repositories used in session. This method may be used under some special circumstances - * (ie reporting), but must not be used within Resolver (and Maven) session for "usual" resolution and - * deployment use cases. - */ - public static String remoteRepositoryUniqueId(RemoteRepository repository) { - return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(remoteRepositoryDescription(repository)); + return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(seed); } /** @@ -198,7 +203,7 @@ public static String idToPathSegment(ArtifactRepository repository) { *

  • {@link RemoteRepository#getIntent()}
  • * */ - public static String remoteRepositoryDescription(RemoteRepository repository) { + private static String remoteRepositoryDescription(RemoteRepository repository, boolean mirrorDetails) { StringBuilder buffer = new StringBuilder(256); buffer.append(repository.getId()); buffer.append(" (").append(repository.getUrl()); @@ -218,11 +223,19 @@ public static String remoteRepositoryDescription(RemoteRepository repository) { buffer.append(", managed"); } if (!repository.getMirroredRepositories().isEmpty()) { - buffer.append(", mirrorOf("); - for (RemoteRepository mirroredRepo : repository.getMirroredRepositories()) { - buffer.append(remoteRepositoryDescription(mirroredRepo)); + if (mirrorDetails) { + // sort them to make it stable ordering + ArrayList mirroredRepositories = + new ArrayList<>(repository.getMirroredRepositories()); + mirroredRepositories.sort(Comparator.comparing(RemoteRepository::getId)); + buffer.append(", mirrorOf("); + for (RemoteRepository mirroredRepo : mirroredRepositories) { + buffer.append(remoteRepositoryDescription(mirroredRepo, true)); + } + buffer.append(")"); + } else { + buffer.append(", isMirror"); } - buffer.append(")"); } if (repository.isBlocked()) { buffer.append(", blocked"); diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index ea09df5777..a04cbc8135 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -74,7 +74,7 @@ To modify this file, edit the template and regenerate. | `"aether.lrm.enhanced.localPrefix"` | `String` | The prefix to use for locally installed artifacts. | `"installed"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.releasesPrefix"` | `String` | The prefix to use for release artifacts. | `"releases"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.remotePrefix"` | `String` | The prefix to use for remotely cached artifacts. | `"cached"` | 1.8.1 | No | Session Configuration | -| `"aether.lrm.enhanced.repositoryKeyFunction"` | `String` | Configuration for "repository key" function. Note: repository key functions other than "simple" produce repository keys will be way different that those produced with previous versions or without this option enabled. Ideally, you may want to use empty local repository to populate with new repository key contained metadata, | `"simple"` | 2.0.14 | No | Session Configuration | +| `"aether.lrm.enhanced.repositoryKeyFunction"` | `String` | Experimental: Configuration for "repository key" function. Note: repository key functions other than "simple" produce repository keys will be way different that those produced with previous versions or without this option enabled. Ideally, you may want to use empty local repository to populate with new repository key contained metadata. | `"simple"` | 2.0.14 | No | Session Configuration | | `"aether.lrm.enhanced.snapshotsPrefix"` | `String` | The prefix to use for snapshot artifacts. | `"snapshots"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.split"` | `Boolean` | Whether LRM should split local and remote artifacts. | `false` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.splitLocal"` | `Boolean` | Whether locally installed artifacts should be split by version (release/snapshot). | `false` | 1.8.1 | No | Session Configuration | From 9b50dafe740c568ab37722d4f4fb69ad1afead87 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Mon, 24 Nov 2025 16:06:19 +0100 Subject: [PATCH 14/28] More key functions --- .../impl/DefaultRemoteRepositoryManager.java | 72 ++++++++++++++++--- ...EnhancedLocalRepositoryManagerFactory.java | 2 +- .../util/repository/RepositoryIdHelper.java | 40 +++++++++-- src/site/markdown/configuration.md | 1 + 4 files changed, 99 insertions(+), 16 deletions(-) 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 3ad72aade6..e2521962ef 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,8 +26,12 @@ import java.util.Arrays; import java.util.List; import java.util.ListIterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; import java.util.stream.Collectors; +import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositoryCache; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.impl.RemoteRepositoryManager; @@ -40,6 +44,8 @@ import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider; +import org.eclipse.aether.util.ConfigUtils; +import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,6 +56,51 @@ @Singleton @Named public class DefaultRemoteRepositoryManager implements RemoteRepositoryManager { + private static final String CONFIG_PROPS_PREFIX = + ConfigurationProperties.PREFIX_AETHER + "remoteRepositoryManager."; + + /** + * Experimental: Configuration for "repository key" function. + * Note: repository key functions other than "nid" produce repository keys will be way different + * that those produced with previous versions or without this option enabled. Manager uses this key to + * detect "same" remote repositories, and in case of mirrors, to merge them. + * + * @since 2.0.14 + * @configurationSource {@link RepositorySystemSession#getConfigProperties()} + * @configurationType {@link java.lang.String} + * @configurationDefaultValue {@link #DEFAULT_REPOSITORY_KEY_FUNCTION} + */ + public static final String CONFIG_PROP_REPOSITORY_KEY_FUNCTION = CONFIG_PROPS_PREFIX + "repositoryKeyFunction"; + + public static final String DEFAULT_REPOSITORY_KEY_FUNCTION = "nid"; + + /** + * Method that based on configuration returns the "repository key function". Used by {@link EnhancedLocalRepositoryManagerFactory} + * and {@link LocalPathPrefixComposerFactory}. + * + * @since 2.0.14 + */ + @SuppressWarnings("unchecked") + private static BiFunction repositoryKeyFunction(RepositorySystemSession session) { + final RepositoryIdHelper.RepositoryKeyFunction repositoryKeyFunction = + RepositoryIdHelper.getRepositoryKeyFunction(ConfigUtils.getString( + session, DEFAULT_REPOSITORY_KEY_FUNCTION, CONFIG_PROP_REPOSITORY_KEY_FUNCTION)); + if (session.getCache() != null) { + // both are expensive methods; cache it in session (repo -> context -> ID) + return (repository, context) -> ((ConcurrentMap>) + session.getCache() + .computeIfAbsent( + session, + EnhancedLocalRepositoryManagerFactory.class.getName() + + ".repositoryKeyFunction", + ConcurrentHashMap::new)) + .computeIfAbsent(repository, k1 -> new ConcurrentHashMap<>()) + .computeIfAbsent( + context == null ? "" : context, k2 -> repositoryKeyFunction.apply(repository, context)); + } else { + return repositoryKeyFunction; + } + } private static final class LoggedMirror { @@ -102,6 +153,7 @@ public List aggregateRepositories( return dominantRepositories; } + BiFunction repositoryKeyFunction = repositoryKeyFunction(session); MirrorSelector mirrorSelector = session.getMirrorSelector(); AuthenticationSelector authSelector = session.getAuthenticationSelector(); ProxySelector proxySelector = session.getProxySelector(); @@ -121,15 +173,16 @@ public List aggregateRepositories( } } - String key = getKey(repository); + String key = repositoryKeyFunction.apply(repository, null); for (ListIterator it = result.listIterator(); it.hasNext(); ) { RemoteRepository dominantRepository = it.next(); - if (key.equals(getKey(dominantRepository))) { + if (key.equals(repositoryKeyFunction.apply(dominantRepository, null))) { if (!dominantRepository.getMirroredRepositories().isEmpty() && !repository.getMirroredRepositories().isEmpty()) { - RemoteRepository mergedRepository = mergeMirrors(session, dominantRepository, repository); + RemoteRepository mergedRepository = + mergeMirrors(session, repositoryKeyFunction, dominantRepository, repository); if (mergedRepository != dominantRepository) { it.set(mergedRepository); } @@ -188,21 +241,20 @@ private void logMirror(RepositorySystemSession session, RemoteRepository origina original.getUrl()); } - private String getKey(RemoteRepository repository) { - return repository.getId(); - } - private RemoteRepository mergeMirrors( - RepositorySystemSession session, RemoteRepository dominant, RemoteRepository recessive) { + RepositorySystemSession session, + BiFunction repositoryKeyFunction, + RemoteRepository dominant, + RemoteRepository recessive) { RemoteRepository.Builder merged = null; RepositoryPolicy releases = null, snapshots = null; next: for (RemoteRepository rec : recessive.getMirroredRepositories()) { - String recKey = getKey(rec); + String recKey = repositoryKeyFunction.apply(rec, null); for (RemoteRepository dom : dominant.getMirroredRepositories()) { - if (recKey.equals(getKey(dom))) { + if (recKey.equals(repositoryKeyFunction.apply(dom, null))) { continue next; } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index 3c3e08f5c0..92f81ba0ca 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -93,7 +93,7 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan * @since 2.0.14 */ @SuppressWarnings("unchecked") - static BiFunction repositoryKeyFunction(RepositorySystemSession session) { + public static BiFunction repositoryKeyFunction(RepositorySystemSession session) { final RepositoryIdHelper.RepositoryKeyFunction repositoryKeyFunction = RepositoryIdHelper.getRepositoryKeyFunction(ConfigUtils.getString( session, DEFAULT_REPOSITORY_KEY_FUNCTION, CONFIG_PROP_REPOSITORY_KEY_FUNCTION)); diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index 1ca9d666d9..05c8277178 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -58,9 +58,17 @@ public enum RepositoryKeyType { */ SIMPLE, /** - * Crafts unique repository key using {@link RemoteRepository#getId()} and {@link RemoteRepository#getUrl()}. + * Crafts repository key using normalized {@link RemoteRepository#getId()}. */ - ID_URL, + NID, + /** + * Crafts repository key using hashed {@link RemoteRepository#getUrl()}. + */ + HURL, + /** + * Crafts unique repository key using normalized {@link RemoteRepository#getId()} and hashed {@link RemoteRepository#getUrl()}. + */ + NID_HURL, /** * Crafts normalized unique repository key using {@link RemoteRepository#getId()} and all the remaining properties of * {@link RemoteRepository} ignoring actual list of mirrors, if any (but mirrors are split). @@ -91,8 +99,12 @@ public static RepositoryKeyFunction getRepositoryKeyFunction(String keyTypeStrin switch (keyType) { case SIMPLE: return RepositoryIdHelper::simpleRepositoryKey; - case ID_URL: - return RepositoryIdHelper::idAndUrlRepositoryKey; + case NID: + return RepositoryIdHelper::nidRepositoryKey; + case HURL: + return RepositoryIdHelper::hurlRepositoryKey; + case NID_HURL: + return RepositoryIdHelper::nidAndHurlRepositoryKey; case NGURK: return RepositoryIdHelper::normalizedGloballyUniqueRepositoryKey; case GURK: @@ -133,13 +145,31 @@ public static String simpleRepositoryKey(RemoteRepository repository, String con } } + /** + * The ID {@code repositoryKey} function that uses only the {@link RemoteRepository#getId()} value to derive a key. + * + * @since 2.0.14 + **/ + private static String nidRepositoryKey(RemoteRepository repository, String context) { + return idToPathSegment(repository); + } + + /** + * The URL {@code repositoryKey} function that uses only the {@link RemoteRepository#getUrl()} hash to derive a key. + * + * @since 2.0.14 + **/ + private static String hurlRepositoryKey(RemoteRepository repository, String context) { + return StringDigestUtil.sha1(repository.getUrl()); + } + /** * The ID and URL {@code repositoryKey} function. This method creates unique identifier based on ID and URL * of the remote repository. * * @since 2.0.14 **/ - private static String idAndUrlRepositoryKey(RemoteRepository repository, String context) { + private static String nidAndHurlRepositoryKey(RemoteRepository repository, String context) { String seed = repository.getUrl(); if (repository.isRepositoryManager() && context != null && !context.isEmpty()) { seed += context; diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index a04cbc8135..aa4bc61bc2 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -108,6 +108,7 @@ To modify this file, edit the template and regenerate. | `"aether.remoteRepositoryFilter.prefixes.skipped"` | `Boolean` | Configuration to skip the Prefixes filter for given request. This configuration is evaluated and if true the prefixes remote filter will not kick in. Main use case is by filter itself, to prevent recursion during discovery of remote prefixes file, but this also allows other components to control prefix filter discovery, while leaving configuration like #CONFIG_PROP_ENABLED still show the "real state". | `false` | 2.0.14 | Yes | Session Configuration | | `"aether.remoteRepositoryFilter.prefixes.useMirroredRepositories"` | `Boolean` | Configuration to allow Prefixes filter to auto-discover prefixes from mirrored repositories as well. For this to work Maven should be aware that given remote repository is mirror and is usually backed by MRM. Given multiple MRM implementations messes up prefixes file, is better to just skip these. In other case, one may use #CONFIG_PROP_ENABLED with repository ID suffix. | `false` | 2.0.14 | Yes | Session Configuration | | `"aether.remoteRepositoryFilter.prefixes.useRepositoryManagers"` | `Boolean` | Configuration to allow Prefixes filter to auto-discover prefixes from repository managers as well. For this to work Maven should be aware that given remote repository is backed by repository manager. Given multiple MRM implementations messes up prefixes file, is better to just skip these. In other case, one may use #CONFIG_PROP_ENABLED with repository ID suffix. Note: as of today, nothing sets this on remote repositories, but is added for future. | `false` | 2.0.14 | Yes | Session Configuration | +| `"aether.remoteRepositoryManager.repositoryKeyFunction"` | `String` | Experimental: Configuration for "repository key" function. Note: repository key functions other than "nid" produce repository keys will be way different that those produced with previous versions or without this option enabled. Manager uses this key to detect "same" remote repositories, and in case of mirrors, to merge them. | `"nid"` | 2.0.14 | No | Session Configuration | | `"aether.snapshotFilter"` | `Boolean` | The key in the repository session's RepositorySystemSession#getConfigProperties() configurationproperties used to store a Boolean flag whether this filter should be forced to ban snapshots. By default, snapshots are only filtered if the root artifact is not a snapshot. | `false` | | No | Session Configuration | | `"aether.syncContext.named.basedir.locksDir"` | `String` | The location of the directory toi use for locks. If relative path, it is resolved from the local repository root. | `".locks"` | 1.9.0 | No | Session Configuration | | `"aether.syncContext.named.discriminating.discriminator"` | `String` | Configuration property to pass in discriminator, if needed. If not present, it is auto-calculated. | - | 1.7.0 | No | Session Configuration | From c2a6ddb4f72e2f49408707094bfe2e73c633f2e3 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 27 Nov 2025 12:06:25 +0100 Subject: [PATCH 15/28] Implement missing pieces --- .../aether/util/repository/RepositoryIdHelper.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index 05c8277178..f9f5e42ace 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -151,7 +151,11 @@ public static String simpleRepositoryKey(RemoteRepository repository, String con * @since 2.0.14 **/ private static String nidRepositoryKey(RemoteRepository repository, String context) { - return idToPathSegment(repository); + String seed = null; + if (repository.isRepositoryManager() && context != null && !context.isEmpty()) { + seed += context; + } + return idToPathSegment(repository) + (seed == null ? "" : "-" + StringDigestUtil.sha1(seed)); } /** @@ -160,7 +164,11 @@ private static String nidRepositoryKey(RemoteRepository repository, String conte * @since 2.0.14 **/ private static String hurlRepositoryKey(RemoteRepository repository, String context) { - return StringDigestUtil.sha1(repository.getUrl()); + String seed = null; + if (repository.isRepositoryManager() && context != null && !context.isEmpty()) { + seed += context; + } + return StringDigestUtil.sha1(repository.getUrl()) + (seed == null ? "" : "-" + StringDigestUtil.sha1(seed)); } /** From 8d506669c7bb9f6ac698e5caee92ecf8e3f64330 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 27 Nov 2025 12:44:21 +0100 Subject: [PATCH 16/28] Make key function used consistently across system. --- ...DefaultLocalPathPrefixComposerFactory.java | 8 +-- .../impl/DefaultRemoteRepositoryManager.java | 38 ++------------ ...EnhancedLocalRepositoryManagerFactory.java | 40 ++------------- .../eclipse/aether/internal/impl/Utils.java | 35 ++++++++++--- .../FileTrustedChecksumsSourceSupport.java | 35 +++++++++++++ ...SparseDirectoryTrustedChecksumsSource.java | 8 +-- .../SummaryFileTrustedChecksumsSource.java | 13 +++-- .../GroupIdRemoteRepositoryFilterSource.java | 7 ++- .../PrefixesRemoteRepositoryFilterSource.java | 10 +++- .../RemoteRepositoryFilterSourceSupport.java | 15 ++++++ .../util/repository/RepositoryIdHelper.java | 8 ++- .../repository/RepositoryIdHelperTest.java | 51 ------------------- src/site/markdown/configuration.md | 2 + 13 files changed, 119 insertions(+), 151 deletions(-) delete mode 100644 maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java index 74c488dda7..6f7700ec95 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java @@ -26,8 +26,6 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.RemoteRepository; -import static org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory.repositoryKeyFunction; - /** * Default local path prefix composer factory: it fully reuses {@link LocalPathPrefixComposerFactorySupport} class * without changing anything from it. @@ -49,7 +47,11 @@ public LocalPathPrefixComposer createComposer(RepositorySystemSession session) { isSplitRemoteRepositoryLast(session), getReleasesPrefix(session), getSnapshotsPrefix(session), - repositoryKeyFunction(session)); + Utils.repositoryKeyFunction( + EnhancedLocalRepositoryManagerFactory.class, + session, + EnhancedLocalRepositoryManagerFactory.DEFAULT_REPOSITORY_KEY_FUNCTION, + EnhancedLocalRepositoryManagerFactory.CONFIG_PROP_REPOSITORY_KEY_FUNCTION)); } /** 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 e2521962ef..d241efda52 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,8 +26,6 @@ import java.util.Arrays; import java.util.List; import java.util.ListIterator; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -44,8 +42,6 @@ import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider; -import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,34 +70,6 @@ public class DefaultRemoteRepositoryManager implements RemoteRepositoryManager { public static final String DEFAULT_REPOSITORY_KEY_FUNCTION = "nid"; - /** - * Method that based on configuration returns the "repository key function". Used by {@link EnhancedLocalRepositoryManagerFactory} - * and {@link LocalPathPrefixComposerFactory}. - * - * @since 2.0.14 - */ - @SuppressWarnings("unchecked") - private static BiFunction repositoryKeyFunction(RepositorySystemSession session) { - final RepositoryIdHelper.RepositoryKeyFunction repositoryKeyFunction = - RepositoryIdHelper.getRepositoryKeyFunction(ConfigUtils.getString( - session, DEFAULT_REPOSITORY_KEY_FUNCTION, CONFIG_PROP_REPOSITORY_KEY_FUNCTION)); - if (session.getCache() != null) { - // both are expensive methods; cache it in session (repo -> context -> ID) - return (repository, context) -> ((ConcurrentMap>) - session.getCache() - .computeIfAbsent( - session, - EnhancedLocalRepositoryManagerFactory.class.getName() - + ".repositoryKeyFunction", - ConcurrentHashMap::new)) - .computeIfAbsent(repository, k1 -> new ConcurrentHashMap<>()) - .computeIfAbsent( - context == null ? "" : context, k2 -> repositoryKeyFunction.apply(repository, context)); - } else { - return repositoryKeyFunction; - } - } - private static final class LoggedMirror { private final Object[] keys; @@ -153,7 +121,11 @@ public List aggregateRepositories( return dominantRepositories; } - BiFunction repositoryKeyFunction = repositoryKeyFunction(session); + BiFunction repositoryKeyFunction = Utils.repositoryKeyFunction( + RemoteRepositoryManager.class, + session, + DEFAULT_REPOSITORY_KEY_FUNCTION, + CONFIG_PROP_REPOSITORY_KEY_FUNCTION); MirrorSelector mirrorSelector = session.getMirrorSelector(); AuthenticationSelector authSelector = session.getAuthenticationSelector(); ProxySelector proxySelector = session.getProxySelector(); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index 92f81ba0ca..b5b34c3e25 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -22,19 +22,13 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.BiFunction; - import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.NoLocalRepositoryManagerException; -import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryIdHelper; import static java.util.Objects.requireNonNull; @@ -86,34 +80,6 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan private final LocalPathPrefixComposerFactory localPathPrefixComposerFactory; - /** - * Method that based on configuration returns the "repository key function". Used by {@link EnhancedLocalRepositoryManagerFactory} - * and {@link LocalPathPrefixComposerFactory}. - * - * @since 2.0.14 - */ - @SuppressWarnings("unchecked") - public static BiFunction repositoryKeyFunction(RepositorySystemSession session) { - final RepositoryIdHelper.RepositoryKeyFunction repositoryKeyFunction = - RepositoryIdHelper.getRepositoryKeyFunction(ConfigUtils.getString( - session, DEFAULT_REPOSITORY_KEY_FUNCTION, CONFIG_PROP_REPOSITORY_KEY_FUNCTION)); - if (session.getCache() != null) { - // both are expensive methods; cache it in session (repo -> context -> ID) - return (repository, context) -> ((ConcurrentMap>) - session.getCache() - .computeIfAbsent( - session, - EnhancedLocalRepositoryManagerFactory.class.getName() - + ".repositoryKeyFunction", - ConcurrentHashMap::new)) - .computeIfAbsent(repository, k1 -> new ConcurrentHashMap<>()) - .computeIfAbsent( - context == null ? "" : context, k2 -> repositoryKeyFunction.apply(repository, context)); - } else { - return repositoryKeyFunction; - } - } - @Inject public EnhancedLocalRepositoryManagerFactory( final LocalPathComposer localPathComposer, @@ -142,7 +108,11 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local return new EnhancedLocalRepositoryManager( repository.getBasePath(), localPathComposer, - repositoryKeyFunction(session), + Utils.repositoryKeyFunction( + EnhancedLocalRepositoryManagerFactory.class, + session, + DEFAULT_REPOSITORY_KEY_FUNCTION, + CONFIG_PROP_REPOSITORY_KEY_FUNCTION), trackingFilename, trackingFileManager, localPathPrefixComposerFactory.createComposer(session)); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java index 7b5d6f2037..7a84a27640 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; @@ -33,7 +34,6 @@ import org.eclipse.aether.impl.OfflineController; import org.eclipse.aether.installation.InstallRequest; import org.eclipse.aether.metadata.Metadata; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ResolutionErrorPolicy; import org.eclipse.aether.resolution.ResolutionErrorPolicyRequest; @@ -42,8 +42,11 @@ import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator; import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory; import org.eclipse.aether.transfer.RepositoryOfflineException; +import org.eclipse.aether.util.ConfigUtils; import org.eclipse.aether.util.repository.RepositoryIdHelper; +import static java.util.Objects.requireNonNull; + /** * Internal utility methods. */ @@ -213,17 +216,33 @@ public static void checkOffline( } /** - * Shared and cached {@link RepositoryIdHelper#idToPathSegment(ArtifactRepository)} method, + * Method that based on configuration returns the "repository key function". The returned function will be session + * cached if session is equipped with cache, otherwise it will be non cached. Method never returns {@code null}. + * + * @since 2.0.14 */ @SuppressWarnings("unchecked") - public static String cachedIdToPathSegment(RepositorySystemSession session, ArtifactRepository artifactRepository) { + public static BiFunction repositoryKeyFunction( + Class owner, RepositorySystemSession session, String defaultValue, String configurationKey) { + requireNonNull(session); + requireNonNull(defaultValue); + requireNonNull(configurationKey); + final RepositoryIdHelper.RepositoryKeyFunction repositoryKeyFunction = + RepositoryIdHelper.getRepositoryKeyFunction( + ConfigUtils.getString(session, defaultValue, configurationKey)); if (session.getCache() != null) { - return ((ConcurrentMap) session.getCache() - .computeIfAbsent( - session, Utils.class.getName() + ".cachedIdToPathSegment", ConcurrentHashMap::new)) - .computeIfAbsent(artifactRepository, RepositoryIdHelper::idToPathSegment); + // both are expensive methods; cache it in session (repo -> context -> ID) + return (repository, context) -> ((ConcurrentMap>) + session.getCache() + .computeIfAbsent( + session, + owner.getName() + ".repositoryKeyFunction", + ConcurrentHashMap::new)) + .computeIfAbsent(repository, k1 -> new ConcurrentHashMap<>()) + .computeIfAbsent( + context == null ? "" : context, k2 -> repositoryKeyFunction.apply(repository, context)); } else { - return RepositoryIdHelper.idToPathSegment(artifactRepository); + return repositoryKeyFunction; } } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java index 5657a585b1..8850c05b4a 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java @@ -27,7 +27,9 @@ import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.repository.ArtifactRepository; +import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.checksums.TrustedChecksumsSource; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.util.DirectoryUtils; @@ -57,6 +59,21 @@ public abstract class FileTrustedChecksumsSourceSupport implements TrustedChecks protected static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_AETHER + "trustedChecksumsSource."; + /** + * Experimental: Configuration for "repository key" function. + * Note: repository key functions other than "nid" produce repository keys will be way different + * that those produced with previous versions or without this option enabled. Checksum source uses this key + * function to lay down and look up files to use in sources. + * + * @since 2.0.14 + * @configurationSource {@link RepositorySystemSession#getConfigProperties()} + * @configurationType {@link java.lang.String} + * @configurationDefaultValue {@link #DEFAULT_REPOSITORY_KEY_FUNCTION} + */ + public static final String CONFIG_PROP_REPOSITORY_KEY_FUNCTION = CONFIG_PROPS_PREFIX + "repositoryKeyFunction"; + + public static final String DEFAULT_REPOSITORY_KEY_FUNCTION = "nid"; + /** * This implementation will call into underlying code only if enabled, and will enforce non-{@code null} return * value. In worst case, empty map should be returned, meaning "no trusted checksums available". @@ -131,4 +148,22 @@ protected Path getBasedir( throw new UncheckedIOException(e); } } + + /** + * Returns repository key to be used on file system layout. + * + * @since 2.0.14 + */ + protected String repositoryKey(RepositorySystemSession session, ArtifactRepository artifactRepository) { + if (artifactRepository instanceof RemoteRepository) { + return Utils.repositoryKeyFunction( + FileTrustedChecksumsSourceSupport.class, + session, + DEFAULT_REPOSITORY_KEY_FUNCTION, + CONFIG_PROP_REPOSITORY_KEY_FUNCTION) + .apply((RemoteRepository) artifactRepository, null); + } else { + return artifactRepository.getId(); + } + } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java index 6162b6c951..27f981fb2c 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java @@ -34,7 +34,6 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.internal.impl.LocalPathComposer; -import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.io.ChecksumProcessor; @@ -132,10 +131,7 @@ protected Map doGetTrustedArtifactChecksums( if (Files.isDirectory(basedir)) { for (ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories) { Path checksumPath = basedir.resolve(calculateArtifactPath( - originAware, - artifact, - Utils.cachedIdToPathSegment(session, artifactRepository), - checksumAlgorithmFactory)); + originAware, artifact, repositoryKey(session, artifactRepository), checksumAlgorithmFactory)); if (!Files.isRegularFile(checksumPath)) { LOGGER.debug( @@ -167,7 +163,7 @@ protected Writer doGetTrustedArtifactChecksumsWriter(RepositorySystemSession ses return new SparseDirectoryWriter( getBasedir(session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, true), isOriginAware(session), - r -> Utils.cachedIdToPathSegment(session, r)); + r -> repositoryKey(session, r)); } private String calculateArtifactPath( diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java index b6827c5c95..952429fff7 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java @@ -43,7 +43,6 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.LocalPathComposer; -import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.io.PathProcessor; @@ -177,7 +176,7 @@ protected Map doGetTrustedArtifactChecksums( Path summaryFile = summaryFile( basedir, originAware, - Utils.cachedIdToPathSegment(session, artifactRepository), + repositoryKey(session, artifactRepository), checksumAlgorithmFactory.getFileExtension()); ConcurrentHashMap algorithmChecksums = checksums.computeIfAbsent(summaryFile, f -> loadProvidedChecksums(summaryFile)); @@ -199,7 +198,7 @@ protected Writer doGetTrustedArtifactChecksumsWriter(RepositorySystemSession ses checksums, getBasedir(session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, true), isOriginAware(session), - r -> Utils.cachedIdToPathSegment(session, r)); + r -> repositoryKey(session, r)); } /** @@ -263,17 +262,17 @@ private class SummaryFileWriter implements Writer { private final boolean originAware; - private final Function idToPathSegmentFunction; + private final Function repositoryKeyFunction; private SummaryFileWriter( ConcurrentHashMap> cache, Path basedir, boolean originAware, - Function idToPathSegmentFunction) { + Function repositoryKeyFunction) { this.cache = cache; this.basedir = basedir; this.originAware = originAware; - this.idToPathSegmentFunction = idToPathSegmentFunction; + this.repositoryKeyFunction = repositoryKeyFunction; } @Override @@ -287,7 +286,7 @@ public void addTrustedArtifactChecksums( Path summaryFile = summaryFile( basedir, originAware, - idToPathSegmentFunction.apply(artifactRepository), + repositoryKeyFunction.apply(artifactRepository), checksumAlgorithmFactory.getFileExtension()); String checksum = requireNonNull(trustedArtifactChecksums.get(checksumAlgorithmFactory.getName())); 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 dd46993319..bf58c793b3 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 @@ -249,7 +249,12 @@ private Path ruleFile(RepositorySystemSession session, RemoteRepository remoteRe return ruleFiles(session).computeIfAbsent(normalizeRemoteRepository(session, remoteRepository), r -> getBasedir( session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, false) .resolve(GROUP_ID_FILE_PREFIX - + Utils.cachedIdToPathSegment(session, remoteRepository) + + Utils.repositoryKeyFunction( + RemoteRepositoryFilterSourceSupport.class, + session, + DEFAULT_REPOSITORY_KEY_FUNCTION, + CONFIG_PROP_REPOSITORY_KEY_FUNCTION) + .apply(remoteRepository, null) + GROUP_ID_FILE_SUFFIX)); } 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 117e621c31..cdd6b19abd 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 @@ -318,8 +318,14 @@ private PrefixTree loadPrefixTree( private Path resolvePrefixesFromLocalConfiguration( RepositorySystemSession session, Path baseDir, RemoteRepository remoteRepository) { - Path filePath = baseDir.resolve( - PREFIXES_FILE_PREFIX + Utils.cachedIdToPathSegment(session, remoteRepository) + PREFIXES_FILE_SUFFIX); + Path filePath = baseDir.resolve(PREFIXES_FILE_PREFIX + + Utils.repositoryKeyFunction( + RemoteRepositoryFilterSourceSupport.class, + session, + DEFAULT_REPOSITORY_KEY_FUNCTION, + CONFIG_PROP_REPOSITORY_KEY_FUNCTION) + .apply(remoteRepository, null) + + PREFIXES_FILE_SUFFIX); if (Files.isReadable(filePath)) { return filePath; } else { 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 438c5eaa8d..b4050d089a 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 @@ -52,6 +52,21 @@ public abstract class RemoteRepositoryFilterSourceSupport implements RemoteRepos protected static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_AETHER + "remoteRepositoryFilter."; + /** + * Experimental: Configuration for "repository key" function. + * Note: repository key functions other than "nid" produce repository keys will be way different + * that those produced with previous versions or without this option enabled. Filter uses this key function to + * lay down and look up files to use in filtering. + * + * @since 2.0.14 + * @configurationSource {@link RepositorySystemSession#getConfigProperties()} + * @configurationType {@link java.lang.String} + * @configurationDefaultValue {@link #DEFAULT_REPOSITORY_KEY_FUNCTION} + */ + public static final String CONFIG_PROP_REPOSITORY_KEY_FUNCTION = CONFIG_PROPS_PREFIX + "repositoryKeyFunction"; + + public static final String DEFAULT_REPOSITORY_KEY_FUNCTION = "nid"; + /** * Returns {@code true} if session configuration contains this name set to {@code true}. *

    diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index f9f5e42ace..9f679e8331 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -30,8 +30,6 @@ import org.eclipse.aether.util.PathUtils; import org.eclipse.aether.util.StringDigestUtil; -import static java.util.Objects.requireNonNull; - /** * Helper class for {@link ArtifactRepository#getId()} handling. This class provides helper methods * to get id of repository as it was originally envisioned: as path safe, unique, etc. While POMs are validated by Maven, @@ -91,10 +89,10 @@ public interface RepositoryKeyFunction extends BiFunction false); - session.setCache(new DefaultRepositoryCache()); // session has cache set - Function safeId = RepositoryIdHelper::idToPathSegment; - - RemoteRepository good = new RemoteRepository.Builder("good", "default", "https://somewhere.com").build(); - RemoteRepository bad = new RemoteRepository.Builder("bad/id", "default", "https://somewhere.com").build(); - - String goodId = good.getId(); - String goodFixedId = safeId.apply(good); - assertEquals(goodId, goodFixedId); - - String badId = bad.getId(); - String badFixedId = safeId.apply(bad); - assertNotEquals(badId, badFixedId); - assertEquals("bad-SLASH-id", badFixedId); - } -} diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index aa4bc61bc2..e025a0ebf0 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -108,6 +108,7 @@ To modify this file, edit the template and regenerate. | `"aether.remoteRepositoryFilter.prefixes.skipped"` | `Boolean` | Configuration to skip the Prefixes filter for given request. This configuration is evaluated and if true the prefixes remote filter will not kick in. Main use case is by filter itself, to prevent recursion during discovery of remote prefixes file, but this also allows other components to control prefix filter discovery, while leaving configuration like #CONFIG_PROP_ENABLED still show the "real state". | `false` | 2.0.14 | Yes | Session Configuration | | `"aether.remoteRepositoryFilter.prefixes.useMirroredRepositories"` | `Boolean` | Configuration to allow Prefixes filter to auto-discover prefixes from mirrored repositories as well. For this to work Maven should be aware that given remote repository is mirror and is usually backed by MRM. Given multiple MRM implementations messes up prefixes file, is better to just skip these. In other case, one may use #CONFIG_PROP_ENABLED with repository ID suffix. | `false` | 2.0.14 | Yes | Session Configuration | | `"aether.remoteRepositoryFilter.prefixes.useRepositoryManagers"` | `Boolean` | Configuration to allow Prefixes filter to auto-discover prefixes from repository managers as well. For this to work Maven should be aware that given remote repository is backed by repository manager. Given multiple MRM implementations messes up prefixes file, is better to just skip these. In other case, one may use #CONFIG_PROP_ENABLED with repository ID suffix. Note: as of today, nothing sets this on remote repositories, but is added for future. | `false` | 2.0.14 | Yes | Session Configuration | +| `"aether.remoteRepositoryFilter.repositoryKeyFunction"` | `String` | Experimental: Configuration for "repository key" function. Note: repository key functions other than "nid" produce repository keys will be way different that those produced with previous versions or without this option enabled. Filter uses this key function to lay down and look up files to use in filtering. | `"nid"` | 2.0.14 | No | Session Configuration | | `"aether.remoteRepositoryManager.repositoryKeyFunction"` | `String` | Experimental: Configuration for "repository key" function. Note: repository key functions other than "nid" produce repository keys will be way different that those produced with previous versions or without this option enabled. Manager uses this key to detect "same" remote repositories, and in case of mirrors, to merge them. | `"nid"` | 2.0.14 | No | Session Configuration | | `"aether.snapshotFilter"` | `Boolean` | The key in the repository session's RepositorySystemSession#getConfigProperties() configurationproperties used to store a Boolean flag whether this filter should be forced to ban snapshots. By default, snapshots are only filtered if the root artifact is not a snapshot. | `false` | | No | Session Configuration | | `"aether.syncContext.named.basedir.locksDir"` | `String` | The location of the directory toi use for locks. If relative path, it is resolved from the local repository root. | `".locks"` | 1.9.0 | No | Session Configuration | @@ -159,6 +160,7 @@ To modify this file, edit the template and regenerate. | `"aether.transport.wagon.perms.dirMode"` | `String` | Octal numerical notation of permissions to set for newly created directories. Only considered by certain Wagon providers. | - | | Yes | Session Configuration | | `"aether.transport.wagon.perms.fileMode"` | `String` | Octal numerical notation of permissions to set for newly created files. Only considered by certain Wagon providers. | - | | Yes | Session Configuration | | `"aether.transport.wagon.perms.group"` | `String` | Group which should own newly created directories/files. Only considered by certain Wagon providers. | - | | Yes | Session Configuration | +| `"aether.trustedChecksumsSource.repositoryKeyFunction"` | `String` | Experimental: Configuration for "repository key" function. Note: repository key functions other than "nid" produce repository keys will be way different that those produced with previous versions or without this option enabled. Checksum source uses this key function to lay down and look up files to use in sources. | `"nid"` | 2.0.14 | No | Session Configuration | | `"aether.trustedChecksumsSource.sparseDirectory"` | `Boolean` | Is checksum source enabled? | `false` | 1.9.0 | No | Session Configuration | | `"aether.trustedChecksumsSource.sparseDirectory.basedir"` | `String` | The basedir where checksums are. If relative, is resolved from local repository root. | `".checksums"` | 1.9.0 | No | Session Configuration | | `"aether.trustedChecksumsSource.sparseDirectory.originAware"` | `Boolean` | Is source origin aware? | `true` | 1.9.0 | No | Session Configuration | From b665c112469f4887f53454e6ba1bb49b8d219f2c Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 27 Nov 2025 12:52:51 +0100 Subject: [PATCH 17/28] Reuse better --- .../SimpleLocalRepositoryManagerFactory.java | 27 ++++++------------- .../eclipse/aether/internal/impl/Utils.java | 7 +++-- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java index a5975138ab..9f19fe8ae0 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java @@ -22,15 +22,10 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.BiFunction; - import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.NoLocalRepositoryManagerException; -import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; import org.eclipse.aether.util.repository.RepositoryIdHelper; @@ -67,21 +62,15 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local requireNonNull(repository, "repository cannot be null"); if ("".equals(repository.getContentType()) || "simple".equals(repository.getContentType())) { - BiFunction repositoryKeyFunction = - RepositoryIdHelper::simpleRepositoryKey; - if (session.getCache() != null) { - repositoryKeyFunction = (r, c) -> ((ConcurrentMap>) - session.getCache() - .computeIfAbsent( - session, - EnhancedLocalRepositoryManagerFactory.class.getName() - + ".repositoryKeyFunction", - ConcurrentHashMap::new)) - .computeIfAbsent(r, k1 -> new ConcurrentHashMap<>()) - .computeIfAbsent(c == null ? "" : c, k2 -> RepositoryIdHelper.simpleRepositoryKey(r, c)); - } return new SimpleLocalRepositoryManager( - repository.getBasePath(), "simple", localPathComposer, repositoryKeyFunction); + repository.getBasePath(), + "simple", + localPathComposer, + Utils.repositoryKeyFunction( + SimpleLocalRepositoryManagerFactory.class, + session, + RepositoryIdHelper.RepositoryKeyType.SIMPLE.name(), + null)); } else { throw new NoLocalRepositoryManagerException(repository); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java index 7a84a27640..482e40606d 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java @@ -218,6 +218,8 @@ public static void checkOffline( /** * Method that based on configuration returns the "repository key function". The returned function will be session * cached if session is equipped with cache, otherwise it will be non cached. Method never returns {@code null}. + * Only the {@code configurationKey} parameter may be {@code null} in which case no configuration lookup happens + * but the {@code defaultValue} is directly used instead. * * @since 2.0.14 */ @@ -226,10 +228,11 @@ public static BiFunction repositoryKeyFunction Class owner, RepositorySystemSession session, String defaultValue, String configurationKey) { requireNonNull(session); requireNonNull(defaultValue); - requireNonNull(configurationKey); final RepositoryIdHelper.RepositoryKeyFunction repositoryKeyFunction = RepositoryIdHelper.getRepositoryKeyFunction( - ConfigUtils.getString(session, defaultValue, configurationKey)); + configurationKey != null + ? ConfigUtils.getString(session, defaultValue, configurationKey) + : defaultValue); if (session.getCache() != null) { // both are expensive methods; cache it in session (repo -> context -> ID) return (repository, context) -> ((ConcurrentMap>) From 0b7f135d43259c5d27a16647abb1204deb11a930 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 27 Nov 2025 13:00:30 +0100 Subject: [PATCH 18/28] Lessen repetition --- .../GroupIdRemoteRepositoryFilterSource.java | 10 +--------- .../PrefixesRemoteRepositoryFilterSource.java | 11 ++--------- .../RemoteRepositoryFilterSourceSupport.java | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 18 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 bf58c793b3..ac6fc3f433 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 @@ -42,7 +42,6 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.RepositorySystemLifecycle; -import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.internal.impl.filter.ruletree.GroupTree; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.repository.RemoteRepository; @@ -248,14 +247,7 @@ public void postProcess(RepositorySystemSession session, List ar private Path ruleFile(RepositorySystemSession session, RemoteRepository remoteRepository) { return ruleFiles(session).computeIfAbsent(normalizeRemoteRepository(session, remoteRepository), r -> getBasedir( session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, false) - .resolve(GROUP_ID_FILE_PREFIX - + Utils.repositoryKeyFunction( - RemoteRepositoryFilterSourceSupport.class, - session, - DEFAULT_REPOSITORY_KEY_FUNCTION, - CONFIG_PROP_REPOSITORY_KEY_FUNCTION) - .apply(remoteRepository, null) - + GROUP_ID_FILE_SUFFIX)); + .resolve(GROUP_ID_FILE_PREFIX + repositoryKey(session, remoteRepository) + GROUP_ID_FILE_SUFFIX)); } private GroupTree cacheRules(RepositorySystemSession session, RemoteRepository remoteRepository) { 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 cdd6b19abd..bd383f6eb2 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 @@ -36,7 +36,6 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.MetadataResolver; import org.eclipse.aether.impl.RemoteRepositoryManager; -import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.internal.impl.filter.prefixes.PrefixesSource; import org.eclipse.aether.internal.impl.filter.ruletree.PrefixTree; import org.eclipse.aether.metadata.DefaultMetadata; @@ -318,14 +317,8 @@ private PrefixTree loadPrefixTree( private Path resolvePrefixesFromLocalConfiguration( RepositorySystemSession session, Path baseDir, RemoteRepository remoteRepository) { - Path filePath = baseDir.resolve(PREFIXES_FILE_PREFIX - + Utils.repositoryKeyFunction( - RemoteRepositoryFilterSourceSupport.class, - session, - DEFAULT_REPOSITORY_KEY_FUNCTION, - CONFIG_PROP_REPOSITORY_KEY_FUNCTION) - .apply(remoteRepository, null) - + PREFIXES_FILE_SUFFIX); + Path filePath = + baseDir.resolve(PREFIXES_FILE_PREFIX + repositoryKey(session, remoteRepository) + PREFIXES_FILE_SUFFIX); if (Files.isReadable(filePath)) { return filePath; } else { 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 b4050d089a..1482de6d84 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 @@ -24,6 +24,8 @@ import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.internal.impl.Utils; +import org.eclipse.aether.internal.impl.checksum.FileTrustedChecksumsSourceSupport; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter; import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource; @@ -103,6 +105,20 @@ protected RemoteRepository normalizeRemoteRepository( return remoteRepository.toBareRemoteRepository(); } + /** + * Returns repository key to be used on file system layout. + * + * @since 2.0.14 + */ + protected String repositoryKey(RepositorySystemSession session, RemoteRepository repository) { + return Utils.repositoryKeyFunction( + FileTrustedChecksumsSourceSupport.class, + session, + DEFAULT_REPOSITORY_KEY_FUNCTION, + CONFIG_PROP_REPOSITORY_KEY_FUNCTION) + .apply(repository, null); + } + /** * Simple {@link RemoteRepositoryFilter.Result} immutable implementation. */ From e09b3001f5928a898c15c42c99d4e9632ae5ce4a Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 27 Nov 2025 17:52:29 +0100 Subject: [PATCH 19/28] Unify where needed Also provide page --- .../aether/ConfigurationProperties.java | 31 +++-- ...DefaultLocalPathPrefixComposerFactory.java | 6 +- .../impl/DefaultRemoteRepositoryManager.java | 24 +--- ...EnhancedLocalRepositoryManagerFactory.java | 21 +--- .../eclipse/aether/internal/impl/Utils.java | 16 +++ src/site/markdown/configuration.md | 3 +- src/site/markdown/repository-key-function.md | 114 ++++++++++++++++++ src/site/site.xml | 1 + 8 files changed, 159 insertions(+), 57 deletions(-) create mode 100644 src/site/markdown/repository-key-function.md diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java b/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java index 327815fdbe..3945a74869 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/ConfigurationProperties.java @@ -149,6 +149,13 @@ public final class ConfigurationProperties { */ public static final String CACHED_PRIORITIES = PREFIX_PRIORITY + "cached"; + /** + * The default caching of priority components if {@link #CACHED_PRIORITIES} isn't set. Default value is {@code true}. + * + * @since 2.0.0 + */ + public static final boolean DEFAULT_CACHED_PRIORITIES = true; + /** * The priority to use for a certain extension class. {@code <class>} can either be the fully qualified * name or the simple name of a class. If the class name ends with Factory that suffix could optionally be left out. @@ -171,13 +178,6 @@ public final class ConfigurationProperties { */ public static final String CLASS_PRIORITIES = PREFIX_PRIORITY + ""; - /** - * The default caching of priority components if {@link #CACHED_PRIORITIES} isn't set. Default value is {@code true}. - * - * @since 2.0.0 - */ - public static final boolean DEFAULT_CACHED_PRIORITIES = true; - /** * A flag indicating whether interaction with the user is allowed. * @@ -560,6 +560,23 @@ public final class ConfigurationProperties { public static final String DEFAULT_REPOSITORY_SYSTEM_DEPENDENCY_VISITOR = REPOSITORY_SYSTEM_DEPENDENCY_VISITOR_LEVELORDER; + /** + * Experimental: Configuration for system-wide "repository key" function. + * Accepted and recommended values: "nid" (default), "nid_hurl" and "ngurk", while "simple" is Maven 3 legacy, + * technically equivalent to "nid". For complete description see enum + * {@code org.eclipse.aether.util.repository.RepositoryIdHelper.RepositoryKeyType} in utils. Warning: + * repository key function affects Resolver fundamentally and may have unexpected results! Only change this + * if you know what you are doing! + * + * @since 2.0.14 + * @configurationSource {@link RepositorySystemSession#getConfigProperties()} + * @configurationType {@link java.lang.String} + * @configurationDefaultValue {@link #DEFAULT_REPOSITORY_SYSTEM_REPOSITORY_KEY_FUNCTION} + */ + public static final String REPOSITORY_SYSTEM_REPOSITORY_KEY_FUNCTION = PREFIX_SYSTEM + "repositoryKeyFunction"; + + public static final String DEFAULT_REPOSITORY_SYSTEM_REPOSITORY_KEY_FUNCTION = "nid"; + /** * A flag indicating whether version scheme cache statistics should be printed on JVM shutdown. * This is useful for analyzing cache performance and effectiveness in development and testing scenarios. diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java index 6f7700ec95..f59fe55777 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java @@ -47,11 +47,7 @@ public LocalPathPrefixComposer createComposer(RepositorySystemSession session) { isSplitRemoteRepositoryLast(session), getReleasesPrefix(session), getSnapshotsPrefix(session), - Utils.repositoryKeyFunction( - EnhancedLocalRepositoryManagerFactory.class, - session, - EnhancedLocalRepositoryManagerFactory.DEFAULT_REPOSITORY_KEY_FUNCTION, - EnhancedLocalRepositoryManagerFactory.CONFIG_PROP_REPOSITORY_KEY_FUNCTION)); + Utils.systemRepositoryKeyFunction(session)); } /** 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 d241efda52..12afe9824f 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 @@ -29,7 +29,6 @@ import java.util.function.BiFunction; import java.util.stream.Collectors; -import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositoryCache; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.impl.RemoteRepositoryManager; @@ -52,23 +51,6 @@ @Singleton @Named public class DefaultRemoteRepositoryManager implements RemoteRepositoryManager { - private static final String CONFIG_PROPS_PREFIX = - ConfigurationProperties.PREFIX_AETHER + "remoteRepositoryManager."; - - /** - * Experimental: Configuration for "repository key" function. - * Note: repository key functions other than "nid" produce repository keys will be way different - * that those produced with previous versions or without this option enabled. Manager uses this key to - * detect "same" remote repositories, and in case of mirrors, to merge them. - * - * @since 2.0.14 - * @configurationSource {@link RepositorySystemSession#getConfigProperties()} - * @configurationType {@link java.lang.String} - * @configurationDefaultValue {@link #DEFAULT_REPOSITORY_KEY_FUNCTION} - */ - public static final String CONFIG_PROP_REPOSITORY_KEY_FUNCTION = CONFIG_PROPS_PREFIX + "repositoryKeyFunction"; - - public static final String DEFAULT_REPOSITORY_KEY_FUNCTION = "nid"; private static final class LoggedMirror { @@ -121,11 +103,7 @@ public List aggregateRepositories( return dominantRepositories; } - BiFunction repositoryKeyFunction = Utils.repositoryKeyFunction( - RemoteRepositoryManager.class, - session, - DEFAULT_REPOSITORY_KEY_FUNCTION, - CONFIG_PROP_REPOSITORY_KEY_FUNCTION); + BiFunction repositoryKeyFunction = Utils.systemRepositoryKeyFunction(session); MirrorSelector mirrorSelector = session.getMirrorSelector(); AuthenticationSelector authSelector = session.getAuthenticationSelector(); ProxySelector proxySelector = session.getProxySelector(); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index b5b34c3e25..e0f1203829 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -57,21 +57,6 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan public static final String DEFAULT_TRACKING_FILENAME = "_remote.repositories"; - /** - * Experimental: Configuration for "repository key" function. - * Note: repository key functions other than "simple" produce repository keys will be way different - * that those produced with previous versions or without this option enabled. Ideally, you may want to - * use empty local repository to populate with new repository key contained metadata. - * - * @since 2.0.14 - * @configurationSource {@link RepositorySystemSession#getConfigProperties()} - * @configurationType {@link java.lang.String} - * @configurationDefaultValue {@link #DEFAULT_REPOSITORY_KEY_FUNCTION} - */ - public static final String CONFIG_PROP_REPOSITORY_KEY_FUNCTION = CONFIG_PROPS_PREFIX + "repositoryKeyFunction"; - - public static final String DEFAULT_REPOSITORY_KEY_FUNCTION = "simple"; - private float priority = 10.0f; private final LocalPathComposer localPathComposer; @@ -108,11 +93,7 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local return new EnhancedLocalRepositoryManager( repository.getBasePath(), localPathComposer, - Utils.repositoryKeyFunction( - EnhancedLocalRepositoryManagerFactory.class, - session, - DEFAULT_REPOSITORY_KEY_FUNCTION, - CONFIG_PROP_REPOSITORY_KEY_FUNCTION), + Utils.systemRepositoryKeyFunction(session), trackingFilename, trackingFileManager, localPathPrefixComposerFactory.createComposer(session)); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java index 482e40606d..a816ececd0 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java @@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.BiFunction; +import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.deployment.DeployRequest; @@ -215,6 +216,21 @@ public static void checkOffline( } } + /** + * Returns system-wide repository key function. + * + * @since 2.0.14 + * @see #repositoryKeyFunction(Class, RepositorySystemSession, String, String) + */ + public static BiFunction systemRepositoryKeyFunction( + RepositorySystemSession session) { + return repositoryKeyFunction( + Utils.class, + session, + ConfigurationProperties.DEFAULT_REPOSITORY_SYSTEM_REPOSITORY_KEY_FUNCTION, + ConfigurationProperties.REPOSITORY_SYSTEM_REPOSITORY_KEY_FUNCTION); + } + /** * Method that based on configuration returns the "repository key function". The returned function will be session * cached if session is equipped with cache, otherwise it will be non cached. Method never returns {@code null}. diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index e025a0ebf0..295203c09e 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -74,7 +74,6 @@ To modify this file, edit the template and regenerate. | `"aether.lrm.enhanced.localPrefix"` | `String` | The prefix to use for locally installed artifacts. | `"installed"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.releasesPrefix"` | `String` | The prefix to use for release artifacts. | `"releases"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.remotePrefix"` | `String` | The prefix to use for remotely cached artifacts. | `"cached"` | 1.8.1 | No | Session Configuration | -| `"aether.lrm.enhanced.repositoryKeyFunction"` | `String` | Experimental: Configuration for "repository key" function. Note: repository key functions other than "simple" produce repository keys will be way different that those produced with previous versions or without this option enabled. Ideally, you may want to use empty local repository to populate with new repository key contained metadata. | `"simple"` | 2.0.14 | No | Session Configuration | | `"aether.lrm.enhanced.snapshotsPrefix"` | `String` | The prefix to use for snapshot artifacts. | `"snapshots"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.split"` | `Boolean` | Whether LRM should split local and remote artifacts. | `false` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.splitLocal"` | `Boolean` | Whether locally installed artifacts should be split by version (release/snapshot). | `false` | 1.8.1 | No | Session Configuration | @@ -109,7 +108,6 @@ To modify this file, edit the template and regenerate. | `"aether.remoteRepositoryFilter.prefixes.useMirroredRepositories"` | `Boolean` | Configuration to allow Prefixes filter to auto-discover prefixes from mirrored repositories as well. For this to work Maven should be aware that given remote repository is mirror and is usually backed by MRM. Given multiple MRM implementations messes up prefixes file, is better to just skip these. In other case, one may use #CONFIG_PROP_ENABLED with repository ID suffix. | `false` | 2.0.14 | Yes | Session Configuration | | `"aether.remoteRepositoryFilter.prefixes.useRepositoryManagers"` | `Boolean` | Configuration to allow Prefixes filter to auto-discover prefixes from repository managers as well. For this to work Maven should be aware that given remote repository is backed by repository manager. Given multiple MRM implementations messes up prefixes file, is better to just skip these. In other case, one may use #CONFIG_PROP_ENABLED with repository ID suffix. Note: as of today, nothing sets this on remote repositories, but is added for future. | `false` | 2.0.14 | Yes | Session Configuration | | `"aether.remoteRepositoryFilter.repositoryKeyFunction"` | `String` | Experimental: Configuration for "repository key" function. Note: repository key functions other than "nid" produce repository keys will be way different that those produced with previous versions or without this option enabled. Filter uses this key function to lay down and look up files to use in filtering. | `"nid"` | 2.0.14 | No | Session Configuration | -| `"aether.remoteRepositoryManager.repositoryKeyFunction"` | `String` | Experimental: Configuration for "repository key" function. Note: repository key functions other than "nid" produce repository keys will be way different that those produced with previous versions or without this option enabled. Manager uses this key to detect "same" remote repositories, and in case of mirrors, to merge them. | `"nid"` | 2.0.14 | No | Session Configuration | | `"aether.snapshotFilter"` | `Boolean` | The key in the repository session's RepositorySystemSession#getConfigProperties() configurationproperties used to store a Boolean flag whether this filter should be forced to ban snapshots. By default, snapshots are only filtered if the root artifact is not a snapshot. | `false` | | No | Session Configuration | | `"aether.syncContext.named.basedir.locksDir"` | `String` | The location of the directory toi use for locks. If relative path, it is resolved from the local repository root. | `".locks"` | 1.9.0 | No | Session Configuration | | `"aether.syncContext.named.discriminating.discriminator"` | `String` | Configuration property to pass in discriminator, if needed. If not present, it is auto-calculated. | - | 1.7.0 | No | Session Configuration | @@ -124,6 +122,7 @@ To modify this file, edit the template and regenerate. | `"aether.syncContext.named.time"` | `Long` | The maximum of time amount to be blocked to obtain lock. | `900l` | 1.7.0 | No | Session Configuration | | `"aether.syncContext.named.time.unit"` | `String` | The unit of maximum time amount to be blocked to obtain lock. Use TimeUnit enum names. | `"SECONDS"` | 1.7.0 | No | Session Configuration | | `"aether.system.dependencyVisitor"` | `String` | A flag indicating which visitor should be used to "flatten" the dependency graph into list. In Maven 4 the default is new "levelOrder", while Maven 3 used "preOrder". This property accepts values "preOrder", "postOrder" and "levelOrder". | `"levelOrder"` | 2.0.0 | No | Session Configuration | +| `"aether.system.repositoryKeyFunction"` | `String` | Experimental: Configuration for system-wide "repository key" function. Accepted and recommended values: "nid" (default), "nid_hurl" and "ngurk", while "simple" is Maven 3 legacy, technically equivalent to "nid". For complete description see enum org.eclipse.aether.util.repository.RepositoryIdHelper.RepositoryKeyType in utils. Warning: repository key function affects Resolver fundamentally and may have unexpected results! Only change this if you know what you are doing! | `"nid"` | 2.0.14 | No | Session Configuration | | `"aether.transport.apache.followRedirects"` | `Boolean` | If enabled, Apache HttpClient will follow HTTP redirects. | `true` | 2.0.2 | Yes | Session Configuration | | `"aether.transport.apache.https.cipherSuites"` | `String` | Comma-separated list of Cipher Suites which are enabled for HTTPS connections. | - | 2.0.0 | No | Session Configuration | | `"aether.transport.apache.https.protocols"` | `String` | Comma-separated list of Protocols which are enabled for HTTPS connections. | - | 2.0.0 | No | Session Configuration | diff --git a/src/site/markdown/repository-key-function.md b/src/site/markdown/repository-key-function.md new file mode 100644 index 0000000000..80f6e11774 --- /dev/null +++ b/src/site/markdown/repository-key-function.md @@ -0,0 +1,114 @@ +# Repository Key Function + + +One long outstanding issue in Maven (across all versions) was how to identify +remote repositories (this problem mostly tackles them, as local, workspace +and other repositories are usually "singletons" and have fixed IDs). + +Existing Maven versions mostly limited themselves to `RemoteRepository#getId()` +method to "key" repositories, but this strategy many times proves suboptimal. + +Known issues that Maven users cannot fight against: +* different IDs for same URLs, examples (from Central) are `apache-snapshots` (plural), `apache-snapshot` (singular) + or `apache.snapshot` (dot vs dash) defined repositories, that all point to same ASF snapshot repository. +* same IDs for different URLs (two totally disconnected project may define repository `project-releases` in their POM, + while in fact those two repositories are not related at all) +* repository IDs that are [not file-friendly](https://github.com/apache/maven-resolver/issues/1564). This should not + be possible, as Maven validates and does not allow these characters in ID field, but in some cases + (ancient or generated POMs) this may happen. + +Remote repositories that user cannot "fix" usually enter the build via those POMs that are not authored by users +themselves, so project POM and parent POMs can be safely excluded. In turn, these may come from POMs that are +being pulled in as third-party plugin or dependency POMs. + +For users wanting to fully control used repository Maven 3.9.x line added the `-itr`/`--ignore-transitive-repositories` +CLI option, but while this 100% solves the problem, it does it by fully delegating the work onto user, to define +all the needed remote repositories (for dependencies but also for plugins) in project POM. In certain cases this +option is the recommended way, but many times it proves too burdensome. + +Hence, Maven Resolver 2.x introduces notion of "repository key function", which is a function that creates +Remote Repository "key", with following properties: +* is configurable (see below) +* produced keys are "file system friendly" + +Latest Resolver uses repository key at these places (and these must be aligned; use same function): +* `EnhancedLocalRepositoryManager`, the default LRM, where artifact availability is being calculated +* `LocalPathPrefixComposer`, in case of "split local repository" to calculate prefix/suffix elements based on artifact originating repository (if enabled) +* `RemoteRepositoryManager` that consolidates existing and newly discovered repositories (by eliminating them or merging mirrors, as needed) + +In these cases, the repository key function affects how Resolver (and hence, Maven) works _fundamentally_, what +`RemoteRepository` it considers "same" or "different". Which artifacts are considered as coming from "same origin" +or "different origin" (i.e. split local repository). + +Furthermore, repository key function (possibly different one) is used in two components to map remote repository configuration to file paths: +* Trusted Checksums Source +* Remote Repository Filter + +In these cases, the repository key function only role is to provide "file system friendly" path segments based on +`RemoteRepository` instances. + +## Implemented Repository Key Functions + +The function is configurable, while the default function remains Maven 3.x compatible. The existing functions are: +* `simple` (Maven 3 default; technically equivalent to `nid`) +* `nid` (default) +* `nid_hurl` +* `ngurk` + +These below are recommended only for some special cases: +* `hurl` +* `gurk` + +## Recommended New Repository Key Functions + +### `nid` + +This key still relies solely on `RemoteRepository#getId()` but applies transformation to returned value to make it +"file system path segment friendly". Is usable in the simplest use cases, and behaves as Maven 3 did. +Technically is equivalent to legacy `simple` repository key function. + +### `nid_hurl` + +This key relies on `RemoteRepository#getId()` and `RemoteRepository#getUrl()`, and forms a key based on these two. +This means if you have same-ID repository pointing to two different URLs, they will be considered different. Still, +on disk the produced key string is user-friendly, as ID remains readable. + +### `ngurk` + +This key relies on **all properties** (details below) of `RemoteRepository`, but is "normalized" in a way that only the +fact that a `RemoteRepository` is a mirror (or not) is recorded, while the list of the mirrored repositories does not +affect key production. This also means that if you have two "similar" `RemoteRepository`, with same ID, same URL, but +one has snapshots enabled, the other snapshots disabled, they will be considered different. + +This function leaves out following `RemoteRepository` properties: `Authorization`, `Proxy`, `Intent`, `Mirrors` +(but checks is list empty or not) and update policies for releases and snapshots. + +## Special Repository Key Functions + +These functions are **not recommended for everyday use**, but may prove useful in some cases. + +### `hurl` + +This key relies solely on `RemoteRepository#getUrl()`. +This means that repository URL becomes what repository ID was for equality check. Note: this function does not perform +any kind of URL "normalization", URL is used as-is. + +### `gurk` + +Similar to `ngurk` but does not normalize mirrors. As a consequence, and due dynamism of mirrors, the key of same +remote repository (for example `external:*`) **may change during the build**. diff --git a/src/site/site.xml b/src/site/site.xml index 91e5e983a0..677ec4ff1c 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -32,6 +32,7 @@ under the License. +

    From 38dd75bfce7da0c537c813dce3e94c9feecdcf4f Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 27 Nov 2025 18:00:33 +0100 Subject: [PATCH 20/28] Typos, tidy up --- src/site/markdown/repository-key-function.md | 26 +++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/site/markdown/repository-key-function.md b/src/site/markdown/repository-key-function.md index 80f6e11774..5405441d04 100644 --- a/src/site/markdown/repository-key-function.md +++ b/src/site/markdown/repository-key-function.md @@ -28,25 +28,30 @@ Known issues that Maven users cannot fight against: or `apache.snapshot` (dot vs dash) defined repositories, that all point to same ASF snapshot repository. * same IDs for different URLs (two totally disconnected project may define repository `project-releases` in their POM, while in fact those two repositories are not related at all) -* repository IDs that are [not file-friendly](https://github.com/apache/maven-resolver/issues/1564). This should not - be possible, as Maven validates and does not allow these characters in ID field, but in some cases +* repository IDs that are [not file-friendly](https://github.com/apache/maven-resolver/issues/1564). Usually this should + be impossible, as Maven validates and forbids these characters in ID field, but in some cases (ancient or generated POMs) this may happen. -Remote repositories that user cannot "fix" usually enter the build via those POMs that are not authored by users +Remote repositories that user cannot "fix", usually enter the build via those POMs that are not authored by user themselves, so project POM and parent POMs can be safely excluded. In turn, these may come from POMs that are being pulled in as third-party plugin or dependency POMs. -For users wanting to fully control used repository Maven 3.9.x line added the `-itr`/`--ignore-transitive-repositories` -CLI option, but while this 100% solves the problem, it does it by fully delegating the work onto user, to define -all the needed remote repositories (for dependencies but also for plugins) in project POM. In certain cases this -option is the recommended way, but many times it proves too burdensome. +While we don't find the first issue deal-breaker (and we did not provide yet a function for fixing it), the latter two +may produce various problems with local repository, split local repository and so on, causing a total mix-up of expected +layout, or even wrongly grouped artifacts. + +For those eager to fully control used repositories, Maven 3.9.x line added the `-itr`/`--ignore-transitive-repositories` +CLI option, but while this solves the problem, it does it by fully delegating the work onto the user itself, to define +all the needed remote repositories (for dependencies but also for plugins) in project POM that build requires. +In certain cases this option is the recommended way, but many times it proves too burdensome. Hence, Maven Resolver 2.x introduces notion of "repository key function", which is a function that creates Remote Repository "key", with following properties: +* can be used to identify a `RemoteRepository` * is configurable (see below) -* produced keys are "file system friendly" +* produced keys are "file system friendly" as well -Latest Resolver uses repository key at these places (and these must be aligned; use same function): +Latest Resolver uses repository key at these places (and these must be aligned; must use same function): * `EnhancedLocalRepositoryManager`, the default LRM, where artifact availability is being calculated * `LocalPathPrefixComposer`, in case of "split local repository" to calculate prefix/suffix elements based on artifact originating repository (if enabled) * `RemoteRepositoryManager` that consolidates existing and newly discovered repositories (by eliminating them or merging mirrors, as needed) @@ -62,6 +67,9 @@ Furthermore, repository key function (possibly different one) is used in two com In these cases, the repository key function only role is to provide "file system friendly" path segments based on `RemoteRepository` instances. +**Important implication:** When Resolver/Maven is reconfigured to use alternative repository key function, it is +worthwhile to start with new, empty local repository (as keys are used in LRM maintained metadata). + ## Implemented Repository Key Functions The function is configurable, while the default function remains Maven 3.x compatible. The existing functions are: From 621b80f9b4f26d71446059d4d9cd72a9f55b248a Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 27 Nov 2025 18:11:21 +0100 Subject: [PATCH 21/28] More --- src/site/markdown/repository-key-function.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/site/markdown/repository-key-function.md b/src/site/markdown/repository-key-function.md index 5405441d04..d4b08b7522 100644 --- a/src/site/markdown/repository-key-function.md +++ b/src/site/markdown/repository-key-function.md @@ -114,7 +114,8 @@ These functions are **not recommended for everyday use**, but may prove useful i This key relies solely on `RemoteRepository#getUrl()`. This means that repository URL becomes what repository ID was for equality check. Note: this function does not perform -any kind of URL "normalization", URL is used as-is. +any kind of URL "normalization", URL is used as-is. The problem with this function is that it will produce +"human unfriendly" repository key that is fully disconnected and hard to trace back to origin repository. ### `gurk` From 867fe05a4bbd16e498b98c59753e2bbbfd990492 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 27 Nov 2025 18:36:08 +0100 Subject: [PATCH 22/28] Reorder --- src/site/markdown/repository-key-function.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/site/markdown/repository-key-function.md b/src/site/markdown/repository-key-function.md index d4b08b7522..250df5e286 100644 --- a/src/site/markdown/repository-key-function.md +++ b/src/site/markdown/repository-key-function.md @@ -47,9 +47,9 @@ In certain cases this option is the recommended way, but many times it proves to Hence, Maven Resolver 2.x introduces notion of "repository key function", which is a function that creates Remote Repository "key", with following properties: -* can be used to identify a `RemoteRepository` -* is configurable (see below) +* is derived from and can be used to identify `RemoteRepository` * produced keys are "file system friendly" as well +* is configurable (see below) Latest Resolver uses repository key at these places (and these must be aligned; must use same function): * `EnhancedLocalRepositoryManager`, the default LRM, where artifact availability is being calculated From 889486bcadc200ae18be7d780057bfae2c6759ff Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 28 Nov 2025 16:14:37 +0100 Subject: [PATCH 23/28] Pull out RepositoryKeyInterface --- ...DefaultLocalPathPrefixComposerFactory.java | 6 +-- .../impl/DefaultRemoteRepositoryManager.java | 6 +-- .../impl/EnhancedLocalRepositoryManager.java | 4 +- ...LocalPathPrefixComposerFactorySupport.java | 7 ++-- .../impl/SimpleLocalRepositoryManager.java | 6 +-- .../eclipse/aether/internal/impl/Utils.java | 16 ++++--- .../util/repository/RepositoryIdHelper.java | 10 ----- .../repository/RepositoryKeyFunction.java | 42 +++++++++++++++++++ 8 files changed, 62 insertions(+), 35 deletions(-) create mode 100644 maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryKeyFunction.java diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java index f59fe55777..49d3963844 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java @@ -21,10 +21,8 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.util.function.BiFunction; - import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.util.repository.RepositoryKeyFunction; /** * Default local path prefix composer factory: it fully reuses {@link LocalPathPrefixComposerFactorySupport} class @@ -65,7 +63,7 @@ private DefaultLocalPathPrefixComposer( boolean splitRemoteRepositoryLast, String releasesPrefix, String snapshotsPrefix, - BiFunction repositoryKeyFunction) { + RepositoryKeyFunction repositoryKeyFunction) { super( split, localPrefix, 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 12afe9824f..00168ab3ce 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,7 +26,6 @@ import java.util.Arrays; import java.util.List; import java.util.ListIterator; -import java.util.function.BiFunction; import java.util.stream.Collectors; import org.eclipse.aether.RepositoryCache; @@ -41,6 +40,7 @@ import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider; +import org.eclipse.aether.util.repository.RepositoryKeyFunction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -103,7 +103,7 @@ public List aggregateRepositories( return dominantRepositories; } - BiFunction repositoryKeyFunction = Utils.systemRepositoryKeyFunction(session); + RepositoryKeyFunction repositoryKeyFunction = Utils.systemRepositoryKeyFunction(session); MirrorSelector mirrorSelector = session.getMirrorSelector(); AuthenticationSelector authSelector = session.getAuthenticationSelector(); ProxySelector proxySelector = session.getProxySelector(); @@ -193,7 +193,7 @@ private void logMirror(RepositorySystemSession session, RemoteRepository origina private RemoteRepository mergeMirrors( RepositorySystemSession session, - BiFunction repositoryKeyFunction, + RepositoryKeyFunction repositoryKeyFunction, RemoteRepository dominant, RemoteRepository recessive) { RemoteRepository.Builder merged = null; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java index c1b80999fd..8e82047ef6 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java @@ -27,7 +27,6 @@ import java.util.Map; import java.util.Objects; import java.util.Properties; -import java.util.function.BiFunction; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; @@ -36,6 +35,7 @@ import org.eclipse.aether.repository.LocalArtifactRequest; import org.eclipse.aether.repository.LocalArtifactResult; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.util.repository.RepositoryKeyFunction; import static java.util.Objects.requireNonNull; @@ -71,7 +71,7 @@ class EnhancedLocalRepositoryManager extends SimpleLocalRepositoryManager { EnhancedLocalRepositoryManager( Path basedir, LocalPathComposer localPathComposer, - BiFunction repositoryKeyFunction, + RepositoryKeyFunction repositoryKeyFunction, String trackingFilename, TrackingFileManager trackingFileManager, LocalPathPrefixComposer localPathPrefixComposer) { diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java index f26c329aab..6e06f7907c 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java @@ -18,13 +18,12 @@ */ package org.eclipse.aether.internal.impl; -import java.util.function.BiFunction; - import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.util.ConfigUtils; +import org.eclipse.aether.util.repository.RepositoryKeyFunction; /** * Support class for {@link LocalPathPrefixComposerFactory} implementations: it predefines and makes re-usable @@ -243,7 +242,7 @@ protected abstract static class LocalPathPrefixComposerSupport implements LocalP protected final String snapshotsPrefix; - protected final BiFunction repositoryKeyFunction; + protected final RepositoryKeyFunction repositoryKeyFunction; protected LocalPathPrefixComposerSupport( boolean split, @@ -255,7 +254,7 @@ protected LocalPathPrefixComposerSupport( boolean splitRemoteRepositoryLast, String releasesPrefix, String snapshotsPrefix, - BiFunction repositoryKeyFunction) { + RepositoryKeyFunction repositoryKeyFunction) { this.split = split; this.localPrefix = localPrefix; this.splitLocal = splitLocal; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java index f1da03db90..f2106d5fbb 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java @@ -21,7 +21,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; -import java.util.function.BiFunction; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; @@ -35,6 +34,7 @@ import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.util.repository.RepositoryKeyFunction; import static java.util.Objects.requireNonNull; @@ -47,13 +47,13 @@ class SimpleLocalRepositoryManager implements LocalRepositoryManager { private final LocalPathComposer localPathComposer; - private final BiFunction repositoryKeyFunction; + private final RepositoryKeyFunction repositoryKeyFunction; SimpleLocalRepositoryManager( Path basePath, String type, LocalPathComposer localPathComposer, - BiFunction repositoryKeyFunction) { + RepositoryKeyFunction repositoryKeyFunction) { requireNonNull(basePath, "base directory cannot be null"); repository = new LocalRepository(basePath.toAbsolutePath(), type); this.localPathComposer = requireNonNull(localPathComposer); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java index a816ececd0..72894a89fc 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java @@ -24,7 +24,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.function.BiFunction; import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; @@ -45,6 +44,7 @@ import org.eclipse.aether.transfer.RepositoryOfflineException; import org.eclipse.aether.util.ConfigUtils; import org.eclipse.aether.util.repository.RepositoryIdHelper; +import org.eclipse.aether.util.repository.RepositoryKeyFunction; import static java.util.Objects.requireNonNull; @@ -222,8 +222,7 @@ public static void checkOffline( * @since 2.0.14 * @see #repositoryKeyFunction(Class, RepositorySystemSession, String, String) */ - public static BiFunction systemRepositoryKeyFunction( - RepositorySystemSession session) { + public static RepositoryKeyFunction systemRepositoryKeyFunction(RepositorySystemSession session) { return repositoryKeyFunction( Utils.class, session, @@ -240,15 +239,14 @@ public static BiFunction systemRepositoryKeyFu * @since 2.0.14 */ @SuppressWarnings("unchecked") - public static BiFunction repositoryKeyFunction( + public static RepositoryKeyFunction repositoryKeyFunction( Class owner, RepositorySystemSession session, String defaultValue, String configurationKey) { requireNonNull(session); requireNonNull(defaultValue); - final RepositoryIdHelper.RepositoryKeyFunction repositoryKeyFunction = - RepositoryIdHelper.getRepositoryKeyFunction( - configurationKey != null - ? ConfigUtils.getString(session, defaultValue, configurationKey) - : defaultValue); + final RepositoryKeyFunction repositoryKeyFunction = RepositoryIdHelper.getRepositoryKeyFunction( + configurationKey != null + ? ConfigUtils.getString(session, defaultValue, configurationKey) + : defaultValue); if (session.getCache() != null) { // both are expensive methods; cache it in session (repo -> context -> ID) return (repository, context) -> ((ConcurrentMap>) diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index 9f679e8331..c9719f51c5 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -23,7 +23,6 @@ import java.util.Locale; import java.util.SortedSet; import java.util.TreeSet; -import java.util.function.BiFunction; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; @@ -79,15 +78,6 @@ public enum RepositoryKeyType { GURK } - /** - * The repository key function. - */ - @FunctionalInterface - public interface RepositoryKeyFunction extends BiFunction { - @Override - String apply(RemoteRepository repository, String context); - } - /** * Selector method for {@link RepositoryKeyFunction} based on string representation of {@link RepositoryKeyType} * enum. diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryKeyFunction.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryKeyFunction.java new file mode 100644 index 0000000000..55d3af8b19 --- /dev/null +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryKeyFunction.java @@ -0,0 +1,42 @@ +/* + * 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.eclipse.aether.util.repository; + +import java.util.function.BiFunction; + +import org.eclipse.aether.repository.RemoteRepository; + +/** + * The repository key function. + * + * @since 2.0.14 + */ +@FunctionalInterface +public interface RepositoryKeyFunction extends BiFunction { + /** + * Produces a string representing "repository key" for given {@link RemoteRepository} and + * optionally (maybe {@code null}) "context". + * + * @param repository The {@link RemoteRepository}, may not be {@code null}. + * @param context The "context" string, or {@code null}. + * @return The "repository key" string, never {@code null}. + */ + @Override + String apply(RemoteRepository repository, String context); +} From 065ea1a6d725593fb29f04ce245a75bc6161f937 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 28 Nov 2025 16:19:10 +0100 Subject: [PATCH 24/28] Remove unneeded --- .../internal/impl/SimpleLocalRepositoryManagerFactory.java | 1 - 1 file changed, 1 deletion(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java index 9f19fe8ae0..fd8c31b670 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java @@ -54,7 +54,6 @@ public SimpleLocalRepositoryManagerFactory(final LocalPathComposer localPathComp this.localPathComposer = requireNonNull(localPathComposer); } - @SuppressWarnings("unchecked") @Override public LocalRepositoryManager newInstance(RepositorySystemSession session, LocalRepository repository) throws NoLocalRepositoryManagerException { From 155050c0cb6266f0e66f23d2e6f2f6a0d1d62835 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 28 Nov 2025 16:56:54 +0100 Subject: [PATCH 25/28] Create dedicated component to create repositoryKey instances --- .../impl/RepositoryKeyFunctionFactory.java | 57 ++++++++++++ ...DefaultLocalPathPrefixComposerFactory.java | 13 ++- .../impl/DefaultRemoteRepositoryManager.java | 11 ++- .../DefaultRepositoryKeyFunctionFactory.java | 88 +++++++++++++++++++ ...EnhancedLocalRepositoryManagerFactory.java | 9 +- .../SimpleLocalRepositoryManagerFactory.java | 10 ++- .../eclipse/aether/internal/impl/Utils.java | 55 ------------ .../FileTrustedChecksumsSourceSupport.java | 11 ++- ...SparseDirectoryTrustedChecksumsSource.java | 6 +- .../SummaryFileTrustedChecksumsSource.java | 3 + .../GroupIdRemoteRepositoryFilterSource.java | 6 +- .../PrefixesRemoteRepositoryFilterSource.java | 3 + .../RemoteRepositoryFilterSourceSupport.java | 11 ++- ...ultLocalPathPrefixComposerFactoryTest.java | 12 ++- .../DefaultRemoteRepositoryManagerTest.java | 6 +- .../impl/DefaultRepositorySystemTest.java | 4 +- .../EnhancedLocalRepositoryManagerTest.java | 3 +- ...hancedSplitLocalRepositoryManagerTest.java | 3 +- ...seDirectoryTrustedChecksumsSourceTest.java | 5 +- ...SummaryFileTrustedChecksumsSourceTest.java | 6 +- ...oupIdRemoteRepositoryFilterSourceTest.java | 5 +- ...fixesRemoteRepositoryFilterSourceTest.java | 6 +- .../supplier/RepositorySystemSupplier.java | 46 ++++++++-- .../supplier/RepositorySystemSupplier.java | 46 ++++++++-- 24 files changed, 328 insertions(+), 97 deletions(-) create mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/impl/RepositoryKeyFunctionFactory.java create mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryKeyFunctionFactory.java diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/RepositoryKeyFunctionFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/RepositoryKeyFunctionFactory.java new file mode 100644 index 0000000000..72354bb306 --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/RepositoryKeyFunctionFactory.java @@ -0,0 +1,57 @@ +/* + * 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.eclipse.aether.impl; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.util.repository.RepositoryKeyFunction; + +/** + * A factory to create {@link RepositoryKeyFunction} instances. + * + * @since 2.0.14 + * @provisional This type is provisional and can be changed, moved or removed without prior notice. + */ +public interface RepositoryKeyFunctionFactory { + /** + * Returns system-wide repository key function. + * + * @param session The repository session, must not be {@code null}. + * @return The repository key function. + * @see #repositoryKeyFunction(Class, RepositorySystemSession, String, String) + * @see org.eclipse.aether.ConfigurationProperties#REPOSITORY_SYSTEM_REPOSITORY_KEY_FUNCTION + */ + RepositoryKeyFunction systemRepositoryKeyFunction(RepositorySystemSession session); + + /** + * Method that based on configuration returns the "repository key function". The returned function will be session + * cached if session is equipped with cache, otherwise it will be non cached. Method never returns {@code null}. + * Only the {@code configurationKey} parameter may be {@code null} in which case no configuration lookup happens + * but the {@code defaultValue} is directly used instead. + * + * @param owner The "owner" of key function (used to create cache-key), must not be {@code null}. + * @param session The repository session, must not be {@code null}. + * @param defaultValue The default value of repository key configuration, must not be {@code null}. + * @param configurationKey The configuration key to lookup configuration from, may be {@code null}, in which case + * no configuration lookup happens but the {@code defaultValue} is used to create the + * repository key function. + * @return The repository key function. + */ + RepositoryKeyFunction repositoryKeyFunction( + Class owner, RepositorySystemSession session, String defaultValue, String configurationKey); +} diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java index 49d3963844..1490241b69 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java @@ -18,12 +18,16 @@ */ package org.eclipse.aether.internal.impl; +import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.util.repository.RepositoryKeyFunction; +import static java.util.Objects.requireNonNull; + /** * Default local path prefix composer factory: it fully reuses {@link LocalPathPrefixComposerFactorySupport} class * without changing anything from it. @@ -33,6 +37,13 @@ @Singleton @Named public final class DefaultLocalPathPrefixComposerFactory extends LocalPathPrefixComposerFactorySupport { + private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory; + + @Inject + public DefaultLocalPathPrefixComposerFactory(RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) { + this.repositoryKeyFunctionFactory = requireNonNull(repositoryKeyFunctionFactory); + } + @Override public LocalPathPrefixComposer createComposer(RepositorySystemSession session) { return new DefaultLocalPathPrefixComposer( @@ -45,7 +56,7 @@ public LocalPathPrefixComposer createComposer(RepositorySystemSession session) { isSplitRemoteRepositoryLast(session), getReleasesPrefix(session), getSnapshotsPrefix(session), - Utils.systemRepositoryKeyFunction(session)); + repositoryKeyFunctionFactory.systemRepositoryKeyFunction(session)); } /** 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 00168ab3ce..1a70529d33 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 @@ -31,6 +31,7 @@ import org.eclipse.aether.RepositoryCache; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.impl.RemoteRepositoryManager; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.impl.UpdatePolicyAnalyzer; import org.eclipse.aether.repository.Authentication; import org.eclipse.aether.repository.AuthenticationSelector; @@ -83,11 +84,17 @@ public int hashCode() { private final ChecksumPolicyProvider checksumPolicyProvider; + private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory; + @Inject public DefaultRemoteRepositoryManager( - UpdatePolicyAnalyzer updatePolicyAnalyzer, ChecksumPolicyProvider checksumPolicyProvider) { + UpdatePolicyAnalyzer updatePolicyAnalyzer, + ChecksumPolicyProvider checksumPolicyProvider, + RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) { this.updatePolicyAnalyzer = requireNonNull(updatePolicyAnalyzer, "update policy analyzer cannot be null"); this.checksumPolicyProvider = requireNonNull(checksumPolicyProvider, "checksum policy provider cannot be null"); + this.repositoryKeyFunctionFactory = + requireNonNull(repositoryKeyFunctionFactory, "repository key function factory cannot be null"); } @Override @@ -103,7 +110,7 @@ public List aggregateRepositories( return dominantRepositories; } - RepositoryKeyFunction repositoryKeyFunction = Utils.systemRepositoryKeyFunction(session); + RepositoryKeyFunction repositoryKeyFunction = repositoryKeyFunctionFactory.systemRepositoryKeyFunction(session); MirrorSelector mirrorSelector = session.getMirrorSelector(); AuthenticationSelector authSelector = session.getAuthenticationSelector(); ProxySelector proxySelector = session.getProxySelector(); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryKeyFunctionFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryKeyFunctionFactory.java new file mode 100644 index 0000000000..71b62bb8f4 --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryKeyFunctionFactory.java @@ -0,0 +1,88 @@ +/* + * 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.eclipse.aether.internal.impl; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.eclipse.aether.ConfigurationProperties; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.util.ConfigUtils; +import org.eclipse.aether.util.repository.RepositoryIdHelper; +import org.eclipse.aether.util.repository.RepositoryKeyFunction; + +import static java.util.Objects.requireNonNull; + +@Singleton +@Named +public class DefaultRepositoryKeyFunctionFactory implements RepositoryKeyFunctionFactory { + /** + * Returns system-wide repository key function. + * + * @since 2.0.14 + * @see #repositoryKeyFunction(Class, RepositorySystemSession, String, String) + */ + @Override + public RepositoryKeyFunction systemRepositoryKeyFunction(RepositorySystemSession session) { + return repositoryKeyFunction( + DefaultRepositoryKeyFunctionFactory.class, + session, + ConfigurationProperties.DEFAULT_REPOSITORY_SYSTEM_REPOSITORY_KEY_FUNCTION, + ConfigurationProperties.REPOSITORY_SYSTEM_REPOSITORY_KEY_FUNCTION); + } + + /** + * Method that based on configuration returns the "repository key function". The returned function will be session + * cached if session is equipped with cache, otherwise it will be non cached. Method never returns {@code null}. + * Only the {@code configurationKey} parameter may be {@code null} in which case no configuration lookup happens + * but the {@code defaultValue} is directly used instead. + * + * @since 2.0.14 + */ + @SuppressWarnings("unchecked") + @Override + public RepositoryKeyFunction repositoryKeyFunction( + Class owner, RepositorySystemSession session, String defaultValue, String configurationKey) { + requireNonNull(session); + requireNonNull(defaultValue); + final RepositoryKeyFunction repositoryKeyFunction = RepositoryIdHelper.getRepositoryKeyFunction( + configurationKey != null + ? ConfigUtils.getString(session, defaultValue, configurationKey) + : defaultValue); + if (session.getCache() != null) { + // both are expensive methods; cache it in session (repo -> context -> ID) + return (repository, context) -> ((ConcurrentMap>) + session.getCache() + .computeIfAbsent( + session, + owner.getName() + ".repositoryKeyFunction", + ConcurrentHashMap::new)) + .computeIfAbsent(repository, k1 -> new ConcurrentHashMap<>()) + .computeIfAbsent( + context == null ? "" : context, k2 -> repositoryKeyFunction.apply(repository, context)); + } else { + return repositoryKeyFunction; + } + } +} diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index e0f1203829..142259c879 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -24,6 +24,7 @@ import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.NoLocalRepositoryManagerException; @@ -65,14 +66,18 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan private final LocalPathPrefixComposerFactory localPathPrefixComposerFactory; + private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory; + @Inject public EnhancedLocalRepositoryManagerFactory( final LocalPathComposer localPathComposer, final TrackingFileManager trackingFileManager, - final LocalPathPrefixComposerFactory localPathPrefixComposerFactory) { + final LocalPathPrefixComposerFactory localPathPrefixComposerFactory, + final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) { this.localPathComposer = requireNonNull(localPathComposer); this.trackingFileManager = requireNonNull(trackingFileManager); this.localPathPrefixComposerFactory = requireNonNull(localPathPrefixComposerFactory); + this.repositoryKeyFunctionFactory = requireNonNull(repositoryKeyFunctionFactory); } @Override @@ -93,7 +98,7 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local return new EnhancedLocalRepositoryManager( repository.getBasePath(), localPathComposer, - Utils.systemRepositoryKeyFunction(session), + repositoryKeyFunctionFactory.systemRepositoryKeyFunction(session), trackingFilename, trackingFileManager, localPathPrefixComposerFactory.createComposer(session)); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java index fd8c31b670..5b49426255 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java @@ -23,6 +23,7 @@ import javax.inject.Singleton; import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.NoLocalRepositoryManagerException; @@ -41,17 +42,22 @@ public class SimpleLocalRepositoryManagerFactory implements LocalRepositoryManag private float priority; private final LocalPathComposer localPathComposer; + private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory; /** * No-arg constructor, as "simple" local repository is meant mainly for use in tests. */ public SimpleLocalRepositoryManagerFactory() { this.localPathComposer = new DefaultLocalPathComposer(); + this.repositoryKeyFunctionFactory = new DefaultRepositoryKeyFunctionFactory(); } @Inject - public SimpleLocalRepositoryManagerFactory(final LocalPathComposer localPathComposer) { + public SimpleLocalRepositoryManagerFactory( + final LocalPathComposer localPathComposer, + final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) { this.localPathComposer = requireNonNull(localPathComposer); + this.repositoryKeyFunctionFactory = requireNonNull(repositoryKeyFunctionFactory); } @Override @@ -65,7 +71,7 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local repository.getBasePath(), "simple", localPathComposer, - Utils.repositoryKeyFunction( + repositoryKeyFunctionFactory.repositoryKeyFunction( SimpleLocalRepositoryManagerFactory.class, session, RepositoryIdHelper.RepositoryKeyType.SIMPLE.name(), diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java index 72894a89fc..87cb48ae12 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java @@ -22,10 +22,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.deployment.DeployRequest; @@ -42,11 +39,6 @@ import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator; import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory; import org.eclipse.aether.transfer.RepositoryOfflineException; -import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryIdHelper; -import org.eclipse.aether.util.repository.RepositoryKeyFunction; - -import static java.util.Objects.requireNonNull; /** * Internal utility methods. @@ -215,51 +207,4 @@ public static void checkOffline( offlineController.checkOffline(session, repository); } } - - /** - * Returns system-wide repository key function. - * - * @since 2.0.14 - * @see #repositoryKeyFunction(Class, RepositorySystemSession, String, String) - */ - public static RepositoryKeyFunction systemRepositoryKeyFunction(RepositorySystemSession session) { - return repositoryKeyFunction( - Utils.class, - session, - ConfigurationProperties.DEFAULT_REPOSITORY_SYSTEM_REPOSITORY_KEY_FUNCTION, - ConfigurationProperties.REPOSITORY_SYSTEM_REPOSITORY_KEY_FUNCTION); - } - - /** - * Method that based on configuration returns the "repository key function". The returned function will be session - * cached if session is equipped with cache, otherwise it will be non cached. Method never returns {@code null}. - * Only the {@code configurationKey} parameter may be {@code null} in which case no configuration lookup happens - * but the {@code defaultValue} is directly used instead. - * - * @since 2.0.14 - */ - @SuppressWarnings("unchecked") - public static RepositoryKeyFunction repositoryKeyFunction( - Class owner, RepositorySystemSession session, String defaultValue, String configurationKey) { - requireNonNull(session); - requireNonNull(defaultValue); - final RepositoryKeyFunction repositoryKeyFunction = RepositoryIdHelper.getRepositoryKeyFunction( - configurationKey != null - ? ConfigUtils.getString(session, defaultValue, configurationKey) - : defaultValue); - if (session.getCache() != null) { - // both are expensive methods; cache it in session (repo -> context -> ID) - return (repository, context) -> ((ConcurrentMap>) - session.getCache() - .computeIfAbsent( - session, - owner.getName() + ".repositoryKeyFunction", - ConcurrentHashMap::new)) - .computeIfAbsent(repository, k1 -> new ConcurrentHashMap<>()) - .computeIfAbsent( - context == null ? "" : context, k2 -> repositoryKeyFunction.apply(repository, context)); - } else { - return repositoryKeyFunction; - } - } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java index 8850c05b4a..4906d969ae 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java @@ -27,7 +27,7 @@ import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.internal.impl.Utils; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.checksums.TrustedChecksumsSource; @@ -74,6 +74,12 @@ public abstract class FileTrustedChecksumsSourceSupport implements TrustedChecks public static final String DEFAULT_REPOSITORY_KEY_FUNCTION = "nid"; + private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory; + + protected FileTrustedChecksumsSourceSupport(RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) { + this.repositoryKeyFunctionFactory = requireNonNull(repositoryKeyFunctionFactory); + } + /** * This implementation will call into underlying code only if enabled, and will enforce non-{@code null} return * value. In worst case, empty map should be returned, meaning "no trusted checksums available". @@ -156,7 +162,8 @@ protected Path getBasedir( */ protected String repositoryKey(RepositorySystemSession session, ArtifactRepository artifactRepository) { if (artifactRepository instanceof RemoteRepository) { - return Utils.repositoryKeyFunction( + return repositoryKeyFunctionFactory + .repositoryKeyFunction( FileTrustedChecksumsSourceSupport.class, session, DEFAULT_REPOSITORY_KEY_FUNCTION, diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java index 27f981fb2c..7f917e0dbd 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java @@ -33,6 +33,7 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.internal.impl.LocalPathComposer; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; @@ -105,7 +106,10 @@ public final class SparseDirectoryTrustedChecksumsSource extends FileTrustedChec @Inject public SparseDirectoryTrustedChecksumsSource( - ChecksumProcessor checksumProcessor, LocalPathComposer localPathComposer) { + RepositoryKeyFunctionFactory repositoryKeyFunctionFactory, + ChecksumProcessor checksumProcessor, + LocalPathComposer localPathComposer) { + super(repositoryKeyFunctionFactory); this.checksumProcessor = requireNonNull(checksumProcessor); this.localPathComposer = requireNonNull(localPathComposer); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java index 952429fff7..b7b4105b20 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java @@ -41,6 +41,7 @@ import org.eclipse.aether.MultiRuntimeException; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.LocalPathComposer; import org.eclipse.aether.repository.ArtifactRepository; @@ -141,9 +142,11 @@ public final class SummaryFileTrustedChecksumsSource extends FileTrustedChecksum @Inject public SummaryFileTrustedChecksumsSource( + RepositoryKeyFunctionFactory repoKeyFunctionFactory, LocalPathComposer localPathComposer, RepositorySystemLifecycle repositorySystemLifecycle, PathProcessor pathProcessor) { + super(repoKeyFunctionFactory); this.localPathComposer = requireNonNull(localPathComposer); this.repositorySystemLifecycle = requireNonNull(repositorySystemLifecycle); this.pathProcessor = requireNonNull(pathProcessor); 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 ac6fc3f433..3c84d3a5ac 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 @@ -41,6 +41,7 @@ import org.eclipse.aether.MultiRuntimeException; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.filter.ruletree.GroupTree; import org.eclipse.aether.metadata.Metadata; @@ -156,7 +157,10 @@ public final class GroupIdRemoteRepositoryFilterSource extends RemoteRepositoryF @Inject public GroupIdRemoteRepositoryFilterSource( - RepositorySystemLifecycle repositorySystemLifecycle, PathProcessor pathProcessor) { + RepositoryKeyFunctionFactory repositoryKeyFunctionFactory, + RepositorySystemLifecycle repositorySystemLifecycle, + PathProcessor pathProcessor) { + super(repositoryKeyFunctionFactory); this.repositorySystemLifecycle = requireNonNull(repositorySystemLifecycle); this.pathProcessor = requireNonNull(pathProcessor); } 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 bd383f6eb2..8116d70b90 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 @@ -36,6 +36,7 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.MetadataResolver; import org.eclipse.aether.impl.RemoteRepositoryManager; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.internal.impl.filter.prefixes.PrefixesSource; import org.eclipse.aether.internal.impl.filter.ruletree.PrefixTree; import org.eclipse.aether.metadata.DefaultMetadata; @@ -196,9 +197,11 @@ public final class PrefixesRemoteRepositoryFilterSource extends RemoteRepository @Inject public PrefixesRemoteRepositoryFilterSource( + RepositoryKeyFunctionFactory repositoryKeyFunctionFactory, Supplier metadataResolver, Supplier remoteRepositoryManager, RepositoryLayoutProvider repositoryLayoutProvider) { + super(repositoryKeyFunctionFactory); this.metadataResolver = requireNonNull(metadataResolver); this.remoteRepositoryManager = requireNonNull(remoteRepositoryManager); this.repositoryLayoutProvider = requireNonNull(repositoryLayoutProvider); 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 1482de6d84..8663f83e5b 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 @@ -24,7 +24,7 @@ import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.internal.impl.Utils; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.internal.impl.checksum.FileTrustedChecksumsSourceSupport; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter; @@ -69,6 +69,12 @@ public abstract class RemoteRepositoryFilterSourceSupport implements RemoteRepos public static final String DEFAULT_REPOSITORY_KEY_FUNCTION = "nid"; + private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory; + + protected RemoteRepositoryFilterSourceSupport(RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) { + this.repositoryKeyFunctionFactory = requireNonNull(repositoryKeyFunctionFactory); + } + /** * Returns {@code true} if session configuration contains this name set to {@code true}. *

    @@ -111,7 +117,8 @@ protected RemoteRepository normalizeRemoteRepository( * @since 2.0.14 */ protected String repositoryKey(RepositorySystemSession session, RemoteRepository repository) { - return Utils.repositoryKeyFunction( + return repositoryKeyFunctionFactory + .repositoryKeyFunction( FileTrustedChecksumsSourceSupport.class, session, DEFAULT_REPOSITORY_KEY_FUNCTION, diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactoryTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactoryTest.java index 0a47ac7ae0..8f6c53452f 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactoryTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactoryTest.java @@ -56,7 +56,8 @@ public class DefaultLocalPathPrefixComposerFactoryTest { void defaultConfigNoSplitAllNulls() { DefaultRepositorySystemSession session = TestUtils.newSession(); - LocalPathPrefixComposerFactory factory = new DefaultLocalPathPrefixComposerFactory(); + LocalPathPrefixComposerFactory factory = + new DefaultLocalPathPrefixComposerFactory(new DefaultRepositoryKeyFunctionFactory()); LocalPathPrefixComposer composer = factory.createComposer(session); assertNotNull(composer); @@ -79,7 +80,8 @@ void splitEnabled() { DefaultRepositorySystemSession session = TestUtils.newSession(); session.setConfigProperty(DefaultLocalPathPrefixComposerFactory.CONFIG_PROP_SPLIT, Boolean.TRUE.toString()); - LocalPathPrefixComposerFactory factory = new DefaultLocalPathPrefixComposerFactory(); + LocalPathPrefixComposerFactory factory = + new DefaultLocalPathPrefixComposerFactory(new DefaultRepositoryKeyFunctionFactory()); LocalPathPrefixComposer composer = factory.createComposer(session); assertNotNull(composer); @@ -110,7 +112,8 @@ void saneConfig() { session.setConfigProperty( DefaultLocalPathPrefixComposerFactory.CONFIG_PROP_SPLIT_REMOTE_REPOSITORY, Boolean.TRUE.toString()); - LocalPathPrefixComposerFactory factory = new DefaultLocalPathPrefixComposerFactory(); + LocalPathPrefixComposerFactory factory = + new DefaultLocalPathPrefixComposerFactory(new DefaultRepositoryKeyFunctionFactory()); LocalPathPrefixComposer composer = factory.createComposer(session); assertNotNull(composer); @@ -175,7 +178,8 @@ void fullConfig() { session.setConfigProperty( DefaultLocalPathPrefixComposerFactory.CONFIG_PROP_SPLIT_REMOTE_REPOSITORY, Boolean.TRUE.toString()); - LocalPathPrefixComposerFactory factory = new DefaultLocalPathPrefixComposerFactory(); + LocalPathPrefixComposerFactory factory = + new DefaultLocalPathPrefixComposerFactory(new DefaultRepositoryKeyFunctionFactory()); LocalPathPrefixComposer composer = factory.createComposer(session); assertNotNull(composer); diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManagerTest.java index 20ec8462ea..9b51a2db5b 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManagerTest.java @@ -53,8 +53,10 @@ void setup() { session = TestUtils.newSession(); session.setChecksumPolicy(null); session.setUpdatePolicy(null); - manager = - new DefaultRemoteRepositoryManager(new StubUpdatePolicyAnalyzer(), new DefaultChecksumPolicyProvider()); + manager = new DefaultRemoteRepositoryManager( + new StubUpdatePolicyAnalyzer(), + new DefaultChecksumPolicyProvider(), + new DefaultRepositoryKeyFunctionFactory()); } @AfterEach diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRepositorySystemTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRepositorySystemTest.java index 08b75df3f5..4fa1953d59 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRepositorySystemTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRepositorySystemTest.java @@ -64,7 +64,9 @@ void init() { mock(LocalRepositoryProvider.class), new StubSyncContextFactory(), new DefaultRemoteRepositoryManager( - new DefaultUpdatePolicyAnalyzer(), new DefaultChecksumPolicyProvider()), + new DefaultUpdatePolicyAnalyzer(), + new DefaultChecksumPolicyProvider(), + new DefaultRepositoryKeyFunctionFactory()), new DefaultRepositorySystemLifecycle(), Collections.emptyMap(), new DefaultRepositorySystemValidator(Collections.emptyList())); diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java index 25c6af7474..2e3468dc6c 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java @@ -111,7 +111,8 @@ protected EnhancedLocalRepositoryManager getManager() { RepositoryIdHelper::simpleRepositoryKey, "_remote.repositories", trackingFileManager, - new DefaultLocalPathPrefixComposerFactory().createComposer(session)); + new DefaultLocalPathPrefixComposerFactory(new DefaultRepositoryKeyFunctionFactory()) + .createComposer(session)); } @AfterEach diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java index dbf37e80c2..538c7afe02 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java @@ -37,7 +37,8 @@ protected EnhancedLocalRepositoryManager getManager() { RepositoryIdHelper::simpleRepositoryKey, "_remote.repositories", trackingFileManager, - new DefaultLocalPathPrefixComposerFactory().createComposer(session)); + new DefaultLocalPathPrefixComposerFactory(new DefaultRepositoryKeyFunctionFactory()) + .createComposer(session)); } @Test diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java index 83e812a577..2053ee4895 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java @@ -23,12 +23,15 @@ import org.eclipse.aether.internal.impl.DefaultChecksumProcessor; import org.eclipse.aether.internal.impl.DefaultLocalPathComposer; import org.eclipse.aether.internal.impl.DefaultPathProcessor; +import org.eclipse.aether.internal.impl.DefaultRepositoryKeyFunctionFactory; public class SparseDirectoryTrustedChecksumsSourceTest extends FileTrustedChecksumsSourceTestSupport { @Override protected FileTrustedChecksumsSourceSupport prepareSubject(RepositorySystemLifecycle lifecycle) { return new SparseDirectoryTrustedChecksumsSource( - new DefaultChecksumProcessor(new DefaultPathProcessor()), new DefaultLocalPathComposer()); + new DefaultRepositoryKeyFunctionFactory(), + new DefaultChecksumProcessor(new DefaultPathProcessor()), + new DefaultLocalPathComposer()); } @Override diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java index 2646d783cd..6d31e5da85 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java @@ -26,6 +26,7 @@ import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.DefaultLocalPathComposer; +import org.eclipse.aether.internal.impl.DefaultRepositoryKeyFunctionFactory; import org.eclipse.aether.internal.impl.DefaultRepositorySystemLifecycle; import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory; import org.eclipse.aether.repository.LocalRepository; @@ -44,7 +45,10 @@ public class SummaryFileTrustedChecksumsSourceTest extends FileTrustedChecksumsS @Override protected FileTrustedChecksumsSourceSupport prepareSubject(RepositorySystemLifecycle lifecycle) { return new SummaryFileTrustedChecksumsSource( - new DefaultLocalPathComposer(), lifecycle, new PathProcessorSupport()); + new DefaultRepositoryKeyFunctionFactory(), + new DefaultLocalPathComposer(), + lifecycle, + new PathProcessorSupport()); } @Override diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java index 4a60622adc..6bcbdaaf9b 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java @@ -26,6 +26,7 @@ import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.internal.impl.DefaultRepositoryKeyFunctionFactory; import org.eclipse.aether.internal.impl.DefaultRepositorySystemLifecycle; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ArtifactRequest; @@ -42,7 +43,9 @@ public class GroupIdRemoteRepositoryFilterSourceTest extends RemoteRepositoryFil protected GroupIdRemoteRepositoryFilterSource getRemoteRepositoryFilterSource( DefaultRepositorySystemSession session, RemoteRepository remoteRepository) { return groupIdRemoteRepositoryFilterSource = new GroupIdRemoteRepositoryFilterSource( - new DefaultRepositorySystemLifecycle(), new PathProcessorSupport()); + new DefaultRepositoryKeyFunctionFactory(), + new DefaultRepositorySystemLifecycle(), + new PathProcessorSupport()); } @Override diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSourceTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSourceTest.java index 7d7c217826..5db48616fe 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSourceTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSourceTest.java @@ -33,6 +33,7 @@ import org.eclipse.aether.impl.MetadataResolver; import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.internal.impl.DefaultArtifactPredicateFactory; +import org.eclipse.aether.internal.impl.DefaultRepositoryKeyFunctionFactory; import org.eclipse.aether.internal.impl.DefaultRepositoryLayoutProvider; import org.eclipse.aether.internal.impl.Maven2RepositoryLayoutFactory; import org.eclipse.aether.repository.RemoteRepository; @@ -84,7 +85,10 @@ public RepositoryPolicy getPolicy( new Maven2RepositoryLayoutFactory( checksumsSelector(), new DefaultArtifactPredicateFactory(checksumsSelector())))); return new PrefixesRemoteRepositoryFilterSource( - () -> metadataResolver, () -> remoteRepositoryManager, layoutProvider); + new DefaultRepositoryKeyFunctionFactory(), + () -> metadataResolver, + () -> remoteRepositoryManager, + layoutProvider); } @Override diff --git a/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java b/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java index c104ebc458..d2c379b724 100644 --- a/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java +++ b/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java @@ -51,6 +51,7 @@ import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.impl.RepositoryConnectorProvider; import org.eclipse.aether.impl.RepositoryEventDispatcher; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.impl.RepositorySystemValidator; import org.eclipse.aether.impl.UpdateCheckManager; @@ -72,6 +73,7 @@ import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager; import org.eclipse.aether.internal.impl.DefaultRepositoryConnectorProvider; import org.eclipse.aether.internal.impl.DefaultRepositoryEventDispatcher; +import org.eclipse.aether.internal.impl.DefaultRepositoryKeyFunctionFactory; import org.eclipse.aether.internal.impl.DefaultRepositoryLayoutProvider; import org.eclipse.aether.internal.impl.DefaultRepositorySystem; import org.eclipse.aether.internal.impl.DefaultRepositorySystemLifecycle; @@ -247,7 +249,7 @@ public final LocalPathPrefixComposerFactory getLocalPathPrefixComposerFactory() } protected LocalPathPrefixComposerFactory createLocalPathPrefixComposerFactory() { - return new DefaultLocalPathPrefixComposerFactory(); + return new DefaultLocalPathPrefixComposerFactory(getRepositoryKeyFunctionFactory()); } private RepositorySystemLifecycle repositorySystemLifecycle; @@ -321,6 +323,20 @@ protected UpdateCheckManager createUpdateCheckManager() { return new DefaultUpdateCheckManager(getTrackingFileManager(), getUpdatePolicyAnalyzer(), getPathProcessor()); } + private RepositoryKeyFunctionFactory repositoriesKeyFunctionFactory; + + public final RepositoryKeyFunctionFactory getRepositoryKeyFunctionFactory() { + checkClosed(); + if (repositoriesKeyFunctionFactory == null) { + repositoriesKeyFunctionFactory = createRepositoryKeyFunctionFactory(); + } + return repositoriesKeyFunctionFactory; + } + + protected RepositoryKeyFunctionFactory createRepositoryKeyFunctionFactory() { + return new DefaultRepositoryKeyFunctionFactory(); + } + private Map namedLockFactories; public final Map getNamedLockFactories() { @@ -484,13 +500,18 @@ public final LocalRepositoryProvider getLocalRepositoryProvider() { protected LocalRepositoryProvider createLocalRepositoryProvider() { LocalPathComposer localPathComposer = getLocalPathComposer(); + RepositoryKeyFunctionFactory repositoryKeyFunctionFactory = getRepositoryKeyFunctionFactory(); HashMap localRepositoryProviders = new HashMap<>(2); localRepositoryProviders.put( - SimpleLocalRepositoryManagerFactory.NAME, new SimpleLocalRepositoryManagerFactory(localPathComposer)); + SimpleLocalRepositoryManagerFactory.NAME, + new SimpleLocalRepositoryManagerFactory(localPathComposer, repositoryKeyFunctionFactory)); localRepositoryProviders.put( EnhancedLocalRepositoryManagerFactory.NAME, new EnhancedLocalRepositoryManagerFactory( - localPathComposer, getTrackingFileManager(), getLocalPathPrefixComposerFactory())); + localPathComposer, + getTrackingFileManager(), + getLocalPathPrefixComposerFactory(), + repositoryKeyFunctionFactory)); return new DefaultLocalRepositoryProvider(localRepositoryProviders); } @@ -505,7 +526,8 @@ public final RemoteRepositoryManager getRemoteRepositoryManager() { } protected RemoteRepositoryManager createRemoteRepositoryManager() { - return new DefaultRemoteRepositoryManager(getUpdatePolicyAnalyzer(), getChecksumPolicyProvider()); + return new DefaultRemoteRepositoryManager( + getUpdatePolicyAnalyzer(), getChecksumPolicyProvider(), getRepositoryKeyFunctionFactory()); } private Map remoteRepositoryFilterSources; @@ -522,11 +544,15 @@ protected Map createRemoteRepositoryFilter HashMap result = new HashMap<>(); result.put( GroupIdRemoteRepositoryFilterSource.NAME, - new GroupIdRemoteRepositoryFilterSource(getRepositorySystemLifecycle(), getPathProcessor())); + new GroupIdRemoteRepositoryFilterSource( + getRepositoryKeyFunctionFactory(), getRepositorySystemLifecycle(), getPathProcessor())); result.put( PrefixesRemoteRepositoryFilterSource.NAME, new PrefixesRemoteRepositoryFilterSource( - this::getMetadataResolver, this::getRemoteRepositoryManager, getRepositoryLayoutProvider())); + getRepositoryKeyFunctionFactory(), + this::getMetadataResolver, + this::getRemoteRepositoryManager, + getRepositoryLayoutProvider())); return result; } @@ -586,11 +612,15 @@ protected Map createTrustedChecksumsSources() { HashMap result = new HashMap<>(); result.put( SparseDirectoryTrustedChecksumsSource.NAME, - new SparseDirectoryTrustedChecksumsSource(getChecksumProcessor(), getLocalPathComposer())); + new SparseDirectoryTrustedChecksumsSource( + getRepositoryKeyFunctionFactory(), getChecksumProcessor(), getLocalPathComposer())); result.put( SummaryFileTrustedChecksumsSource.NAME, new SummaryFileTrustedChecksumsSource( - getLocalPathComposer(), getRepositorySystemLifecycle(), getPathProcessor())); + getRepositoryKeyFunctionFactory(), + getLocalPathComposer(), + getRepositorySystemLifecycle(), + getPathProcessor())); return result; } diff --git a/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java b/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java index 0fc19c0e9b..01b2f81e05 100644 --- a/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java +++ b/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java @@ -55,6 +55,7 @@ import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.impl.RepositoryConnectorProvider; import org.eclipse.aether.impl.RepositoryEventDispatcher; +import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.impl.RepositorySystemValidator; import org.eclipse.aether.impl.UpdateCheckManager; @@ -76,6 +77,7 @@ import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager; import org.eclipse.aether.internal.impl.DefaultRepositoryConnectorProvider; import org.eclipse.aether.internal.impl.DefaultRepositoryEventDispatcher; +import org.eclipse.aether.internal.impl.DefaultRepositoryKeyFunctionFactory; import org.eclipse.aether.internal.impl.DefaultRepositoryLayoutProvider; import org.eclipse.aether.internal.impl.DefaultRepositorySystem; import org.eclipse.aether.internal.impl.DefaultRepositorySystemLifecycle; @@ -251,7 +253,7 @@ public final LocalPathPrefixComposerFactory getLocalPathPrefixComposerFactory() } protected LocalPathPrefixComposerFactory createLocalPathPrefixComposerFactory() { - return new DefaultLocalPathPrefixComposerFactory(); + return new DefaultLocalPathPrefixComposerFactory(getRepositoryKeyFunctionFactory()); } private RepositorySystemLifecycle repositorySystemLifecycle; @@ -325,6 +327,20 @@ protected UpdateCheckManager createUpdateCheckManager() { return new DefaultUpdateCheckManager(getTrackingFileManager(), getUpdatePolicyAnalyzer(), getPathProcessor()); } + private RepositoryKeyFunctionFactory repositoriesKeyFunctionFactory; + + public final RepositoryKeyFunctionFactory getRepositoryKeyFunctionFactory() { + checkClosed(); + if (repositoriesKeyFunctionFactory == null) { + repositoriesKeyFunctionFactory = createRepositoryKeyFunctionFactory(); + } + return repositoriesKeyFunctionFactory; + } + + protected RepositoryKeyFunctionFactory createRepositoryKeyFunctionFactory() { + return new DefaultRepositoryKeyFunctionFactory(); + } + private Map namedLockFactories; public final Map getNamedLockFactories() { @@ -488,13 +504,18 @@ public final LocalRepositoryProvider getLocalRepositoryProvider() { protected LocalRepositoryProvider createLocalRepositoryProvider() { LocalPathComposer localPathComposer = getLocalPathComposer(); + RepositoryKeyFunctionFactory repositoryKeyFunctionFactory = getRepositoryKeyFunctionFactory(); HashMap localRepositoryProviders = new HashMap<>(2); localRepositoryProviders.put( - SimpleLocalRepositoryManagerFactory.NAME, new SimpleLocalRepositoryManagerFactory(localPathComposer)); + SimpleLocalRepositoryManagerFactory.NAME, + new SimpleLocalRepositoryManagerFactory(localPathComposer, repositoryKeyFunctionFactory)); localRepositoryProviders.put( EnhancedLocalRepositoryManagerFactory.NAME, new EnhancedLocalRepositoryManagerFactory( - localPathComposer, getTrackingFileManager(), getLocalPathPrefixComposerFactory())); + localPathComposer, + getTrackingFileManager(), + getLocalPathPrefixComposerFactory(), + repositoryKeyFunctionFactory)); return new DefaultLocalRepositoryProvider(localRepositoryProviders); } @@ -509,7 +530,8 @@ public final RemoteRepositoryManager getRemoteRepositoryManager() { } protected RemoteRepositoryManager createRemoteRepositoryManager() { - return new DefaultRemoteRepositoryManager(getUpdatePolicyAnalyzer(), getChecksumPolicyProvider()); + return new DefaultRemoteRepositoryManager( + getUpdatePolicyAnalyzer(), getChecksumPolicyProvider(), getRepositoryKeyFunctionFactory()); } private Map remoteRepositoryFilterSources; @@ -526,11 +548,15 @@ protected Map createRemoteRepositoryFilter HashMap result = new HashMap<>(); result.put( GroupIdRemoteRepositoryFilterSource.NAME, - new GroupIdRemoteRepositoryFilterSource(getRepositorySystemLifecycle(), getPathProcessor())); + new GroupIdRemoteRepositoryFilterSource( + getRepositoryKeyFunctionFactory(), getRepositorySystemLifecycle(), getPathProcessor())); result.put( PrefixesRemoteRepositoryFilterSource.NAME, new PrefixesRemoteRepositoryFilterSource( - this::getMetadataResolver, this::getRemoteRepositoryManager, getRepositoryLayoutProvider())); + getRepositoryKeyFunctionFactory(), + this::getMetadataResolver, + this::getRemoteRepositoryManager, + getRepositoryLayoutProvider())); return result; } @@ -590,11 +616,15 @@ protected Map createTrustedChecksumsSources() { HashMap result = new HashMap<>(); result.put( SparseDirectoryTrustedChecksumsSource.NAME, - new SparseDirectoryTrustedChecksumsSource(getChecksumProcessor(), getLocalPathComposer())); + new SparseDirectoryTrustedChecksumsSource( + getRepositoryKeyFunctionFactory(), getChecksumProcessor(), getLocalPathComposer())); result.put( SummaryFileTrustedChecksumsSource.NAME, new SummaryFileTrustedChecksumsSource( - getLocalPathComposer(), getRepositorySystemLifecycle(), getPathProcessor())); + getRepositoryKeyFunctionFactory(), + getLocalPathComposer(), + getRepositorySystemLifecycle(), + getPathProcessor())); return result; } From 4817504b7923f1d76a0a156ba3a872aa9aa63eae Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 28 Nov 2025 17:19:14 +0100 Subject: [PATCH 26/28] Make it SPI --- .../repository/RepositoryKeyFunction.java | 6 ++--- ...DefaultLocalPathPrefixComposerFactory.java | 4 ++-- .../impl/DefaultRemoteRepositoryManager.java | 4 ++-- .../DefaultRepositoryKeyFunctionFactory.java | 4 ++-- .../impl/EnhancedLocalRepositoryManager.java | 2 +- ...EnhancedLocalRepositoryManagerFactory.java | 2 +- ...LocalPathPrefixComposerFactorySupport.java | 2 +- .../impl/SimpleLocalRepositoryManager.java | 2 +- .../SimpleLocalRepositoryManagerFactory.java | 2 +- .../FileTrustedChecksumsSourceSupport.java | 2 +- ...SparseDirectoryTrustedChecksumsSource.java | 2 +- .../SummaryFileTrustedChecksumsSource.java | 2 +- .../GroupIdRemoteRepositoryFilterSource.java | 2 +- .../PrefixesRemoteRepositoryFilterSource.java | 2 +- .../RemoteRepositoryFilterSourceSupport.java | 2 +- .../RepositoryKeyFunctionFactory.java | 5 ++-- .../aether/spi/remoterepo/package-info.java | 23 +++++++++++++++++++ .../supplier/RepositorySystemSupplier.java | 2 +- .../supplier/RepositorySystemSupplier.java | 2 +- .../util/repository/RepositoryIdHelper.java | 1 + 20 files changed, 47 insertions(+), 26 deletions(-) rename {maven-resolver-util/src/main/java/org/eclipse/aether/util => maven-resolver-api/src/main/java/org/eclipse/aether}/repository/RepositoryKeyFunction.java (90%) rename {maven-resolver-impl/src/main/java/org/eclipse/aether/impl => maven-resolver-spi/src/main/java/org/eclipse/aether/spi/remoterepo}/RepositoryKeyFunctionFactory.java (93%) create mode 100644 maven-resolver-spi/src/main/java/org/eclipse/aether/spi/remoterepo/package-info.java diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryKeyFunction.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RepositoryKeyFunction.java similarity index 90% rename from maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryKeyFunction.java rename to maven-resolver-api/src/main/java/org/eclipse/aether/repository/RepositoryKeyFunction.java index 55d3af8b19..e44f7cdc98 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryKeyFunction.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RepositoryKeyFunction.java @@ -16,14 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.eclipse.aether.util.repository; +package org.eclipse.aether.repository; import java.util.function.BiFunction; -import org.eclipse.aether.repository.RemoteRepository; - /** - * The repository key function. + * The repository key function, it produces keys (strings) for given {@link RemoteRepository} instances. * * @since 2.0.14 */ diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java index 1490241b69..0224006676 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java @@ -23,8 +23,8 @@ import javax.inject.Singleton; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; -import org.eclipse.aether.util.repository.RepositoryKeyFunction; +import org.eclipse.aether.repository.RepositoryKeyFunction; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import static java.util.Objects.requireNonNull; 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 1a70529d33..fbe35695aa 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 @@ -31,7 +31,6 @@ import org.eclipse.aether.RepositoryCache; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.impl.RemoteRepositoryManager; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.impl.UpdatePolicyAnalyzer; import org.eclipse.aether.repository.Authentication; import org.eclipse.aether.repository.AuthenticationSelector; @@ -39,9 +38,10 @@ import org.eclipse.aether.repository.Proxy; import org.eclipse.aether.repository.ProxySelector; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.RepositoryKeyFunction; import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider; -import org.eclipse.aether.util.repository.RepositoryKeyFunction; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryKeyFunctionFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryKeyFunctionFactory.java index 71b62bb8f4..4f7a706217 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryKeyFunctionFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryKeyFunctionFactory.java @@ -26,11 +26,11 @@ import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.RepositoryKeyFunction; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.eclipse.aether.util.ConfigUtils; import org.eclipse.aether.util.repository.RepositoryIdHelper; -import org.eclipse.aether.util.repository.RepositoryKeyFunction; import static java.util.Objects.requireNonNull; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java index 8e82047ef6..dd314fed9d 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java @@ -35,7 +35,7 @@ import org.eclipse.aether.repository.LocalArtifactRequest; import org.eclipse.aether.repository.LocalArtifactResult; import org.eclipse.aether.repository.RemoteRepository; -import org.eclipse.aether.util.repository.RepositoryKeyFunction; +import org.eclipse.aether.repository.RepositoryKeyFunction; import static java.util.Objects.requireNonNull; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index 142259c879..e31a493c91 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -24,11 +24,11 @@ import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.NoLocalRepositoryManagerException; import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.eclipse.aether.util.ConfigUtils; import static java.util.Objects.requireNonNull; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java index 6e06f7907c..a01b377c83 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java @@ -22,8 +22,8 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.RepositoryKeyFunction; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryKeyFunction; /** * Support class for {@link LocalPathPrefixComposerFactory} implementations: it predefines and makes re-usable diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java index f2106d5fbb..0acf1b3743 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java @@ -34,7 +34,7 @@ import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.RemoteRepository; -import org.eclipse.aether.util.repository.RepositoryKeyFunction; +import org.eclipse.aether.repository.RepositoryKeyFunction; import static java.util.Objects.requireNonNull; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java index 5b49426255..f454608522 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java @@ -23,11 +23,11 @@ import javax.inject.Singleton; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.NoLocalRepositoryManagerException; import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.eclipse.aether.util.repository.RepositoryIdHelper; import static java.util.Objects.requireNonNull; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java index 4906d969ae..8f052b92be 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java @@ -27,11 +27,11 @@ import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.checksums.TrustedChecksumsSource; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.eclipse.aether.util.DirectoryUtils; import static java.util.Objects.requireNonNull; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java index 7f917e0dbd..0456b3f179 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java @@ -33,11 +33,11 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.internal.impl.LocalPathComposer; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.io.ChecksumProcessor; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.eclipse.aether.util.ConfigUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java index b7b4105b20..f7f4515462 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java @@ -41,12 +41,12 @@ import org.eclipse.aether.MultiRuntimeException; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.LocalPathComposer; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.io.PathProcessor; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.eclipse.aether.util.ConfigUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 3c84d3a5ac..32be14c797 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 @@ -41,7 +41,6 @@ import org.eclipse.aether.MultiRuntimeException; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.filter.ruletree.GroupTree; import org.eclipse.aether.metadata.Metadata; @@ -49,6 +48,7 @@ import org.eclipse.aether.resolution.ArtifactResult; import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter; import org.eclipse.aether.spi.io.PathProcessor; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor; import org.eclipse.aether.util.ConfigUtils; import org.slf4j.Logger; 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 8116d70b90..8e77aab215 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 @@ -36,7 +36,6 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.MetadataResolver; import org.eclipse.aether.impl.RemoteRepositoryManager; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.internal.impl.filter.prefixes.PrefixesSource; import org.eclipse.aether.internal.impl.filter.ruletree.PrefixTree; import org.eclipse.aether.metadata.DefaultMetadata; @@ -48,6 +47,7 @@ import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter; import org.eclipse.aether.spi.connector.layout.RepositoryLayout; import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.eclipse.aether.transfer.NoRepositoryLayoutException; import org.eclipse.aether.util.ConfigUtils; import org.slf4j.Logger; 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 8663f83e5b..e97900d11b 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 @@ -24,11 +24,11 @@ import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.internal.impl.checksum.FileTrustedChecksumsSourceSupport; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter; import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.eclipse.aether.util.DirectoryUtils; import static java.util.Objects.requireNonNull; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/RepositoryKeyFunctionFactory.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/remoterepo/RepositoryKeyFunctionFactory.java similarity index 93% rename from maven-resolver-impl/src/main/java/org/eclipse/aether/impl/RepositoryKeyFunctionFactory.java rename to maven-resolver-spi/src/main/java/org/eclipse/aether/spi/remoterepo/RepositoryKeyFunctionFactory.java index 72354bb306..d537a10f6b 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/RepositoryKeyFunctionFactory.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/remoterepo/RepositoryKeyFunctionFactory.java @@ -16,16 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -package org.eclipse.aether.impl; +package org.eclipse.aether.spi.remoterepo; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.util.repository.RepositoryKeyFunction; +import org.eclipse.aether.repository.RepositoryKeyFunction; /** * A factory to create {@link RepositoryKeyFunction} instances. * * @since 2.0.14 - * @provisional This type is provisional and can be changed, moved or removed without prior notice. */ public interface RepositoryKeyFunctionFactory { /** diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/remoterepo/package-info.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/remoterepo/package-info.java new file mode 100644 index 0000000000..0a53f82350 --- /dev/null +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/remoterepo/package-info.java @@ -0,0 +1,23 @@ +// CHECKSTYLE_OFF: RegexpHeader +/* + * 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. + */ +/** + * The contract for remote repository customizations. + */ +package org.eclipse.aether.spi.remoterepo; diff --git a/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java b/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java index d2c379b724..26abebd3e6 100644 --- a/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java +++ b/maven-resolver-supplier-mvn3/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java @@ -51,7 +51,6 @@ import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.impl.RepositoryConnectorProvider; import org.eclipse.aether.impl.RepositoryEventDispatcher; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.impl.RepositorySystemValidator; import org.eclipse.aether.impl.UpdateCheckManager; @@ -140,6 +139,7 @@ import org.eclipse.aether.spi.io.ChecksumProcessor; import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor; import org.eclipse.aether.spi.synccontext.SyncContextFactory; import org.eclipse.aether.spi.validator.ValidatorFactory; diff --git a/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java b/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java index 01b2f81e05..870b6b1c81 100644 --- a/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java +++ b/maven-resolver-supplier-mvn4/src/main/java/org/eclipse/aether/supplier/RepositorySystemSupplier.java @@ -55,7 +55,6 @@ import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.impl.RepositoryConnectorProvider; import org.eclipse.aether.impl.RepositoryEventDispatcher; -import org.eclipse.aether.impl.RepositoryKeyFunctionFactory; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.impl.RepositorySystemValidator; import org.eclipse.aether.impl.UpdateCheckManager; @@ -144,6 +143,7 @@ import org.eclipse.aether.spi.io.ChecksumProcessor; import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; +import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor; import org.eclipse.aether.spi.synccontext.SyncContextFactory; import org.eclipse.aether.spi.validator.ValidatorFactory; diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index c9719f51c5..6cacbb707c 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java @@ -26,6 +26,7 @@ import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.RepositoryKeyFunction; import org.eclipse.aether.util.PathUtils; import org.eclipse.aether.util.StringDigestUtil; From a819319d36ab782845c63354634da49aef65431d Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 28 Nov 2025 20:39:02 +0100 Subject: [PATCH 27/28] Add UT More as a showcase --- .../repository/RepositoryIdHelperTest.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java diff --git a/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java b/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java new file mode 100644 index 0000000000..709ad1829f --- /dev/null +++ b/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java @@ -0,0 +1,100 @@ +/* + * 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.eclipse.aether.util.repository; + +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.RepositoryKeyFunction; +import org.eclipse.aether.repository.RepositoryPolicy; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class RepositoryIdHelperTest { + private final RemoteRepository central = new RemoteRepository.Builder( + "central", "default", "https://repo.maven.apache.org/maven2/") + .setSnapshotPolicy(new RepositoryPolicy(false, null, null)) + .build(); + private final RemoteRepository central_legacy = new RemoteRepository.Builder( + "central", "default", "https://repo1.maven.org/maven2/") + .setSnapshotPolicy(new RepositoryPolicy(false, null, null)) + .build(); + private final RemoteRepository central_trivial = new RemoteRepository.Builder( + "central", "default", "https://repo1.maven.org/maven2/") + .build(); + private final RemoteRepository central_mirror = new RemoteRepository.Builder( + "my-mirror", "default", "https://mymrm.com/maven/") + .setSnapshotPolicy(new RepositoryPolicy(false, null, null)) + .setMirroredRepositories(Collections.singletonList(central)) + .build(); + private final RemoteRepository asf_snapshots = new RemoteRepository.Builder( + "apache-snapshots", "default", "https://repository.apache.org/content/repositories/snapshots/") + .setReleasePolicy(new RepositoryPolicy(false, null, null)) + .build(); + private final RemoteRepository file_unfriendly = new RemoteRepository.Builder( + "apache/snapshots", "default", "https://repository.apache.org/content/repositories/snapshots/") + .setReleasePolicy(new RepositoryPolicy(false, null, null)) + .build(); + + @Test + void simple() { + RepositoryKeyFunction func = RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.SIMPLE.name()); + assertEquals("central", func.apply(central, null)); + assertEquals("central", func.apply(central_legacy, null)); + assertEquals("central", func.apply(central_trivial, null)); + assertEquals("my-mirror", func.apply(central_mirror, null)); + assertEquals("apache-snapshots", func.apply(asf_snapshots, null)); + assertEquals("apache-SLASH-snapshots", func.apply(file_unfriendly, null)); + } + + @Test + void nid() { + RepositoryKeyFunction func = RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.NID.name()); + assertEquals("central", func.apply(central, null)); + assertEquals("central", func.apply(central_legacy, null)); + assertEquals("central", func.apply(central_trivial, null)); + assertEquals("my-mirror", func.apply(central_mirror, null)); + assertEquals("apache-snapshots", func.apply(asf_snapshots, null)); + assertEquals("apache-SLASH-snapshots", func.apply(file_unfriendly, null)); + } + + @Test + void nidHurl() { + RepositoryKeyFunction func = RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.NID_HURL.name()); + assertEquals("central-0aeeb43004cebeccad6fdf0fec27084167d5880a", func.apply(central, null)); + assertEquals("central-a27bb55260d64d6035671716555d10644054c89d", func.apply(central_legacy, null)); + assertEquals("central-a27bb55260d64d6035671716555d10644054c89d", func.apply(central_trivial, null)); + assertEquals("my-mirror-eb106d0adc4a56b55067f069a2fed5526fd6cb18", func.apply(central_mirror, null)); + assertEquals("apache-snapshots-5c4f89479e3c71fb3c2fbc6213fb00f6371fbb96", func.apply(asf_snapshots, null)); + assertEquals("apache-SLASH-snapshots-5c4f89479e3c71fb3c2fbc6213fb00f6371fbb96", func.apply(file_unfriendly, null)); + } + + @Test + void ngurk() { + RepositoryKeyFunction func = RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.NGURK.name()); + assertEquals("central-ff5deec948d038ceb880e13e9f61455903b0d0a6", func.apply(central, null)); + assertEquals("central-ffb5c2a34e47c429571fc29752730e9ce6e44d79", func.apply(central_legacy, null)); + assertEquals("central-acc6c84ca8674036eda6708502b5f02fb09a9731", func.apply(central_trivial, null)); + assertEquals("my-mirror-256631324003f5718aca1e80db8377c7f9ecd852", func.apply(central_mirror, null)); + assertEquals("apache-snapshots-62375dea6c3c8bebdbae5cca79a4f5ad2eaebf34", func.apply(asf_snapshots, null)); + assertEquals("apache-SLASH-snapshots-2e126ec79795c077a3c42dc536fa28c13c3bdb0d", func.apply(file_unfriendly, null)); + } +} From 4ba9473690d0a05cd5f15fdf40d547cea8f8c095 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 28 Nov 2025 20:41:33 +0100 Subject: [PATCH 28/28] Reformat --- .../repository/RepositoryIdHelperTest.java | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java b/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java index 709ad1829f..74f5b73b53 100644 --- a/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java +++ b/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java @@ -18,14 +18,13 @@ */ package org.eclipse.aether.util.repository; +import java.util.Collections; + import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RepositoryKeyFunction; import org.eclipse.aether.repository.RepositoryPolicy; import org.junit.jupiter.api.Test; -import java.util.Arrays; -import java.util.Collections; - import static org.junit.jupiter.api.Assertions.assertEquals; public class RepositoryIdHelperTest { @@ -37,11 +36,10 @@ public class RepositoryIdHelperTest { "central", "default", "https://repo1.maven.org/maven2/") .setSnapshotPolicy(new RepositoryPolicy(false, null, null)) .build(); - private final RemoteRepository central_trivial = new RemoteRepository.Builder( - "central", "default", "https://repo1.maven.org/maven2/") - .build(); + private final RemoteRepository central_trivial = + new RemoteRepository.Builder("central", "default", "https://repo1.maven.org/maven2/").build(); private final RemoteRepository central_mirror = new RemoteRepository.Builder( - "my-mirror", "default", "https://mymrm.com/maven/") + "my-mirror", "default", "https://mymrm.com/maven/") .setSnapshotPolicy(new RepositoryPolicy(false, null, null)) .setMirroredRepositories(Collections.singletonList(central)) .build(); @@ -50,13 +48,14 @@ public class RepositoryIdHelperTest { .setReleasePolicy(new RepositoryPolicy(false, null, null)) .build(); private final RemoteRepository file_unfriendly = new RemoteRepository.Builder( - "apache/snapshots", "default", "https://repository.apache.org/content/repositories/snapshots/") + "apache/snapshots", "default", "https://repository.apache.org/content/repositories/snapshots/") .setReleasePolicy(new RepositoryPolicy(false, null, null)) .build(); @Test void simple() { - RepositoryKeyFunction func = RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.SIMPLE.name()); + RepositoryKeyFunction func = + RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.SIMPLE.name()); assertEquals("central", func.apply(central, null)); assertEquals("central", func.apply(central_legacy, null)); assertEquals("central", func.apply(central_trivial, null)); @@ -67,7 +66,8 @@ void simple() { @Test void nid() { - RepositoryKeyFunction func = RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.NID.name()); + RepositoryKeyFunction func = + RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.NID.name()); assertEquals("central", func.apply(central, null)); assertEquals("central", func.apply(central_legacy, null)); assertEquals("central", func.apply(central_trivial, null)); @@ -78,23 +78,27 @@ void nid() { @Test void nidHurl() { - RepositoryKeyFunction func = RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.NID_HURL.name()); + RepositoryKeyFunction func = + RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.NID_HURL.name()); assertEquals("central-0aeeb43004cebeccad6fdf0fec27084167d5880a", func.apply(central, null)); assertEquals("central-a27bb55260d64d6035671716555d10644054c89d", func.apply(central_legacy, null)); assertEquals("central-a27bb55260d64d6035671716555d10644054c89d", func.apply(central_trivial, null)); assertEquals("my-mirror-eb106d0adc4a56b55067f069a2fed5526fd6cb18", func.apply(central_mirror, null)); assertEquals("apache-snapshots-5c4f89479e3c71fb3c2fbc6213fb00f6371fbb96", func.apply(asf_snapshots, null)); - assertEquals("apache-SLASH-snapshots-5c4f89479e3c71fb3c2fbc6213fb00f6371fbb96", func.apply(file_unfriendly, null)); + assertEquals( + "apache-SLASH-snapshots-5c4f89479e3c71fb3c2fbc6213fb00f6371fbb96", func.apply(file_unfriendly, null)); } @Test void ngurk() { - RepositoryKeyFunction func = RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.NGURK.name()); + RepositoryKeyFunction func = + RepositoryIdHelper.getRepositoryKeyFunction(RepositoryIdHelper.RepositoryKeyType.NGURK.name()); assertEquals("central-ff5deec948d038ceb880e13e9f61455903b0d0a6", func.apply(central, null)); assertEquals("central-ffb5c2a34e47c429571fc29752730e9ce6e44d79", func.apply(central_legacy, null)); assertEquals("central-acc6c84ca8674036eda6708502b5f02fb09a9731", func.apply(central_trivial, null)); assertEquals("my-mirror-256631324003f5718aca1e80db8377c7f9ecd852", func.apply(central_mirror, null)); assertEquals("apache-snapshots-62375dea6c3c8bebdbae5cca79a4f5ad2eaebf34", func.apply(asf_snapshots, null)); - assertEquals("apache-SLASH-snapshots-2e126ec79795c077a3c42dc536fa28c13c3bdb0d", func.apply(file_unfriendly, null)); + assertEquals( + "apache-SLASH-snapshots-2e126ec79795c077a3c42dc536fa28c13c3bdb0d", func.apply(file_unfriendly, null)); } }