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 3b15101d6 Remote repository intent (#1680)
3b15101d6 is described below
commit 3b15101d6f33edfd52a43f3f49cfd597c74294fb
Author: Tamas Cservenak <[email protected]>
AuthorDate: Thu Nov 20 17:48:34 2025 +0100
Remote repository intent (#1680)
Add new API to reflect the "intent" with given remote repository. Also,
_within session_ remote repositories may be used as keys (ie in a map), but
then use of new method IS MUST.
(This _immediately_ revealed bug in 2.0.13: triggered #1667; not after
fixes got merged)
---
.../aether/repository/RemoteRepository.java | 127 +++++++++++++++++----
.../impl/DefaultRemoteRepositoryManager.java | 7 +-
.../internal/impl/DefaultRepositorySystem.java | 14 ++-
.../RemoteRepositoryFilterSourceSupport.java | 23 +---
4 files changed, 126 insertions(+), 45 deletions(-)
diff --git
a/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java
b/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RemoteRepository.java
index 99cec7d2e..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 @@ import static java.util.Objects.requireNonNull;
/**
* A repository on a remote server.
+ * <p>
+ * 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".
+ * <p>
+ * <em>Important consequence:</em> 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<RemoteRepository> copy(List<RemoteRepository> repos) {
@@ -111,10 +154,12 @@ public final class RemoteRepository implements
ArtifactRepository {
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 final class RemoteRepository implements
ArtifactRepository {
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 final class RemoteRepository implements
ArtifactRepository {
if (isBlocked()) {
buffer.append(", blocked");
}
+ buffer.append(", ").append(getIntent().name()).append(")");
buffer.append(")");
return buffer.toString();
}
@@ -248,26 +304,39 @@ public final class RemoteRepository implements
ArtifactRepository {
&& 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):
+ * <ul>
+ * <li>sets intent to {@link Intent#BARE}</li>
+ * <li>nullifies proxy</li>
+ * <li>nullifies authentication</li>
+ * <li>nullifies mirrors</li>
+ * <li>sets repositoryManager to {@code false}</li>
+ * </ul>
+ * 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 final class RemoteRepository implements
ArtifactRepository {
AUTH = 0x0040,
MIRRORED = 0x0080,
REPOMAN = 0x0100,
- BLOCKED = 0x0200;
+ BLOCKED = 0x0200,
+ INTENT = 0x0400;
int delta;
@@ -312,6 +382,8 @@ public final class RemoteRepository implements
ArtifactRepository {
boolean blocked;
+ Intent intent = Intent.BARE;
+
/**
* Creates a new repository builder.
*
@@ -545,5 +617,20 @@ public final class RemoteRepository implements
ArtifactRepository {
}
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.ArrayList;
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 class DefaultRemoteRepositoryManager implements
RemoteRepositoryManager {
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 class DefaultRepositorySystem implements
RepositorySystem {
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 class DefaultRepositorySystem implements
RepositorySystem {
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 @@ package org.eclipse.aether.internal.impl.filter;
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 @@ public abstract class RemoteRepositoryFilterSourceSupport
implements RemoteRepos
}
/**
- * 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:
- * <ul>
- * <li>proxy - is environment dependent</li>
- * <li>authentication - is environment and/or user dependent</li>
- * <li>mirrored repositories - is environment dependent (within same
session does not change)</li>
- * <li>repository manager - is environment dependent (within same
session does not change)</li>
- * </ul>
+ * 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();
}
/**