This is an automated email from the ASF dual-hosted git repository.
cstamas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git
The following commit(s) were added to refs/heads/master by this push:
new 79b6d5f2e Repository Key Function SPI (#1679)
79b6d5f2e is described below
commit 79b6d5f2ee1472a4aaf3224b7670f3c55b8dace2
Author: Tamas Cservenak <[email protected]>
AuthorDate: Mon Dec 1 17:31:39 2025 +0100
Repository Key Function SPI (#1679)
New opt-in **experimental** feature for enhanced local repository: ability
to choose "repository key" function. Local repository (simple and enhanced) by
default uses the `simple` key (historically) where other places all used `nid`.
Have to note, that `simple` is technically equivalent to `nid` as
`RemoteRepository.isRepositoryManager()` is not set anywhere in Maven.
Added key functions:
* `simple` -> the original code in LRM
* `nid` -> `norm(id)` (path friendly)
* `hurl` -> `sha1(url)`
* `nid_hurl` -> `norm(id)-sha1(url)`
* `gurk` -> `norm(id)-sha1(seed)` where "seed" is all config properties of
repo
* `ngurk` -> `norm(id)-sha1(seed)` where "seed" is all config sans mirror
list (just the string "isMirrored" is added for mirrors)
Introduced `RepositoryKeyFunction` type, that is used consistently in
enhanced local repository (availability calculation), prefix composer (split
repository prefix calculation) and remote repository manager (remote repository
consolidation). This is new SPI, and impl provides default implementation with
those above.
---
.../eclipse/aether/ConfigurationProperties.java | 31 ++-
.../eclipse/aether/repository/LocalRepository.java | 18 +-
.../aether/repository/RepositoryKeyFunction.java | 40 +++
.../aether/repository/WorkspaceRepository.java | 12 +-
.../maven/resolver/examples/resolver/Resolver.java | 3 +-
.../resolver/examples/resolver/ResolverDemo.java | 12 +-
.../DefaultLocalPathPrefixComposerFactory.java | 22 +-
.../impl/DefaultRemoteRepositoryManager.java | 31 ++-
.../impl/DefaultRepositoryKeyFunctionFactory.java | 88 ++++++
.../impl/EnhancedLocalRepositoryManager.java | 7 +-
.../EnhancedLocalRepositoryManagerFactory.java | 10 +-
.../LocalPathPrefixComposerFactorySupport.java | 18 +-
.../impl/SimpleLocalRepositoryManager.java | 44 +--
.../impl/SimpleLocalRepositoryManagerFactory.java | 14 +-
.../FileTrustedChecksumsSourceSupport.java | 44 ++-
.../SparseDirectoryTrustedChecksumsSource.java | 14 +-
.../SummaryFileTrustedChecksumsSource.java | 16 +-
.../GroupIdRemoteRepositoryFilterSource.java | 11 +-
.../PrefixesRemoteRepositoryFilterSource.java | 9 +-
.../RemoteRepositoryFilterSourceSupport.java | 38 +++
.../DefaultLocalPathPrefixComposerFactoryTest.java | 12 +-
.../impl/DefaultRemoteRepositoryManagerTest.java | 6 +-
.../internal/impl/DefaultRepositorySystemTest.java | 4 +-
.../impl/EnhancedLocalRepositoryManagerTest.java | 7 +-
.../EnhancedSplitLocalRepositoryManagerTest.java | 7 +-
.../impl/SimpleLocalRepositoryManagerTest.java | 4 +-
.../SparseDirectoryTrustedChecksumsSourceTest.java | 5 +-
.../SummaryFileTrustedChecksumsSourceTest.java | 6 +-
.../GroupIdRemoteRepositoryFilterSourceTest.java | 5 +-
.../PrefixesRemoteRepositoryFilterSourceTest.java | 6 +-
.../remoterepo/RepositoryKeyFunctionFactory.java | 56 ++++
.../aether/spi/remoterepo/package-info.java | 23 ++
.../aether/supplier/RepositorySystemSupplier.java | 46 ++-
.../aether/supplier/RepositorySystemSupplier.java | 46 ++-
.../aether/util/repository/RepositoryIdHelper.java | 309 ++++++++++++++-------
.../util/repository/RepositoryIdHelperTest.java | 110 +++++---
src/site/markdown/configuration.md | 3 +
src/site/markdown/repository-key-function.md | 123 ++++++++
src/site/site.xml | 1 +
39 files changed, 958 insertions(+), 303 deletions(-)
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 327815fdb..3945a7486 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 + "<class>";
- /**
- * 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;
+ /**
+ * <b>Experimental:</b> 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. <em>Warning:</em>
+ * 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-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java
b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/LocalRepository.java
index 44ae964b0..6c586c572 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,19 +31,25 @@ import java.util.Objects;
* the repository.
*/
public final class LocalRepository implements ArtifactRepository {
+ public static final String ID = "local";
private final Path basePath;
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);
}
/**
@@ -99,6 +105,7 @@ public final class LocalRepository implements
ArtifactRepository {
public LocalRepository(Path basePath, String type) {
this.basePath = basePath;
this.type = (type != null) ? type : "";
+ this.hashCode = Objects.hash(this.basePath, this.type);
}
@Override
@@ -108,7 +115,7 @@ public final class LocalRepository implements
ArtifactRepository {
@Override
public String getId() {
- return "local";
+ return ID;
}
/**
@@ -153,13 +160,6 @@ public final class LocalRepository implements
ArtifactRepository {
@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/RepositoryKeyFunction.java
b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RepositoryKeyFunction.java
new file mode 100644
index 000000000..e44f7cdc9
--- /dev/null
+++
b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RepositoryKeyFunction.java
@@ -0,0 +1,40 @@
+/*
+ * 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.repository;
+
+import java.util.function.BiFunction;
+
+/**
+ * The repository key function, it produces keys (strings) for given {@link
RemoteRepository} instances.
+ *
+ * @since 2.0.14
+ */
+@FunctionalInterface
+public interface RepositoryKeyFunction extends BiFunction<RemoteRepository,
String, String> {
+ /**
+ * 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);
+}
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 e128af209..9b0b44021 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;
/**
@@ -27,11 +28,14 @@ import java.util.UUID;
* 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;
private final Object key;
+ private final int hashCode;
+
/**
* Creates a new workspace repository of type {@code "workspace"} and a
random key.
*/
@@ -58,6 +62,7 @@ public final class WorkspaceRepository implements
ArtifactRepository {
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() {
@@ -65,7 +70,7 @@ public final class WorkspaceRepository implements
ArtifactRepository {
}
public String getId() {
- return "workspace";
+ return ID;
}
/**
@@ -99,9 +104,6 @@ public final class WorkspaceRepository implements
ArtifactRepository {
@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 b44f4011b..8b5e308f7 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 @@ package org.apache.maven.resolver.examples.resolver;
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 7c0e70a46..eb8193bbd 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 class ResolverDemo {
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 class ResolverDemo {
}
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 class ResolverDemo {
}
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 2a18ce049..022400667 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,14 +18,15 @@
*/
package org.eclipse.aether.internal.impl;
+import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
-import java.util.function.Function;
-
import org.eclipse.aether.RepositorySystemSession;
-import org.eclipse.aether.repository.ArtifactRepository;
-import org.eclipse.aether.util.repository.RepositoryIdHelper;
+import org.eclipse.aether.repository.RepositoryKeyFunction;
+import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory;
+
+import static java.util.Objects.requireNonNull;
/**
* Default local path prefix composer factory: it fully reuses {@link
LocalPathPrefixComposerFactorySupport} class
@@ -36,6 +37,13 @@ import org.eclipse.aether.util.repository.RepositoryIdHelper;
@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(
@@ -48,7 +56,7 @@ public final class DefaultLocalPathPrefixComposerFactory
extends LocalPathPrefix
isSplitRemoteRepositoryLast(session),
getReleasesPrefix(session),
getSnapshotsPrefix(session),
- RepositoryIdHelper.cachedIdToPathSegment(session));
+
repositoryKeyFunctionFactory.systemRepositoryKeyFunction(session));
}
/**
@@ -66,7 +74,7 @@ public final class DefaultLocalPathPrefixComposerFactory
extends LocalPathPrefix
boolean splitRemoteRepositoryLast,
String releasesPrefix,
String snapshotsPrefix,
- Function<ArtifactRepository, String> idToPathSegmentFunction) {
+ RepositoryKeyFunction repositoryKeyFunction) {
super(
split,
localPrefix,
@@ -77,7 +85,7 @@ public final class DefaultLocalPathPrefixComposerFactory
extends LocalPathPrefix
splitRemoteRepositoryLast,
releasesPrefix,
snapshotsPrefix,
- idToPathSegmentFunction);
+ repositoryKeyFunction);
}
}
}
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 3ad72aade..fbe35695a 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
@@ -38,8 +38,10 @@ import org.eclipse.aether.repository.MirrorSelector;
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.spi.remoterepo.RepositoryKeyFunctionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -82,11 +84,17 @@ public class DefaultRemoteRepositoryManager implements
RemoteRepositoryManager {
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
@@ -102,6 +110,7 @@ public class DefaultRemoteRepositoryManager implements
RemoteRepositoryManager {
return dominantRepositories;
}
+ RepositoryKeyFunction repositoryKeyFunction =
repositoryKeyFunctionFactory.systemRepositoryKeyFunction(session);
MirrorSelector mirrorSelector = session.getMirrorSelector();
AuthenticationSelector authSelector =
session.getAuthenticationSelector();
ProxySelector proxySelector = session.getProxySelector();
@@ -121,15 +130,16 @@ public class DefaultRemoteRepositoryManager implements
RemoteRepositoryManager {
}
}
- String key = getKey(repository);
+ String key = repositoryKeyFunction.apply(repository, null);
for (ListIterator<RemoteRepository> 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 +198,20 @@ public class DefaultRemoteRepositoryManager implements
RemoteRepositoryManager {
original.getUrl());
}
- private String getKey(RemoteRepository repository) {
- return repository.getId();
- }
-
private RemoteRepository mergeMirrors(
- RepositorySystemSession session, RemoteRepository dominant,
RemoteRepository recessive) {
+ RepositorySystemSession session,
+ RepositoryKeyFunction 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/DefaultRepositoryKeyFunctionFactory.java
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositoryKeyFunctionFactory.java
new file mode 100644
index 000000000..4f7a70621
--- /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.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 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<RemoteRepository,
ConcurrentMap<String, String>>)
+ 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/EnhancedLocalRepositoryManager.java
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java
index e55150f3f..dd314fed9 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,16 +27,15 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
-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;
import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.repository.RepositoryKeyFunction;
import static java.util.Objects.requireNonNull;
@@ -72,11 +71,11 @@ class EnhancedLocalRepositoryManager extends
SimpleLocalRepositoryManager {
EnhancedLocalRepositoryManager(
Path basedir,
LocalPathComposer localPathComposer,
- Function<ArtifactRepository, String> idToPathSegmentFunction,
+ RepositoryKeyFunction 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 b00f1dff9..e31a493c9 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
@@ -28,8 +28,8 @@ 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 org.eclipse.aether.util.repository.RepositoryIdHelper;
import static java.util.Objects.requireNonNull;
@@ -66,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
@@ -94,7 +98,7 @@ public class EnhancedLocalRepositoryManagerFactory implements
LocalRepositoryMan
return new EnhancedLocalRepositoryManager(
repository.getBasePath(),
localPathComposer,
- RepositoryIdHelper.cachedIdToPathSegment(session),
+
repositoryKeyFunctionFactory.systemRepositoryKeyFunction(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 b86223b53..a01b377c8 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,11 @@
*/
package org.eclipse.aether.internal.impl;
-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.repository.RepositoryKeyFunction;
import org.eclipse.aether.util.ConfigUtils;
/**
@@ -244,7 +242,7 @@ public abstract class LocalPathPrefixComposerFactorySupport
implements LocalPath
protected final String snapshotsPrefix;
- protected final Function<ArtifactRepository, String>
idToPathSegmentFunction;
+ protected final RepositoryKeyFunction repositoryKeyFunction;
protected LocalPathPrefixComposerSupport(
boolean split,
@@ -256,7 +254,7 @@ public abstract class LocalPathPrefixComposerFactorySupport
implements LocalPath
boolean splitRemoteRepositoryLast,
String releasesPrefix,
String snapshotsPrefix,
- Function<ArtifactRepository, String> idToPathSegmentFunction) {
+ RepositoryKeyFunction repositoryKeyFunction) {
this.split = split;
this.localPrefix = localPrefix;
this.splitLocal = splitLocal;
@@ -266,7 +264,7 @@ public abstract class LocalPathPrefixComposerFactorySupport
implements LocalPath
this.splitRemoteRepositoryLast = splitRemoteRepositoryLast;
this.releasesPrefix = releasesPrefix;
this.snapshotsPrefix = snapshotsPrefix;
- this.idToPathSegmentFunction = idToPathSegmentFunction;
+ this.repositoryKeyFunction = repositoryKeyFunction;
}
@Override
@@ -288,13 +286,13 @@ public abstract class
LocalPathPrefixComposerFactorySupport implements LocalPath
}
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 +316,13 @@ public abstract class
LocalPathPrefixComposerFactorySupport implements LocalPath
}
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 ea751d41a..0acf1b374 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,14 +21,10 @@ package org.eclipse.aether.internal.impl;
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.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;
@@ -38,7 +34,7 @@ import org.eclipse.aether.repository.LocalMetadataResult;
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 org.eclipse.aether.repository.RepositoryKeyFunction;
import static java.util.Objects.requireNonNull;
@@ -51,17 +47,17 @@ class SimpleLocalRepositoryManager implements
LocalRepositoryManager {
private final LocalPathComposer localPathComposer;
- private final Function<ArtifactRepository, String> idToPathSegmentFunction;
+ private final RepositoryKeyFunction repositoryKeyFunction;
SimpleLocalRepositoryManager(
Path basePath,
String type,
LocalPathComposer localPathComposer,
- Function<ArtifactRepository, String> idToPathSegmentFunction) {
+ RepositoryKeyFunction 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
@@ -85,7 +81,7 @@ class SimpleLocalRepositoryManager implements
LocalRepositoryManager {
@Override
public String getPathForLocalMetadata(Metadata metadata) {
requireNonNull(metadata, "metadata cannot be null");
- return localPathComposer.getPathForMetadata(metadata, "local");
+ return localPathComposer.getPathForMetadata(metadata,
LocalRepository.ID);
}
@Override
@@ -101,35 +97,7 @@ class SimpleLocalRepositoryManager implements
LocalRepositoryManager {
* 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<String> 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 c1e4ccd21..f45460852 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
@@ -27,6 +27,7 @@ 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;
@@ -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,11 @@ public class SimpleLocalRepositoryManagerFactory implements
LocalRepositoryManag
repository.getBasePath(),
"simple",
localPathComposer,
- RepositoryIdHelper.cachedIdToPathSegment(session));
+ repositoryKeyFunctionFactory.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/checksum/FileTrustedChecksumsSourceSupport.java
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java
index fc417884c..8f052b92b 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
@@ -28,8 +28,10 @@ import org.eclipse.aether.ConfigurationProperties;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
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;
@@ -53,10 +55,31 @@ import static java.util.Objects.requireNonNull;
*
* @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.";
+ /**
+ * <b>Experimental:</b> Configuration for "repository key" function.
+ * Note: repository key functions other than "nid" produce repository keys
will be <em>way different
+ * that those produced with previous versions or without this option
enabled</em>. 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";
+
+ 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".
@@ -131,4 +154,23 @@ abstract class FileTrustedChecksumsSourceSupport
implements TrustedChecksumsSour
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 repositoryKeyFunctionFactory
+ .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 1d6af287b..0456b3f17 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
@@ -37,8 +37,8 @@ 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.eclipse.aether.util.repository.RepositoryIdHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -106,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);
}
@@ -132,10 +135,7 @@ public final class SparseDirectoryTrustedChecksumsSource
extends FileTrustedChec
if (Files.isDirectory(basedir)) {
for (ChecksumAlgorithmFactory checksumAlgorithmFactory :
checksumAlgorithmFactories) {
Path checksumPath = basedir.resolve(calculateArtifactPath(
- originAware,
- artifact,
-
RepositoryIdHelper.cachedIdToPathSegment(session).apply(artifactRepository),
- checksumAlgorithmFactory));
+ originAware, artifact, repositoryKey(session,
artifactRepository), checksumAlgorithmFactory));
if (!Files.isRegularFile(checksumPath)) {
LOGGER.debug(
@@ -167,7 +167,7 @@ public final class SparseDirectoryTrustedChecksumsSource
extends FileTrustedChec
return new SparseDirectoryWriter(
getBasedir(session, LOCAL_REPO_PREFIX_DIR,
CONFIG_PROP_BASEDIR, true),
isOriginAware(session),
- RepositoryIdHelper.cachedIdToPathSegment(session));
+ 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 85c9be19f..f7f451546 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
@@ -46,8 +46,8 @@ 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.eclipse.aether.util.repository.RepositoryIdHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -142,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);
@@ -177,7 +179,7 @@ public final class SummaryFileTrustedChecksumsSource
extends FileTrustedChecksum
Path summaryFile = summaryFile(
basedir,
originAware,
-
RepositoryIdHelper.cachedIdToPathSegment(session).apply(artifactRepository),
+ repositoryKey(session, artifactRepository),
checksumAlgorithmFactory.getFileExtension());
ConcurrentHashMap<String, String> algorithmChecksums =
checksums.computeIfAbsent(summaryFile, f ->
loadProvidedChecksums(summaryFile));
@@ -199,7 +201,7 @@ public final class SummaryFileTrustedChecksumsSource
extends FileTrustedChecksum
checksums,
getBasedir(session, LOCAL_REPO_PREFIX_DIR,
CONFIG_PROP_BASEDIR, true),
isOriginAware(session),
- RepositoryIdHelper.cachedIdToPathSegment(session));
+ r -> repositoryKey(session, r));
}
/**
@@ -263,17 +265,17 @@ public final class SummaryFileTrustedChecksumsSource
extends FileTrustedChecksum
private final boolean originAware;
- private final Function<ArtifactRepository, String>
idToPathSegmentFunction;
+ private final Function<ArtifactRepository, String>
repositoryKeyFunction;
private SummaryFileWriter(
ConcurrentHashMap<Path, ConcurrentHashMap<String, String>>
cache,
Path basedir,
boolean originAware,
- Function<ArtifactRepository, String> idToPathSegmentFunction) {
+ Function<ArtifactRepository, String> repositoryKeyFunction) {
this.cache = cache;
this.basedir = basedir;
this.originAware = originAware;
- this.idToPathSegmentFunction = idToPathSegmentFunction;
+ this.repositoryKeyFunction = repositoryKeyFunction;
}
@Override
@@ -287,7 +289,7 @@ public final class SummaryFileTrustedChecksumsSource
extends FileTrustedChecksum
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 3d6643876..32be14c79 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
@@ -48,9 +48,9 @@ import org.eclipse.aether.repository.RemoteRepository;
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.eclipse.aether.util.repository.RepositoryIdHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -157,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);
}
@@ -248,9 +251,7 @@ public final class GroupIdRemoteRepositoryFilterSource
extends RemoteRepositoryF
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
- +
RepositoryIdHelper.cachedIdToPathSegment(session).apply(remoteRepository)
- + 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 9a4b2c301..8e77aab21 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
@@ -47,9 +47,9 @@ import
org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
+import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory;
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;
@@ -197,9 +197,11 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
@Inject
public PrefixesRemoteRepositoryFilterSource(
+ RepositoryKeyFunctionFactory repositoryKeyFunctionFactory,
Supplier<MetadataResolver> metadataResolver,
Supplier<RemoteRepositoryManager> remoteRepositoryManager,
RepositoryLayoutProvider repositoryLayoutProvider) {
+ super(repositoryKeyFunctionFactory);
this.metadataResolver = requireNonNull(metadataResolver);
this.remoteRepositoryManager = requireNonNull(remoteRepositoryManager);
this.repositoryLayoutProvider =
requireNonNull(repositoryLayoutProvider);
@@ -318,9 +320,8 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
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 + 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 438c5eaa8..e97900d11 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,9 +24,11 @@ import java.nio.file.Path;
import org.eclipse.aether.ConfigurationProperties;
import org.eclipse.aether.RepositorySystemSession;
+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;
@@ -52,6 +54,27 @@ public abstract class RemoteRepositoryFilterSourceSupport
implements RemoteRepos
protected static final String CONFIG_PROPS_PREFIX =
ConfigurationProperties.PREFIX_AETHER + "remoteRepositoryFilter.";
+ /**
+ * <b>Experimental:</b> Configuration for "repository key" function.
+ * Note: repository key functions other than "nid" produce repository keys
will be <em>way different
+ * that those produced with previous versions or without this option
enabled</em>. 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";
+
+ 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}.
* <p>
@@ -88,6 +111,21 @@ public abstract class RemoteRepositoryFilterSourceSupport
implements RemoteRepos
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 repositoryKeyFunctionFactory
+ .repositoryKeyFunction(
+ FileTrustedChecksumsSourceSupport.class,
+ session,
+ DEFAULT_REPOSITORY_KEY_FUNCTION,
+ CONFIG_PROP_REPOSITORY_KEY_FUNCTION)
+ .apply(repository, null);
+ }
+
/**
* Simple {@link RemoteRepositoryFilter.Result} immutable implementation.
*/
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 0a47ac7ae..8f6c53452 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 @@ public class DefaultLocalPathPrefixComposerFactoryTest {
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 @@ public class DefaultLocalPathPrefixComposerFactoryTest {
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 @@ public class DefaultLocalPathPrefixComposerFactoryTest {
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 20ec8462e..9b51a2db5 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 @@ public class DefaultRemoteRepositoryManagerTest {
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 08b75df3f..4fa1953d5 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 @@ public class DefaultRepositorySystemTest {
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 7d8a296cc..2e3468dc6 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.internal.test.util.TestUtils;
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,10 +108,11 @@ public class EnhancedLocalRepositoryManagerTest {
return new EnhancedLocalRepositoryManager(
basedir.toPath(),
new DefaultLocalPathComposer(),
- ArtifactRepository::getId,
+ 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 4a78ea302..538c7afe0 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 @@ package org.eclipse.aether.internal.impl;
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,10 +34,11 @@ public class EnhancedSplitLocalRepositoryManagerTest
extends EnhancedLocalReposi
return new EnhancedLocalRepositoryManager(
basedir.toPath(),
new DefaultLocalPathComposer(),
- ArtifactRepository::getId,
+ 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/SimpleLocalRepositoryManagerTest.java
b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerTest.java
index fbe240992..5bd674136 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.Artifact;
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-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 83e812a57..2053ee489 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.impl.RepositorySystemLifecycle;
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 2646d783c..6d31e5da8 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.DefaultRepositorySystemSession;
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 4a60622ad..6bcbdaaf9 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 java.util.List;
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 7d7c21782..5db48616f 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.artifact.Artifact;
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 class PrefixesRemoteRepositoryFilterSourceTest
extends RemoteRepositoryFi
new Maven2RepositoryLayoutFactory(
checksumsSelector(), new
DefaultArtifactPredicateFactory(checksumsSelector()))));
return new PrefixesRemoteRepositoryFilterSource(
- () -> metadataResolver, () -> remoteRepositoryManager,
layoutProvider);
+ new DefaultRepositoryKeyFunctionFactory(),
+ () -> metadataResolver,
+ () -> remoteRepositoryManager,
+ layoutProvider);
}
@Override
diff --git
a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/remoterepo/RepositoryKeyFunctionFactory.java
b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/remoterepo/RepositoryKeyFunctionFactory.java
new file mode 100644
index 000000000..d537a10f6
--- /dev/null
+++
b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/remoterepo/RepositoryKeyFunctionFactory.java
@@ -0,0 +1,56 @@
+/*
+ * 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.spi.remoterepo;
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.repository.RepositoryKeyFunction;
+
+/**
+ * A factory to create {@link RepositoryKeyFunction} instances.
+ *
+ * @since 2.0.14
+ */
+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-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 000000000..0a53f8235
--- /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 c104ebc45..26abebd3e 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
@@ -72,6 +72,7 @@ import org.eclipse.aether.internal.impl.DefaultPathProcessor;
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;
@@ -138,6 +139,7 @@ import
org.eclipse.aether.spi.connector.transport.http.ChecksumExtractorStrategy
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;
@@ -247,7 +249,7 @@ public class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
}
protected LocalPathPrefixComposerFactory
createLocalPathPrefixComposerFactory() {
- return new DefaultLocalPathPrefixComposerFactory();
+ return new
DefaultLocalPathPrefixComposerFactory(getRepositoryKeyFunctionFactory());
}
private RepositorySystemLifecycle repositorySystemLifecycle;
@@ -321,6 +323,20 @@ public class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
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<String, NamedLockFactory> namedLockFactories;
public final Map<String, NamedLockFactory> getNamedLockFactories() {
@@ -484,13 +500,18 @@ public class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
protected LocalRepositoryProvider createLocalRepositoryProvider() {
LocalPathComposer localPathComposer = getLocalPathComposer();
+ RepositoryKeyFunctionFactory repositoryKeyFunctionFactory =
getRepositoryKeyFunctionFactory();
HashMap<String, LocalRepositoryManagerFactory>
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 class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
}
protected RemoteRepositoryManager createRemoteRepositoryManager() {
- return new DefaultRemoteRepositoryManager(getUpdatePolicyAnalyzer(),
getChecksumPolicyProvider());
+ return new DefaultRemoteRepositoryManager(
+ getUpdatePolicyAnalyzer(), getChecksumPolicyProvider(),
getRepositoryKeyFunctionFactory());
}
private Map<String, RemoteRepositoryFilterSource>
remoteRepositoryFilterSources;
@@ -522,11 +544,15 @@ public class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
HashMap<String, RemoteRepositoryFilterSource> 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 @@ public class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
HashMap<String, TrustedChecksumsSource> 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 0fc19c0e9..870b6b1c8 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
@@ -76,6 +76,7 @@ import org.eclipse.aether.internal.impl.DefaultPathProcessor;
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;
@@ -142,6 +143,7 @@ import
org.eclipse.aether.spi.connector.transport.http.ChecksumExtractorStrategy
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;
@@ -251,7 +253,7 @@ public class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
}
protected LocalPathPrefixComposerFactory
createLocalPathPrefixComposerFactory() {
- return new DefaultLocalPathPrefixComposerFactory();
+ return new
DefaultLocalPathPrefixComposerFactory(getRepositoryKeyFunctionFactory());
}
private RepositorySystemLifecycle repositorySystemLifecycle;
@@ -325,6 +327,20 @@ public class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
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<String, NamedLockFactory> namedLockFactories;
public final Map<String, NamedLockFactory> getNamedLockFactories() {
@@ -488,13 +504,18 @@ public class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
protected LocalRepositoryProvider createLocalRepositoryProvider() {
LocalPathComposer localPathComposer = getLocalPathComposer();
+ RepositoryKeyFunctionFactory repositoryKeyFunctionFactory =
getRepositoryKeyFunctionFactory();
HashMap<String, LocalRepositoryManagerFactory>
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 class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
}
protected RemoteRepositoryManager createRemoteRepositoryManager() {
- return new DefaultRemoteRepositoryManager(getUpdatePolicyAnalyzer(),
getChecksumPolicyProvider());
+ return new DefaultRemoteRepositoryManager(
+ getUpdatePolicyAnalyzer(), getChecksumPolicyProvider(),
getRepositoryKeyFunctionFactory());
}
private Map<String, RemoteRepositoryFilterSource>
remoteRepositoryFilterSources;
@@ -526,11 +548,15 @@ public class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
HashMap<String, RemoteRepositoryFilterSource> 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 @@ public class RepositorySystemSupplier implements
Supplier<RepositorySystem> {
HashMap<String, TrustedChecksumsSource> 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-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 5c8bbbbcd..6cacbb707 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,27 +18,26 @@
*/
package org.eclipse.aether.util.repository;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
+import java.util.ArrayList;
+import java.util.Comparator;
import java.util.Locale;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-import java.util.function.Predicate;
+import java.util.SortedSet;
+import java.util.TreeSet;
-import org.eclipse.aether.RepositorySystemSession;
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;
-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.
+ * <p>
+ * <em>Important:</em> multiple of these provided methods are not trivial
processing-wise, and some sort of
+ * caching is warmly recommended.
*
* @see PathUtils
* @since 2.0.11
@@ -46,103 +45,167 @@ import static java.util.Objects.requireNonNull;
public final class RepositoryIdHelper {
private RepositoryIdHelper() {}
- private static final String CENTRAL_REPOSITORY_ID = "central";
- private static final Collection<String> 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<RemoteRepository> 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();
+ /**
+ * Supported {@code repositoryKey} types.
+ *
+ * @since 2.0.14
+ */
+ public enum RepositoryKeyType {
+ /**
+ * The "simple" repository key, was default in Maven 3.
+ */
+ SIMPLE,
+ /**
+ * Crafts repository key using normalized {@link
RemoteRepository#getId()}.
+ */
+ 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).
+ */
+ NGURK,
+ /**
+ * Crafts unique repository key using {@link RemoteRepository#getId()}
and all the remaining properties of
+ * {@link RemoteRepository}.
+ */
+ GURK
+ }
/**
- * 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.
- * <p>
- * This method is costly, so should be invoked sparingly, or cache results
if needed.
- * <p>
- * <em>Important:</em>Do not use this method, or at least <em>do consider
when do you want to use it</em>, as it
- * totally disconnects repositories used in session. This method may be
used under some special circumstances
- * (ie reporting), but <em>must not be used within Resolver (and Maven)
session for "usual" resolution and
- * deployment use cases</em>.
+ * Selector method for {@link RepositoryKeyFunction} based on string
representation of {@link RepositoryKeyType}
+ * enum.
*/
- public static String remoteRepositoryUniqueId(RemoteRepository repository)
{
- if (CENTRAL_DIRECT_ONLY.test(repository)) {
- return CENTRAL_REPOSITORY_ID;
- } 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(");
- for (RemoteRepository mirroredRepo :
repository.getMirroredRepositories()) {
- buffer.append(remoteRepositoryUniqueId(mirroredRepo));
- }
- buffer.append(")");
+ public static RepositoryKeyFunction getRepositoryKeyFunction(String
keyTypeString) {
+ RepositoryKeyType keyType =
RepositoryKeyType.valueOf(keyTypeString.toUpperCase(Locale.ENGLISH));
+ switch (keyType) {
+ case SIMPLE:
+ return RepositoryIdHelper::simpleRepositoryKey;
+ case NID:
+ return RepositoryIdHelper::nidRepositoryKey;
+ case HURL:
+ return RepositoryIdHelper::hurlRepositoryKey;
+ case NID_HURL:
+ return RepositoryIdHelper::nidAndHurlRepositoryKey;
+ case NGURK:
+ return
RepositoryIdHelper::normalizedGloballyUniqueRepositoryKey;
+ 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
+ * unique identifier based on ID and current configuration of the remote
repository and context.
+ * <p>
+ * 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
+ **/
+ public static String simpleRepositoryKey(RemoteRepository repository,
String context) {
+ if (repository.isRepositoryManager()) {
+ StringBuilder buffer = new StringBuilder(128);
+ buffer.append(idToPathSegment(repository));
+ buffer.append('-');
+ SortedSet<String> subKeys = new TreeSet<>();
+ for (RemoteRepository mirroredRepo :
repository.getMirroredRepositories()) {
+ subKeys.add(mirroredRepo.getId());
}
- if (repository.isBlocked()) {
- buffer.append(", blocked");
+ StringDigestUtil sha1 = StringDigestUtil.sha1();
+ sha1.update(context);
+ for (String subKey : subKeys) {
+ sha1.update(subKey);
}
- buffer.append(")");
- return idToPathSegment(repository) + "-" +
StringDigestUtil.sha1(buffer.toString());
+ buffer.append(sha1.digest());
+ return buffer.toString();
+ } else {
+ return idToPathSegment(repository);
}
}
/**
- * Returns same instance of (session cached) function for session.
- */
- @SuppressWarnings("unchecked")
- public static Function<ArtifactRepository, String>
cachedIdToPathSegment(RepositorySystemSession session) {
- requireNonNull(session, "session");
- return (Function<ArtifactRepository, String>) session.getData()
- .computeIfAbsent(
- RepositoryIdHelper.class.getSimpleName() +
"-idToPathSegmentFunction",
- () -> cachedIdToPathSegmentFunction(session));
+ * 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) {
+ String seed = null;
+ if (repository.isRepositoryManager() && context != null &&
!context.isEmpty()) {
+ seed += context;
+ }
+ return idToPathSegment(repository) + (seed == null ? "" : "-" +
StringDigestUtil.sha1(seed));
}
/**
- * 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<ArtifactRepository, String>
cachedIdToPathSegmentFunction(RepositorySystemSession session) {
- if (session.getCache() != null) {
- return repository -> ((ConcurrentHashMap<String, String>)
session.getCache()
- .computeIfAbsent(
- session,
- RepositoryIdHelper.class.getSimpleName() +
"-idToPathSegmentCache",
- ConcurrentHashMap::new))
- .computeIfAbsent(repository.getId(), id ->
idToPathSegment(repository));
- } else {
- return RepositoryIdHelper::idToPathSegment; // uncached
+ * 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) {
+ String seed = null;
+ if (repository.isRepositoryManager() && context != null &&
!context.isEmpty()) {
+ seed += context;
+ }
+ return StringDigestUtil.sha1(repository.getUrl()) + (seed == null ? ""
: "-" + StringDigestUtil.sha1(seed));
+ }
+
+ /**
+ * 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 nidAndHurlRepositoryKey(RemoteRepository repository,
String context) {
+ String seed = repository.getUrl();
+ if (repository.isRepositoryManager() && context != null &&
!context.isEmpty()) {
+ seed += context;
+ }
+ 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.
+ * <p>
+ * <em>Important:</em> 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.
+ *
+ * @since 2.0.14
+ **/
+ private static String globallyUniqueRepositoryKey(RemoteRepository
repository, String context) {
+ String seed = remoteRepositoryDescription(repository, true);
+ if (repository.isRepositoryManager() && context != null &&
!context.isEmpty()) {
+ seed += context;
+ }
+ return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(seed);
}
/**
@@ -150,11 +213,6 @@ public final class RepositoryIdHelper {
* 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.
- * <p>
- * 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) {
@@ -163,4 +221,53 @@ public final class RepositoryIdHelper {
return repository.getId();
}
}
+
+ /**
+ * Creates unique string for given {@link RemoteRepository}. Ignores
following properties:
+ * <ul>
+ * <li>{@link RemoteRepository#getAuthentication()}</li>
+ * <li>{@link RemoteRepository#getProxy()}</li>
+ * <li>{@link RemoteRepository#getIntent()}</li>
+ * </ul>
+ */
+ private static String remoteRepositoryDescription(RemoteRepository
repository, boolean mirrorDetails) {
+ 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()) {
+ if (mirrorDetails) {
+ // sort them to make it stable ordering
+ ArrayList<RemoteRepository> 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");
+ }
+ }
+ if (repository.isBlocked()) {
+ buffer.append(", blocked");
+ }
+ buffer.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 bdbc15c1e..74f5b73b5 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,59 +18,87 @@
*/
package org.eclipse.aether.util.repository;
-import java.util.function.Function;
+import java.util.Collections;
-import org.eclipse.aether.DefaultRepositoryCache;
-import org.eclipse.aether.DefaultRepositorySystemSession;
-import org.eclipse.aether.repository.ArtifactRepository;
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 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() {
- DefaultRepositorySystemSession session = new
DefaultRepositorySystemSession(s -> false);
- session.setCache(new DefaultRepositoryCache()); // session has cache
set
- Function<ArtifactRepository, String> 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);
- assertSame(goodFixedId, safeId.apply(good));
+ 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();
- String badId = bad.getId();
- String badFixedId = safeId.apply(bad);
- assertNotEquals(badId, badFixedId);
- assertEquals("bad-SLASH-id", badFixedId);
- assertSame(badFixedId, safeId.apply(bad));
+ @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 nonCaching() {
- DefaultRepositorySystemSession session = new
DefaultRepositorySystemSession(s -> false);
- session.setCache(null); // session has no cache set
- Function<ArtifactRepository, String> 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();
+ 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));
+ }
- String goodId = good.getId();
- String goodFixedId = safeId.apply(good);
- assertEquals(goodId, goodFixedId);
- assertNotSame(goodFixedId, safeId.apply(good));
+ @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));
+ }
- String badId = bad.getId();
- String badFixedId = safeId.apply(bad);
- assertNotEquals(badId, badFixedId);
- assertEquals("bad-SLASH-id", badFixedId);
- assertNotSame(badFixedId, safeId.apply(bad));
+ @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));
}
}
diff --git a/src/site/markdown/configuration.md
b/src/site/markdown/configuration.md
index 4735a16ed..295203c09 100644
--- a/src/site/markdown/configuration.md
+++ b/src/site/markdown/configuration.md
@@ -107,6 +107,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 <code>true</code> 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
<code>#CONFIG_PROP_ENABLED</code> still show the "real st [...]
| `"aether.remoteRepositoryFilter.prefixes.useMirroredRepositories"` |
`Boolean` | Configuration to allow Prefixes filter to auto-discover prefixes
from mirrored repositories as well. For this to work <em>Maven should be
aware</em> 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 <code>#CONFIG_PROP_ENABLED</code> with
repository ID suffix. | `false` | [...]
| `"aether.remoteRepositoryFilter.prefixes.useRepositoryManagers"` | `Boolean`
| Configuration to allow Prefixes filter to auto-discover prefixes from
repository managers as well. For this to work <em>Maven should be aware</em>
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 <code>#CONFIG_PROP_ENABLED</code> with repository ID
suffix. <em>Note: as of today, n [...]
+| `"aether.remoteRepositoryFilter.repositoryKeyFunction"` | `String` |
<b>Experimental:</b> Configuration for "repository key" function. Note:
repository key functions other than "nid" produce repository keys will be
<em>way different that those produced with previous versions or without this
option enabled</em>. Filter uses this key function to lay down and look up
files to use in filtering. | `"nid"` | 2.0.14 | No | Session Configuration |
| `"aether.snapshotFilter"` | `Boolean` | The key in the repository session's
<code>RepositorySystemSession#getConfigProperties()
configurationproperties</code> used to store a <code>Boolean</code> 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 |
@@ -121,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` | <b>Experimental:</b>
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</code>
in utils. <em>Warning:</em> repository key function affects Resolver
fundamentally and may have une [...]
| `"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 <a
href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#ciphersuites">Cipher
Suites</a> which are enabled for HTTPS connections. | - | 2.0.0 | No |
Session Configuration |
| `"aether.transport.apache.https.protocols"` | `String` | Comma-separated
list of <a
href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#jssenames">Protocols
</a> which are enabled for HTTPS connections. | - | 2.0.0 | No | Session
Configuration |
@@ -157,6 +159,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` |
<b>Experimental:</b> Configuration for "repository key" function. Note:
repository key functions other than "nid" produce repository keys will be
<em>way different that those produced with previous versions or without this
option enabled</em>. 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 |
diff --git a/src/site/markdown/repository-key-function.md
b/src/site/markdown/repository-key-function.md
new file mode 100644
index 000000000..250df5e28
--- /dev/null
+++ b/src/site/markdown/repository-key-function.md
@@ -0,0 +1,123 @@
+# Repository Key Function
+<!---
+ 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.
+-->
+
+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). 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 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.
+
+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:
+* 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
+* `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.
+
+**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:
+* `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. 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`
+
+Similar to `ngurk` but does not normalize mirrors. As a consequence, and due
dynamism of mirrors, the key of same
+remote repository (for example `<mirrorOf>external:*</mirrorOf>`) **may change
during the build**.
diff --git a/src/site/site.xml b/src/site/site.xml
index 91e5e983a..677ec4ff1 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -32,6 +32,7 @@ under the License.
<item name="Local Repository" href="local-repository.html"/>
<item name="Remote Repository Filtering"
href="remote-repository-filtering.html"/>
<item name="Third-party Integrations"
href="third-party-integrations.html"/>
+ <item name="Repository Key Function"
href="repository-key-function.html"/>
<item name="Download" href="download.html"/>
</menu>
<menu name="Guides">