diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java index 99cec7d2e..8b359cea4 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java @@ -30,8 +30,32 @@ /** * A repository on a remote server. + *

+ * If use of instances of this class are meant to be used as keys, see {@link #toBareRemoteRepository()} method. */ public final class RemoteRepository implements ArtifactRepository { + /** + * The intent this repository is to be used for. Newly created repositories are usually "bare", and before + * their actual use (caller or repository system) adapts them, equip with auth/proxy info and even mirrors, if + * environment is configured for them. Note: "bare" does not always mean "without authentication", as client + * code may create with all required properties, but {@link org.eclipse.aether.RepositorySystem} will process + * them anyway, marking their "intent". + *

+ * Important consequence: the change of {@link Intent} on repository may affect the use cases when + * they are used as keys (they are suitable for that). To use {@link RemoteRepository} instances as key, + * you should use instances returned by method {@link #toBareRemoteRepository()}, that returns "normalized" + * repository instances usable as keys. Also, in "key usage case" two instances of remote repository are + * considered equal if following stands: {@code Objects.equals(r1.toBareRemoteRepository(), r2.toBareRemoteRepository())}. + * + * @see org.eclipse.aether.RepositorySystem#newResolutionRepositories(RepositorySystemSession, List) + * @see org.eclipse.aether.RepositorySystem#newDeploymentRepository(RepositorySystemSession, RemoteRepository) + * @since 2.0.14 + */ + public enum Intent { + BARE, + RESOLUTION, + DEPLOYMENT + } private static final Pattern URL_PATTERN = Pattern.compile("([^:/]+(:[^:/]{2,}+(?=://))?):(//([^@/]*@)?([^/:]+))?.*"); @@ -60,6 +84,10 @@ public final class RemoteRepository implements ArtifactRepository { private final boolean blocked; + private final Intent intent; + + private final int hashCode; + RemoteRepository(Builder builder) { if (builder.prototype != null) { id = (builder.delta & Builder.ID) != 0 ? builder.id : builder.prototype.id; @@ -80,6 +108,7 @@ public final class RemoteRepository implements ArtifactRepository { mirroredRepositories = (builder.delta & Builder.MIRRORED) != 0 ? copy(builder.mirroredRepositories) : builder.prototype.mirroredRepositories; + intent = (builder.delta & Builder.INTENT) != 0 ? builder.intent : builder.prototype.intent; } else { id = builder.id; type = builder.type; @@ -91,17 +120,31 @@ public final class RemoteRepository implements ArtifactRepository { repositoryManager = builder.repositoryManager; blocked = builder.blocked; mirroredRepositories = copy(builder.mirroredRepositories); + intent = builder.intent; } Matcher m = URL_PATTERN.matcher(url); if (m.matches()) { - protocol = m.group(1); - String host = m.group(5); - this.host = (host != null) ? host : ""; + String h = m.group(5); + this.host = (h != null) ? h : ""; + this.protocol = m.group(1); } else { - protocol = ""; - host = ""; + this.host = ""; + this.protocol = ""; } + + this.hashCode = Objects.hash( + id, + type, + url, // host, protocol derived from url + releasePolicy, + snapshotPolicy, + proxy, + authentication, + mirroredRepositories, + repositoryManager, + blocked, + intent); } private static List copy(List repos) { @@ -111,10 +154,12 @@ private static List copy(List repos) { return Collections.unmodifiableList(Arrays.asList(repos.toArray(new RemoteRepository[0]))); } + @Override public String getId() { return id; } + @Override public String getContentType() { return type; } @@ -203,6 +248,16 @@ public boolean isBlocked() { return blocked; } + /** + * Returns the intent this repository is prepared for. + * + * @return The intent this repository is prepared for. + * @since 2.0.14 + */ + public Intent getIntent() { + return intent; + } + @Override public String toString() { StringBuilder buffer = new StringBuilder(256); @@ -225,6 +280,7 @@ public String toString() { if (isBlocked()) { buffer.append(", blocked"); } + buffer.append(", ").append(getIntent().name()).append(")"); buffer.append(")"); return buffer.toString(); } @@ -248,26 +304,39 @@ public boolean equals(Object obj) { && Objects.equals(proxy, that.proxy) && Objects.equals(authentication, that.authentication) && Objects.equals(mirroredRepositories, that.mirroredRepositories) - && repositoryManager == that.repositoryManager; + && repositoryManager == that.repositoryManager + && blocked == that.blocked + && intent == that.intent; } @Override public int hashCode() { - int hash = 17; - hash = hash * 31 + hash(url); - hash = hash * 31 + hash(type); - hash = hash * 31 + hash(id); - hash = hash * 31 + hash(releasePolicy); - hash = hash * 31 + hash(snapshotPolicy); - hash = hash * 31 + hash(proxy); - hash = hash * 31 + hash(authentication); - hash = hash * 31 + hash(mirroredRepositories); - hash = hash * 31 + (repositoryManager ? 1 : 0); - return hash; + return hashCode; } - private static int hash(Object obj) { - return obj != null ? obj.hashCode() : 0; + /** + * Makes "bare" repository out of this instance, usable as keys within one single session, by applying following + * changes to repository (returns new instance): + *

+ * These properties are managed by repository system, based on configuration. See {@link org.eclipse.aether.RepositorySystem} + * and (internal component) {@code org.eclipse.aether.impl.RemoteRepositoryManager}. + * + * @since 2.0.14 + */ + public RemoteRepository toBareRemoteRepository() { + return new Builder(this) + .setIntent(Intent.BARE) + .setProxy(null) + .setAuthentication(null) + .setMirroredRepositories(null) + .setRepositoryManager(false) + .build(); } /** @@ -286,7 +355,8 @@ public static final class Builder { AUTH = 0x0040, MIRRORED = 0x0080, REPOMAN = 0x0100, - BLOCKED = 0x0200; + BLOCKED = 0x0200, + INTENT = 0x0400; int delta; @@ -312,6 +382,8 @@ public static final class Builder { boolean blocked; + Intent intent = Intent.BARE; + /** * Creates a new repository builder. * @@ -545,5 +617,20 @@ public Builder setBlocked(boolean blocked) { } return this; } + + /** + * Marks the intent for this repository. + * + * @param intent the intent with this remote repository. + * @return This builder for chaining, never {@code null}. + * @since 2.0.14 + */ + public Builder setIntent(Intent intent) { + this.intent = intent; + if (prototype != null) { + delta(INTENT, this.intent, prototype.intent); + } + return this; + } } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManager.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManager.java index 6995ded82..3ad72aade 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManager.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRemoteRepositoryManager.java @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.List; import java.util.ListIterator; +import java.util.stream.Collectors; import org.eclipse.aether.RepositoryCache; import org.eclipse.aether.RepositorySystemSession; @@ -160,7 +161,11 @@ public List aggregateRepositories( result.add(repository); } - return result; + return result.stream() + .map(r -> new RemoteRepository.Builder(r) + .setIntent(RemoteRepository.Intent.RESOLUTION) + .build()) + .collect(Collectors.toList()); } private void logMirror(RepositorySystemSession session, RemoteRepository original, RemoteRepository mirror) { diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java index 90003a7c2..542fa5fa4 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java @@ -441,8 +441,8 @@ public List newResolutionRepositories( validateSession(session); validateRepositories(repositories); repositorySystemValidator.validateRemoteRepositories(session, repositories); - repositories = remoteRepositoryManager.aggregateRepositories(session, new ArrayList<>(), repositories, true); - return repositories; + + return remoteRepositoryManager.aggregateRepositories(session, new ArrayList<>(), repositories, true); } @Override @@ -450,12 +450,14 @@ public RemoteRepository newDeploymentRepository(RepositorySystemSession session, validateSession(session); requireNonNull(repository, "repository cannot be null"); repositorySystemValidator.validateRemoteRepositories(session, Collections.singletonList(repository)); - RemoteRepository.Builder builder = new RemoteRepository.Builder(repository); + Authentication auth = session.getAuthenticationSelector().getAuthentication(repository); - builder.setAuthentication(auth); Proxy proxy = session.getProxySelector().getProxy(repository); - builder.setProxy(proxy); - return builder.build(); + return new RemoteRepository.Builder(repository) + .setAuthentication(auth) + .setProxy(proxy) + .setIntent(RemoteRepository.Intent.DEPLOYMENT) + .build(); } @Override diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java index bb4c1e3ff..438c5eaa8 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceSupport.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Path; -import java.util.List; import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; @@ -79,26 +78,14 @@ protected Path getBasedir( } /** - * We use remote repositories as keys, but they may fly in as "bare" or as "equipped" (w/ auth and proxy) if caller - * used {@link org.eclipse.aether.RepositorySystem#newResolutionRepositories(RepositorySystemSession, List)} beforehand. - * The hash/equalTo method factors in all these as well, but from our perspective, they do not matter. So we make all - * key remote repositories back to "bare". - * Ignored properties of normalized repositories: - * + * We use remote repositories as keys, so normalize them. + * + * @since 2.0.14 + * @see RemoteRepository#toBareRemoteRepository() */ protected RemoteRepository normalizeRemoteRepository( RepositorySystemSession session, RemoteRepository remoteRepository) { - return new RemoteRepository.Builder(remoteRepository) - .setProxy(null) - .setAuthentication(null) - .setMirroredRepositories(null) - .setRepositoryManager(false) - .build(); + return remoteRepository.toBareRemoteRepository(); } /**