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 @@ * 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 LocalRepository(File basedir, String type) { public LocalRepository(Path basePath, String type) { this.basePath = basePath; this.type = (type != null) ? type : ""; + this.hashCode = Objects.hash(this.basePath, this.type); } @Override @@ -108,7 +115,7 @@ public String getContentType() { @Override public String getId() { - return "local"; + return ID; } /** @@ -153,13 +160,6 @@ public boolean equals(Object obj) { @Override public int hashCode() { - int hash = 17; - hash = hash * 31 + hash(basePath); - hash = hash * 31 + hash(type); - return hash; - } - - private static int hash(Object obj) { - return obj != null ? obj.hashCode() : 0; + return hashCode; } } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceRepository.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/WorkspaceRepository.java index 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 @@ * 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 WorkspaceRepository(String type) { public WorkspaceRepository(String type, Object key) { this.type = (type != null) ? type : ""; this.key = (key != null) ? key : UUID.randomUUID().toString().replace("-", ""); + this.hashCode = Objects.hash(type, key); } public String getContentType() { @@ -65,7 +70,7 @@ public String getContentType() { } public String getId() { - return "workspace"; + return ID; } /** @@ -99,9 +104,6 @@ public boolean equals(Object obj) { @Override public int hashCode() { - int hash = 17; - hash = hash * 31 + getKey().hashCode(); - hash = hash * 31 + getContentType().hashCode(); - return hash; + return hashCode; } } diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/resolver/Resolver.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/resolver/Resolver.java index 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 @@ 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 static void main(String[] args) throws Exception { System.out.println("------------------------------------------------------------"); System.out.println(ResolverDemo.class.getSimpleName()); - Resolver resolver = new Resolver(args, "https://repo.maven.apache.org/maven2/", "target/resolver-demo-repo"); + Resolver resolver = + new Resolver(args, "https://repo.maven.apache.org/maven2/", Paths.get("target/resolver-demo-repo")); ResolverResult result = resolver.resolve("junit", "junit", "4.13.2"); System.out.println("Result:"); @@ -47,8 +49,8 @@ public static void main(String[] args) throws Exception { } public void resolve(String[] args) throws DependencyResolutionException { - Resolver resolver = - new Resolver(args, "http://localhost:8081/nexus/content/groups/public", "target/aether-repo"); + Resolver resolver = new Resolver( + args, "http://localhost:8081/nexus/content/groups/public", Paths.get("target/aether-repo")); ResolverResult result = resolver.resolve("com.mycompany.app", "super-app", "1.0"); @@ -66,8 +68,8 @@ public void resolve(String[] args) throws DependencyResolutionException { } public void installAndDeploy(String[] args) throws InstallationException, DeploymentException { - Resolver resolver = - new Resolver(args, "http://localhost:8081/nexus/content/groups/public", "target/aether-repo"); + Resolver resolver = new Resolver( + args, "http://localhost:8081/nexus/content/groups/public", Paths.get("target/aether-repo")); Artifact artifact = new DefaultArtifact("com.mycompany.super", "super-core", "jar", "0.1-SNAPSHOT"); artifact = artifact.setFile(new File("jar-from-whatever-process.jar")); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java index 2a18ce049..74c488dda 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultLocalPathPrefixComposerFactory.java @@ -21,11 +21,12 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.util.function.Function; +import java.util.function.BiFunction; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.repository.ArtifactRepository; -import org.eclipse.aether.util.repository.RepositoryIdHelper; +import org.eclipse.aether.repository.RemoteRepository; + +import static org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory.repositoryKeyFunction; /** * Default local path prefix composer factory: it fully reuses {@link LocalPathPrefixComposerFactorySupport} class @@ -48,7 +49,7 @@ public LocalPathPrefixComposer createComposer(RepositorySystemSession session) { isSplitRemoteRepositoryLast(session), getReleasesPrefix(session), getSnapshotsPrefix(session), - RepositoryIdHelper.cachedIdToPathSegment(session)); + repositoryKeyFunction(session)); } /** @@ -66,7 +67,7 @@ private DefaultLocalPathPrefixComposer( boolean splitRemoteRepositoryLast, String releasesPrefix, String snapshotsPrefix, - Function idToPathSegmentFunction) { + BiFunction repositoryKeyFunction) { super( split, localPrefix, @@ -77,7 +78,7 @@ private DefaultLocalPathPrefixComposer( splitRemoteRepositoryLast, releasesPrefix, snapshotsPrefix, - idToPathSegmentFunction); + repositoryKeyFunction); } } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java index e55150f3f..c1b80999f 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManager.java @@ -27,12 +27,11 @@ import java.util.Map; import java.util.Objects; import java.util.Properties; -import java.util.function.Function; +import java.util.function.BiFunction; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.metadata.Metadata; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.LocalArtifactRegistration; import org.eclipse.aether.repository.LocalArtifactRequest; import org.eclipse.aether.repository.LocalArtifactResult; @@ -72,11 +71,11 @@ class EnhancedLocalRepositoryManager extends SimpleLocalRepositoryManager { EnhancedLocalRepositoryManager( Path basedir, LocalPathComposer localPathComposer, - Function idToPathSegmentFunction, + BiFunction repositoryKeyFunction, String trackingFilename, TrackingFileManager trackingFileManager, LocalPathPrefixComposer localPathPrefixComposer) { - super(basedir, "enhanced", localPathComposer, idToPathSegmentFunction); + super(basedir, "enhanced", localPathComposer, repositoryKeyFunction); this.trackingFilename = requireNonNull(trackingFilename); this.trackingFileManager = requireNonNull(trackingFileManager); this.localPathPrefixComposer = requireNonNull(localPathPrefixComposer); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java index b00f1dff9..51f01fd3b 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerFactory.java @@ -22,11 +22,16 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; + import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.NoLocalRepositoryManagerException; +import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; import org.eclipse.aether.util.ConfigUtils; import org.eclipse.aether.util.repository.RepositoryIdHelper; @@ -58,6 +63,25 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan public static final String DEFAULT_TRACKING_FILENAME = "_remote.repositories"; + /** + * Make enhanced repository use "globally unique repository keys" (repository keys are used for designating + * cached metadata, artifact availability tracking and split repository prefix production). By default, this + * option is disabled. If enabled, repository keys produced by enhanced repository will be way different + * that those produced with previous versions or without this option enabled. Ideally, you may want to + * use empty local repository to populate with new repository key contained metadata, Interoperability between + * enabled and disabled affects only metadata and split repository (ie. split repository may not find existing + * caches, and may opt to re-download them). + * + * @since 2.0.14 + * @configurationSource {@link RepositorySystemSession#getConfigProperties()} + * @configurationType {@link java.lang.Boolean} + * @configurationDefaultValue {@link #DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS} + */ + public static final String CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS = + CONFIG_PROPS_PREFIX + "globallyUniqueRepositoryKeys"; + + public static final boolean DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS = false; + private float priority = 10.0f; private final LocalPathComposer localPathComposer; @@ -66,6 +90,32 @@ public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryMan private final LocalPathPrefixComposerFactory localPathPrefixComposerFactory; + /** + * Method that based on configuration returns the "repository key function". Used by {@link EnhancedLocalRepositoryManagerFactory} + * and {@link LocalPathPrefixComposerFactory}. + * + * @since 2.0.14 + */ + @SuppressWarnings("unchecked") + static BiFunction repositoryKeyFunction(RepositorySystemSession session) { + if (ConfigUtils.getBoolean( + session, DEFAULT_GLOBALLY_UNIQUE_REPOSITORY_KEYS, CONFIG_PROP_GLOBALLY_UNIQUE_REPOSITORY_KEYS)) { + // this is expensive method; cache it in session (repo -> context -> ID) + return (repository, context) -> ((ConcurrentMap>) + session.getData() + .computeIfAbsent( + EnhancedLocalRepositoryManagerFactory.class.getName() + + ".repositoryKeyFunction", + ConcurrentHashMap::new)) + .computeIfAbsent(repository, k1 -> new ConcurrentHashMap<>()) + .computeIfAbsent( + context == null ? "" : context, + k2 -> RepositoryIdHelper.globallyUniqueRepositoryKey(repository, context)); + } else { + return RepositoryIdHelper::simpleRepositoryKey; + } + } + @Inject public EnhancedLocalRepositoryManagerFactory( final LocalPathComposer localPathComposer, @@ -94,7 +144,7 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local return new EnhancedLocalRepositoryManager( repository.getBasePath(), localPathComposer, - RepositoryIdHelper.cachedIdToPathSegment(session), + repositoryKeyFunction(session), trackingFilename, trackingFileManager, localPathPrefixComposerFactory.createComposer(session)); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/LocalPathPrefixComposerFactorySupport.java index b86223b53..f26c329aa 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,12 +18,11 @@ */ package org.eclipse.aether.internal.impl; -import java.util.function.Function; +import java.util.function.BiFunction; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.metadata.Metadata; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.util.ConfigUtils; @@ -244,7 +243,7 @@ protected abstract static class LocalPathPrefixComposerSupport implements LocalP protected final String snapshotsPrefix; - protected final Function idToPathSegmentFunction; + protected final BiFunction repositoryKeyFunction; protected LocalPathPrefixComposerSupport( boolean split, @@ -256,7 +255,7 @@ protected LocalPathPrefixComposerSupport( boolean splitRemoteRepositoryLast, String releasesPrefix, String snapshotsPrefix, - Function idToPathSegmentFunction) { + BiFunction repositoryKeyFunction) { this.split = split; this.localPrefix = localPrefix; this.splitLocal = splitLocal; @@ -266,7 +265,7 @@ protected LocalPathPrefixComposerSupport( this.splitRemoteRepositoryLast = splitRemoteRepositoryLast; this.releasesPrefix = releasesPrefix; this.snapshotsPrefix = snapshotsPrefix; - this.idToPathSegmentFunction = idToPathSegmentFunction; + this.repositoryKeyFunction = repositoryKeyFunction; } @Override @@ -288,13 +287,13 @@ public String getPathPrefixForRemoteArtifact(Artifact artifact, RemoteRepository } String result = remotePrefix; if (!splitRemoteRepositoryLast && splitRemoteRepository) { - result += "/" + idToPathSegmentFunction.apply(repository); + result += "/" + repositoryKeyFunction.apply(repository, null); } if (splitRemote) { result += "/" + (artifact.isSnapshot() ? snapshotsPrefix : releasesPrefix); } if (splitRemoteRepositoryLast && splitRemoteRepository) { - result += "/" + idToPathSegmentFunction.apply(repository); + result += "/" + repositoryKeyFunction.apply(repository, null); } return result; } @@ -318,13 +317,13 @@ public String getPathPrefixForRemoteMetadata(Metadata metadata, RemoteRepository } String result = remotePrefix; if (!splitRemoteRepositoryLast && splitRemoteRepository) { - result += "/" + idToPathSegmentFunction.apply(repository); + result += "/" + repositoryKeyFunction.apply(repository, null); } if (splitRemote) { result += "/" + (isSnapshot(metadata) ? snapshotsPrefix : releasesPrefix); } if (splitRemoteRepositoryLast && splitRemoteRepository) { - result += "/" + idToPathSegmentFunction.apply(repository); + result += "/" + repositoryKeyFunction.apply(repository, null); } return result; } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManager.java index ea751d41a..f1da03db9 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,11 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; -import java.util.SortedSet; -import java.util.TreeSet; -import java.util.function.Function; +import java.util.function.BiFunction; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.metadata.Metadata; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.LocalArtifactRegistration; import org.eclipse.aether.repository.LocalArtifactRequest; import org.eclipse.aether.repository.LocalArtifactResult; @@ -38,7 +35,6 @@ import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.repository.RemoteRepository; -import org.eclipse.aether.util.StringDigestUtil; import static java.util.Objects.requireNonNull; @@ -51,17 +47,17 @@ class SimpleLocalRepositoryManager implements LocalRepositoryManager { private final LocalPathComposer localPathComposer; - private final Function idToPathSegmentFunction; + private final BiFunction repositoryKeyFunction; SimpleLocalRepositoryManager( Path basePath, String type, LocalPathComposer localPathComposer, - Function idToPathSegmentFunction) { + BiFunction repositoryKeyFunction) { requireNonNull(basePath, "base directory cannot be null"); repository = new LocalRepository(basePath.toAbsolutePath(), type); this.localPathComposer = requireNonNull(localPathComposer); - this.idToPathSegmentFunction = requireNonNull(idToPathSegmentFunction); + this.repositoryKeyFunction = requireNonNull(repositoryKeyFunction); } @Override @@ -85,7 +81,7 @@ public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository repos @Override public String getPathForLocalMetadata(Metadata metadata) { requireNonNull(metadata, "metadata cannot be null"); - return localPathComposer.getPathForMetadata(metadata, "local"); + return localPathComposer.getPathForMetadata(metadata, LocalRepository.ID); } @Override @@ -101,35 +97,7 @@ public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository repos * of the remote repository (as it may change). */ protected String getRepositoryKey(RemoteRepository repository, String context) { - String key; - - if (repository.isRepositoryManager()) { - // repository serves dynamic contents, take request parameters into account for key - - StringBuilder buffer = new StringBuilder(128); - - buffer.append(idToPathSegmentFunction.apply(repository)); - - buffer.append('-'); - - SortedSet subKeys = new TreeSet<>(); - for (RemoteRepository mirroredRepo : repository.getMirroredRepositories()) { - subKeys.add(mirroredRepo.getId()); - } - - StringDigestUtil sha1 = StringDigestUtil.sha1(); - sha1.update(context); - for (String subKey : subKeys) { - sha1.update(subKey); - } - buffer.append(sha1.digest()); - - key = buffer.toString(); - } else { - key = idToPathSegmentFunction.apply(repository); - } - - return key; + return repositoryKeyFunction.apply(repository, context); } @Override diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java index c1e4ccd21..4be3c2833 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerFactory.java @@ -62,10 +62,7 @@ public LocalRepositoryManager newInstance(RepositorySystemSession session, Local if ("".equals(repository.getContentType()) || "simple".equals(repository.getContentType())) { return new SimpleLocalRepositoryManager( - repository.getBasePath(), - "simple", - localPathComposer, - RepositoryIdHelper.cachedIdToPathSegment(session)); + repository.getBasePath(), "simple", localPathComposer, RepositoryIdHelper::simpleRepositoryKey); } else { throw new NoLocalRepositoryManagerException(repository); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java index 87cb48ae1..d869a12dd 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Utils.java @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; @@ -31,6 +33,7 @@ import org.eclipse.aether.impl.OfflineController; import org.eclipse.aether.installation.InstallRequest; import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ResolutionErrorPolicy; import org.eclipse.aether.resolution.ResolutionErrorPolicyRequest; @@ -39,6 +42,7 @@ import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator; import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory; import org.eclipse.aether.transfer.RepositoryOfflineException; +import org.eclipse.aether.util.repository.RepositoryIdHelper; /** * Internal utility methods. @@ -207,4 +211,14 @@ public static void checkOffline( offlineController.checkOffline(session, repository); } } + + /** + * Shared and cached {@link RepositoryIdHelper#idToPathSegment(ArtifactRepository)} method, + */ + @SuppressWarnings("unchecked") + public static String cachedIdToPathSegment(RepositorySystemSession session, ArtifactRepository artifactRepository) { + return ((ConcurrentMap) session.getData() + .computeIfAbsent(Utils.class.getName() + ".cachedIdToPathSegment", ConcurrentHashMap::new)) + .computeIfAbsent(artifactRepository, RepositoryIdHelper::idToPathSegment); + } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java index fc417884c..5657a585b 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceSupport.java @@ -53,7 +53,7 @@ * * @since 1.9.0 */ -abstract class FileTrustedChecksumsSourceSupport implements TrustedChecksumsSource { +public abstract class FileTrustedChecksumsSourceSupport implements TrustedChecksumsSource { protected static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_AETHER + "trustedChecksumsSource."; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java index 1d6af287b..6162b6c95 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java @@ -34,11 +34,11 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.internal.impl.LocalPathComposer; +import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.io.ChecksumProcessor; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -134,7 +134,7 @@ protected Map doGetTrustedArtifactChecksums( Path checksumPath = basedir.resolve(calculateArtifactPath( originAware, artifact, - RepositoryIdHelper.cachedIdToPathSegment(session).apply(artifactRepository), + Utils.cachedIdToPathSegment(session, artifactRepository), checksumAlgorithmFactory)); if (!Files.isRegularFile(checksumPath)) { @@ -167,7 +167,7 @@ protected Writer doGetTrustedArtifactChecksumsWriter(RepositorySystemSession ses return new SparseDirectoryWriter( getBasedir(session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, true), isOriginAware(session), - RepositoryIdHelper.cachedIdToPathSegment(session)); + r -> Utils.cachedIdToPathSegment(session, r)); } private String calculateArtifactPath( diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java index 85c9be19f..b6827c5c9 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java @@ -43,11 +43,11 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.LocalPathComposer; +import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -177,7 +177,7 @@ protected Map doGetTrustedArtifactChecksums( Path summaryFile = summaryFile( basedir, originAware, - RepositoryIdHelper.cachedIdToPathSegment(session).apply(artifactRepository), + Utils.cachedIdToPathSegment(session, artifactRepository), checksumAlgorithmFactory.getFileExtension()); ConcurrentHashMap algorithmChecksums = checksums.computeIfAbsent(summaryFile, f -> loadProvidedChecksums(summaryFile)); @@ -199,7 +199,7 @@ protected Writer doGetTrustedArtifactChecksumsWriter(RepositorySystemSession ses checksums, getBasedir(session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, true), isOriginAware(session), - RepositoryIdHelper.cachedIdToPathSegment(session)); + r -> Utils.cachedIdToPathSegment(session, r)); } /** diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java index 3d6643876..dd4699331 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java @@ -42,6 +42,7 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.RepositorySystemLifecycle; +import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.internal.impl.filter.ruletree.GroupTree; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.repository.RemoteRepository; @@ -50,7 +51,6 @@ import org.eclipse.aether.spi.io.PathProcessor; import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -249,7 +249,7 @@ private Path ruleFile(RepositorySystemSession session, RemoteRepository remoteRe return ruleFiles(session).computeIfAbsent(normalizeRemoteRepository(session, remoteRepository), r -> getBasedir( session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, false) .resolve(GROUP_ID_FILE_PREFIX - + RepositoryIdHelper.cachedIdToPathSegment(session).apply(remoteRepository) + + Utils.cachedIdToPathSegment(session, remoteRepository) + GROUP_ID_FILE_SUFFIX)); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java index 9a4b2c301..117e621c3 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java @@ -36,6 +36,7 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.MetadataResolver; import org.eclipse.aether.impl.RemoteRepositoryManager; +import org.eclipse.aether.internal.impl.Utils; import org.eclipse.aether.internal.impl.filter.prefixes.PrefixesSource; import org.eclipse.aether.internal.impl.filter.ruletree.PrefixTree; import org.eclipse.aether.metadata.DefaultMetadata; @@ -49,7 +50,6 @@ import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider; import org.eclipse.aether.transfer.NoRepositoryLayoutException; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -318,9 +318,8 @@ private PrefixTree loadPrefixTree( private Path resolvePrefixesFromLocalConfiguration( RepositorySystemSession session, Path baseDir, RemoteRepository remoteRepository) { - Path filePath = baseDir.resolve(PREFIXES_FILE_PREFIX - + RepositoryIdHelper.cachedIdToPathSegment(session).apply(remoteRepository) - + PREFIXES_FILE_SUFFIX); + Path filePath = baseDir.resolve( + PREFIXES_FILE_PREFIX + Utils.cachedIdToPathSegment(session, remoteRepository) + PREFIXES_FILE_SUFFIX); if (Files.isReadable(filePath)) { return filePath; } else { diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java index 7d8a296cc..25c6af747 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedLocalRepositoryManagerTest.java @@ -33,13 +33,13 @@ import org.eclipse.aether.metadata.DefaultMetadata; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.metadata.Metadata.Nature; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.LocalArtifactRegistration; import org.eclipse.aether.repository.LocalArtifactRequest; import org.eclipse.aether.repository.LocalArtifactResult; import org.eclipse.aether.repository.LocalMetadataRequest; import org.eclipse.aether.repository.LocalMetadataResult; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -108,7 +108,7 @@ protected EnhancedLocalRepositoryManager getManager() { return new EnhancedLocalRepositoryManager( basedir.toPath(), new DefaultLocalPathComposer(), - ArtifactRepository::getId, + RepositoryIdHelper::simpleRepositoryKey, "_remote.repositories", trackingFileManager, new DefaultLocalPathPrefixComposerFactory().createComposer(session)); diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java index 4a78ea302..dbf37e80c 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/EnhancedSplitLocalRepositoryManagerTest.java @@ -20,8 +20,8 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -34,7 +34,7 @@ protected EnhancedLocalRepositoryManager getManager() { return new EnhancedLocalRepositoryManager( basedir.toPath(), new DefaultLocalPathComposer(), - ArtifactRepository::getId, + RepositoryIdHelper::simpleRepositoryKey, "_remote.repositories", trackingFileManager, new DefaultLocalPathPrefixComposerFactory().createComposer(session)); diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/SimpleLocalRepositoryManagerTest.java index 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.DefaultArtifact; import org.eclipse.aether.internal.test.util.TestFileUtils; import org.eclipse.aether.internal.test.util.TestUtils; -import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.repository.LocalArtifactRequest; import org.eclipse.aether.repository.LocalArtifactResult; import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.util.repository.RepositoryIdHelper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -50,7 +50,7 @@ public class SimpleLocalRepositoryManagerTest { @BeforeEach void setup() throws IOException { manager = new SimpleLocalRepositoryManager( - basedir.toPath(), "simple", new DefaultLocalPathComposer(), ArtifactRepository::getId); + basedir.toPath(), "simple", new DefaultLocalPathComposer(), RepositoryIdHelper::simpleRepositoryKey); session = TestUtils.newSession(); } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/RepositoryIdHelper.java index 5c8bbbbcd..0722a458a 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,22 @@ */ package org.eclipse.aether.util.repository; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Locale; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import java.util.function.Predicate; +import 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.util.PathUtils; import org.eclipse.aether.util.StringDigestUtil; -import static java.util.Objects.requireNonNull; - /** - * Helper class for {@link ArtifactRepository#getId()} handling. This class provides helper function (cached or uncached) - * to get id of repository as it was originally envisioned: as path safe. While POMs are validated by Maven, there are - * POMs out there that somehow define repositories with unsafe characters in their id. The problem affects mostly + * Helper class for {@link ArtifactRepository#getId()} handling. This class provides helper methods + * to get id of repository as it was originally envisioned: as path safe, unique, etc. While POMs are validated by Maven, + * there are POMs out there that somehow define repositories with unsafe characters in their id. The problem affects mostly * {@link RemoteRepository} instances, as all other implementations have fixed ids that are path safe. + *

+ * Important: multiple of these provided methods are not trivial processing-wise, and some sort of + * caching is warmly recommended. * * @see PathUtils * @since 2.0.11 @@ -46,103 +41,68 @@ public final class RepositoryIdHelper { private RepositoryIdHelper() {} - private static final String CENTRAL_REPOSITORY_ID = "central"; - private static final Collection CENTRAL_URLS = Collections.unmodifiableList(Arrays.asList( - "https://repo.maven.apache.org/maven2", - "https://repo1.maven.org/maven2", - "https://maven-central.storage-download.googleapis.com/maven2")); - private static final Predicate CENTRAL_DIRECT_ONLY = - remoteRepository -> CENTRAL_REPOSITORY_ID.equals(remoteRepository.getId()) - && "https".equals(remoteRepository.getProtocol().toLowerCase(Locale.ENGLISH)) - && CENTRAL_URLS.stream().anyMatch(remoteUrl -> { - String rurl = remoteRepository.getUrl().toLowerCase(Locale.ENGLISH); - if (rurl.endsWith("/")) { - rurl = rurl.substring(0, rurl.length() - 1); - } - return rurl.equals(remoteUrl); - }) - && remoteRepository.getPolicy(false).isEnabled() - && !remoteRepository.getPolicy(true).isEnabled() - && remoteRepository.getMirroredRepositories().isEmpty() - && !remoteRepository.isRepositoryManager() - && !remoteRepository.isBlocked(); - /** - * 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. - *

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

- * Important:Do not use this method, or at least do consider when do you want to use it, as it - * totally disconnects repositories used in session. This method may be used under some special circumstances - * (ie reporting), but must not be used within Resolver (and Maven) session for "usual" resolution and - * deployment use cases. - */ - public static String remoteRepositoryUniqueId(RemoteRepository repository) { - 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(")"); + * Simple {@code repositoryKey} function (classic). Returns {@link RemoteRepository#getId()}, unless + * {@link RemoteRepository#isRepositoryManager()} returns {@code true}, in which case this method creates + * unique identifier based on ID and current configuration of the remote repository (as it may change). + * This was the default method in Maven 3. + * + * @since 2.0.14 + **/ + public static String simpleRepositoryKey(RemoteRepository repository, String context) { + String key; + if (repository.isRepositoryManager()) { + StringBuilder buffer = new StringBuilder(128); + buffer.append(idToPathSegment(repository)); + buffer.append('-'); + SortedSet subKeys = new TreeSet<>(); + for (RemoteRepository mirroredRepo : repository.getMirroredRepositories()) { + subKeys.add(mirroredRepo.getId()); } - 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()); + key = buffer.toString(); + } else { + key = idToPathSegment(repository); } + return key; } /** - * Returns same instance of (session cached) function for session. - */ - @SuppressWarnings("unchecked") - public static Function cachedIdToPathSegment(RepositorySystemSession session) { - requireNonNull(session, "session"); - return (Function) session.getData() - .computeIfAbsent( - RepositoryIdHelper.class.getSimpleName() + "-idToPathSegmentFunction", - () -> cachedIdToPathSegmentFunction(session)); + * Globally unique {@code repositoryKey} function. This repository key method returns same results as + * {@link #remoteRepositoryUniqueId(RemoteRepository)} if parameter context is {@code null} or empty string. + * + * @see #remoteRepositoryUniqueId(RemoteRepository) + * @since 2.0.14 + **/ + public static String globallyUniqueRepositoryKey(RemoteRepository repository, String context) { + String description = remoteRepositoryDescription(repository); + if (context != null && !context.isEmpty()) { + description += context; + } + return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(description); } /** - * Returns new instance of function backed by cached or uncached (if session has no cache set) - * {@link #idToPathSegment(ArtifactRepository)} method call. + * Creates unique repository id for given {@link RemoteRepository}. + * For any remote repository it will return string created as {@code $(repository.id)-sha1(repository-aspects)}. + * The key material contains all relevant aspects of remote repository, so repository with same ID even if just + * policy changes (enabled/disabled), will map to different string id. The checksum and update policies are not + * participating in key creation. + *

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

+ * Important:Do not use this method, or at least do consider when do you want to use it, as it + * totally disconnects repositories used in session. This method may be used under some special circumstances + * (ie reporting), but must not be used within Resolver (and Maven) session for "usual" resolution and + * deployment use cases. */ - @SuppressWarnings("unchecked") - private static Function cachedIdToPathSegmentFunction(RepositorySystemSession session) { - if (session.getCache() != null) { - return repository -> ((ConcurrentHashMap) session.getCache() - .computeIfAbsent( - session, - RepositoryIdHelper.class.getSimpleName() + "-idToPathSegmentCache", - ConcurrentHashMap::new)) - .computeIfAbsent(repository.getId(), id -> idToPathSegment(repository)); - } else { - return RepositoryIdHelper::idToPathSegment; // uncached - } + public static String remoteRepositoryUniqueId(RemoteRepository repository) { + return idToPathSegment(repository) + "-" + StringDigestUtil.sha1(remoteRepositoryDescription(repository)); } /** @@ -150,17 +110,52 @@ private static Function cachedIdToPathSegmentFunctio * returned repository ID is "path segment" safe. Ideally, this method should never modify repository ID, as * Maven validation prevents use of illegal FS characters in them, but we found in Maven Central several POMs that * define remote repositories with illegal FS characters in their ID. - *

- * This method is simplistic on purpose, and if frequently used, best if results are cached (per session), - * see {@link #cachedIdToPathSegment(RepositorySystemSession)} method. - * - * @see #cachedIdToPathSegment(RepositorySystemSession) */ - private static String idToPathSegment(ArtifactRepository repository) { + public static String idToPathSegment(ArtifactRepository repository) { if (repository instanceof RemoteRepository) { return PathUtils.stringToPathSegment(repository.getId()); } else { return repository.getId(); } } + + /** + * Creates unique string for given {@link RemoteRepository}. Ignores following properties: + *

    + *
  • {@link RemoteRepository#getAuthentication()}
  • + *
  • {@link RemoteRepository#getProxy()}
  • + *
+ */ + public static String remoteRepositoryDescription(RemoteRepository repository) { + StringBuilder buffer = new StringBuilder(256); + buffer.append(repository.getId()); + buffer.append(" (").append(repository.getUrl()); + buffer.append(", ").append(repository.getContentType()); + boolean r = repository.getPolicy(false).isEnabled(), + s = repository.getPolicy(true).isEnabled(); + if (r && s) { + buffer.append(", releases+snapshots"); + } else if (r) { + buffer.append(", releases"); + } else if (s) { + buffer.append(", snapshots"); + } else { + buffer.append(", disabled"); + } + if (repository.isRepositoryManager()) { + buffer.append(", managed"); + } + if (!repository.getMirroredRepositories().isEmpty()) { + buffer.append(", mirrorOf("); + for (RemoteRepository mirroredRepo : repository.getMirroredRepositories()) { + buffer.append(remoteRepositoryDescription(mirroredRepo)); + } + buffer.append(")"); + } + if (repository.isBlocked()) { + buffer.append(", blocked"); + } + buffer.append(", ").append(repository.getIntent().name()).append(")"); + return buffer.toString(); + } } diff --git a/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java b/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java index bdbc15c1e..f9c10ee72 100644 --- a/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java +++ b/maven-resolver-util/src/test/java/org/eclipse/aether/util/repository/RepositoryIdHelperTest.java @@ -28,15 +28,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertSame; public class RepositoryIdHelperTest { @Test - void caching() { + void idToPathSegment() { DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(s -> false); session.setCache(new DefaultRepositoryCache()); // session has cache set - Function safeId = RepositoryIdHelper.cachedIdToPathSegment(session); + Function safeId = RepositoryIdHelper::idToPathSegment; RemoteRepository good = new RemoteRepository.Builder("good", "default", "https://somewhere.com").build(); RemoteRepository bad = new RemoteRepository.Builder("bad/id", "default", "https://somewhere.com").build(); @@ -44,33 +42,10 @@ void caching() { String goodId = good.getId(); String goodFixedId = safeId.apply(good); assertEquals(goodId, goodFixedId); - assertSame(goodFixedId, safeId.apply(good)); String badId = bad.getId(); String badFixedId = safeId.apply(bad); assertNotEquals(badId, badFixedId); assertEquals("bad-SLASH-id", badFixedId); - assertSame(badFixedId, safeId.apply(bad)); - } - - @Test - void nonCaching() { - DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(s -> false); - session.setCache(null); // session has no cache set - Function safeId = RepositoryIdHelper.cachedIdToPathSegment(session); - - RemoteRepository good = new RemoteRepository.Builder("good", "default", "https://somewhere.com").build(); - RemoteRepository bad = new RemoteRepository.Builder("bad/id", "default", "https://somewhere.com").build(); - - String goodId = good.getId(); - String goodFixedId = safeId.apply(good); - assertEquals(goodId, goodFixedId); - assertNotSame(goodFixedId, safeId.apply(good)); - - String badId = bad.getId(); - String badFixedId = safeId.apply(bad); - assertNotEquals(badId, badFixedId); - assertEquals("bad-SLASH-id", badFixedId); - assertNotSame(badFixedId, safeId.apply(bad)); } } diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index 4735a16ed..1980cfa79 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -71,6 +71,7 @@ To modify this file, edit the template and regenerate. | `"aether.generator.sigstore.publicStaging"` | `Boolean` | Whether Sigstore should use public staging sigstage.dev instead of public default sigstore.dev . | `false` | 2.0.2 | No | Session Configuration | | `"aether.interactive"` | `Boolean` | A flag indicating whether interaction with the user is allowed. | `false` | | No | Session Configuration | | `"aether.layout.maven2.checksumAlgorithms"` | `String` | Comma-separated list of checksum algorithms with which checksums are validated (downloaded) and generated (uploaded) with this layout. Resolver by default supports following algorithms: MD5, SHA-1, SHA-256 and SHA-512. New algorithms can be added by implementing ChecksumAlgorithmFactory component. | `"SHA-1,MD5"` | 1.8.0 | Yes | Session Configuration | +| `"aether.lrm.enhanced.globallyUniqueRepositoryKeys"` | `Boolean` | Make enhanced repository use "globally unique repository keys" (repository keys are used for designating cached metadata, artifact availability tracking and split repository prefix production). By default, this option is disabled. If enabled, repository keys produced by enhanced repository will be way different that those produced with previous versions or without this option enabled. Ideally, you may want to use empty local repository to populate with new repository key contained metadata, Interoperability between enabled and disabled affects only metadata and split repository (ie. split repository may not find existing caches, and may opt to re-download them). | `false` | 2.0.14 | No | Session Configuration | | `"aether.lrm.enhanced.localPrefix"` | `String` | The prefix to use for locally installed artifacts. | `"installed"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.releasesPrefix"` | `String` | The prefix to use for release artifacts. | `"releases"` | 1.8.1 | No | Session Configuration | | `"aether.lrm.enhanced.remotePrefix"` | `String` | The prefix to use for remotely cached artifacts. | `"cached"` | 1.8.1 | No | Session Configuration |