Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,32 @@

/**
* 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,}+(?=://))?):(//([^@/]*@)?([^/:]+))?.*");
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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) {
Expand All @@ -111,10 +154,12 @@ private static List<RemoteRepository> copy(List<RemoteRepository> repos) {
return Collections.unmodifiableList(Arrays.asList(repos.toArray(new RemoteRepository[0])));
}

@Override
public String getId() {
return id;
}

@Override
public String getContentType() {
return type;
}
Expand Down Expand Up @@ -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);
Expand All @@ -225,6 +280,7 @@ public String toString() {
if (isBlocked()) {
buffer.append(", blocked");
}
buffer.append(", ").append(getIntent().name()).append(")");
buffer.append(")");
return buffer.toString();
}
Expand All @@ -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):
* <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();
}

/**
Expand All @@ -286,7 +355,8 @@ public static final class Builder {
AUTH = 0x0040,
MIRRORED = 0x0080,
REPOMAN = 0x0100,
BLOCKED = 0x0200;
BLOCKED = 0x0200,
INTENT = 0x0400;

int delta;

Expand All @@ -312,6 +382,8 @@ public static final class Builder {

boolean blocked;

Intent intent = Intent.BARE;

/**
* Creates a new repository builder.
*
Expand Down Expand Up @@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -160,7 +161,11 @@ public List<RemoteRepository> 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,21 +441,23 @@ public List<RemoteRepository> 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
public RemoteRepository newDeploymentRepository(RepositorySystemSession session, RemoteRepository repository) {
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
* <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();
}

/**
Expand Down
Loading