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 170b04333 Bug: Filter fixes (#1655)
170b04333 is described below
commit 170b043336c9e26262a7e58db5b0f175db9a4f59
Author: Tamas Cservenak <[email protected]>
AuthorDate: Thu Nov 20 16:16:25 2025 +0100
Bug: Filter fixes (#1655)
Three major issues:
* (prefix) one was that it tried to fix the Maven issue of remote
repository uniqueness (globally), and that attempt was in fact wrong, fix is
elsewhere. The reason it tried to do this is in fact to circumvent locking
issues #1644.
* (manager) other issue was filter lifecycle, manager did it wrongly (data
is _inherited on customized sessions_): the bug caused that one session was
used to acquire filters and same filter got used for potentially other (maybe
even reconfigured) session. This overlook forced filters to implement hoops and
loops (to prevent recursion), but also prevented any third party code to have
saying in filter operation.
* (both filters) make sure RemoteRepositories used as keys for prefixes are
_same_ (normalized as bare)
Changes:
* fix manager to make sure filters and session are aligned
* fix/simplify prefix filter recursion prevention
* introduce public and documented way to inhibit prefix discovery
Note: as this PR now removes the "hack" (that in fact tried to circumvent
#1644) we are now back at state we were before
https://github.com/apache/maven-resolver/pull/1575 (the "by make repositories
unique" bit). As we see, this change (without locking fix) will cause Maven IT
failures, as ITs are executed in parallel, and "prefix discovery" initiated
over same local repository will/may cause locking conflicts and hence timeouts
(result is sporadically failing ITs due locking timeout [...]
Fixes #1654
Fixes #1667
---
.../DefaultRemoteRepositoryFilterManager.java | 4 +-
.../GroupIdRemoteRepositoryFilterSource.java | 40 ++++-
.../PrefixesRemoteRepositoryFilterSource.java | 167 +++++++++++++--------
.../RemoteRepositoryFilterSourceSupport.java | 25 +++
.../PrefixesRemoteRepositoryFilterSourceTest.java | 42 +++++-
.../RemoteRepositoryFilterSourceTestSupport.java | 10 +-
.../aether/util/repository/RepositoryIdHelper.java | 5 +
src/site/markdown/configuration.md | 6 +-
8 files changed, 220 insertions(+), 79 deletions(-)
diff --git
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/DefaultRemoteRepositoryFilterManager.java
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/DefaultRemoteRepositoryFilterManager.java
index 276153ce4..42f406416 100644
---
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/DefaultRemoteRepositoryFilterManager.java
+++
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/DefaultRemoteRepositoryFilterManager.java
@@ -59,7 +59,9 @@ public final class DefaultRemoteRepositoryFilterManager
implements RemoteReposit
@Override
public RemoteRepositoryFilter
getRemoteRepositoryFilter(RepositorySystemSession session) {
- return (RemoteRepositoryFilter)
session.getData().computeIfAbsent(INSTANCE_KEY, () -> {
+ // use session specific key to distinguish between "derived" sessions
+ String instanceSpecificKey = INSTANCE_KEY + "." + session.hashCode();
+ return (RemoteRepositoryFilter)
session.getData().computeIfAbsent(instanceSpecificKey, () -> {
HashMap<String, RemoteRepositoryFilter> filters = new HashMap<>();
for (Map.Entry<String, RemoteRepositoryFilterSource> entry :
sources.entrySet()) {
RemoteRepositoryFilter filter =
entry.getValue().getRemoteRepositoryFilter(session);
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 92585d35f..844616bcf 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
@@ -109,6 +109,21 @@ public final class GroupIdRemoteRepositoryFilterSource
extends RemoteRepositoryF
public static final boolean DEFAULT_ENABLED = true;
+ /**
+ * Configuration to skip the GroupId filter for given request. This
configuration is evaluated and if {@code true}
+ * the GroupId remote filter will not kick in.
+ *
+ * @since 2.0.14
+ * @configurationSource {@link
RepositorySystemSession#getConfigProperties()}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationRepoIdSuffix Yes
+ * @configurationDefaultValue {@link #DEFAULT_SKIPPED}
+ */
+ public static final String CONFIG_PROP_SKIPPED =
+ RemoteRepositoryFilterSourceSupport.CONFIG_PROPS_PREFIX + NAME +
".skipped";
+
+ public static final boolean DEFAULT_SKIPPED = false;
+
/**
* The basedir where to store filter files. If path is relative, it is
resolved from local repository root.
*
@@ -160,15 +175,22 @@ public final class GroupIdRemoteRepositoryFilterSource
extends RemoteRepositoryF
@Override
protected boolean isEnabled(RepositorySystemSession session) {
- return ConfigUtils.getBoolean(session, DEFAULT_ENABLED,
CONFIG_PROP_ENABLED);
+ return ConfigUtils.getBoolean(session, DEFAULT_ENABLED,
CONFIG_PROP_ENABLED)
+ && !ConfigUtils.getBoolean(session, DEFAULT_SKIPPED,
CONFIG_PROP_SKIPPED);
}
private boolean isRepositoryFilteringEnabled(RepositorySystemSession
session, RemoteRepository remoteRepository) {
if (isEnabled(session)) {
return ConfigUtils.getBoolean(
- session,
- ConfigUtils.getBoolean(session, true, CONFIG_PROP_ENABLED
+ ".*"),
- CONFIG_PROP_ENABLED + "." + remoteRepository.getId());
+ session,
+ DEFAULT_ENABLED,
+ CONFIG_PROP_ENABLED + "." +
remoteRepository.getId(),
+ CONFIG_PROP_ENABLED + ".*")
+ && !ConfigUtils.getBoolean(
+ session,
+ DEFAULT_SKIPPED,
+ CONFIG_PROP_SKIPPED + "." +
remoteRepository.getId(),
+ CONFIG_PROP_SKIPPED + ".*");
}
return false;
}
@@ -193,10 +215,11 @@ public final class GroupIdRemoteRepositoryFilterSource
extends RemoteRepositoryF
if (isRepositoryFilteringEnabled(session,
remoteRepository)) {
ruleFile(session, remoteRepository); // populate it;
needed for save
String line = "=" +
artifactResult.getArtifact().getGroupId();
+ RemoteRepository normalized =
normalizeRemoteRepository(session, remoteRepository);
recordedRules
- .computeIfAbsent(remoteRepository, k -> new
TreeSet<>())
+ .computeIfAbsent(normalized, k -> new
TreeSet<>())
.add(line);
- rules.compute(remoteRepository, (k, v) -> {
+ rules.compute(normalized, (k, v) -> {
if (v == null || v == GroupTree.SENTINEL) {
v = new GroupTree("");
}
@@ -210,7 +233,7 @@ public final class GroupIdRemoteRepositoryFilterSource
extends RemoteRepositoryF
}
private Path ruleFile(RepositorySystemSession session, RemoteRepository
remoteRepository) {
- return ruleFiles.computeIfAbsent(remoteRepository, r -> getBasedir(
+ return ruleFiles.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)
@@ -218,7 +241,8 @@ public final class GroupIdRemoteRepositoryFilterSource
extends RemoteRepositoryF
}
private GroupTree cacheRules(RepositorySystemSession session,
RemoteRepository remoteRepository) {
- return rules.computeIfAbsent(remoteRepository, r ->
loadRepositoryRules(session, r));
+ return rules.computeIfAbsent(
+ normalizeRemoteRepository(session, remoteRepository), r ->
loadRepositoryRules(session, r));
}
private GroupTree loadRepositoryRules(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 34698d310..806cfef96 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
@@ -25,7 +25,6 @@ import javax.inject.Singleton;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
-import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
@@ -106,9 +105,7 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
* <strong>Initial setup:</strong> Don't provide any files - rely on
auto-discovery as repositories are accessed.
* <strong>Override when needed:</strong> Create {@code
prefixes-myrepoId.txt} files in {@code .mvn/rrf/} and
* commit to version control.
- * <strong>Caching:</strong> Auto-discovered prefix files are cached in
the local repository with unique IDs
- * (using {@link
RepositoryIdHelper#remoteRepositoryUniqueId(RemoteRepository)}) to prevent
conflicts that
- * could cause build failures.
+ * <strong>Caching:</strong> Auto-discovered prefix files are cached in
the local repository.
*
* @configurationSource {@link
RepositorySystemSession#getConfigProperties()}
* @configurationType {@link java.lang.Boolean}
@@ -119,6 +116,58 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
public static final boolean DEFAULT_ENABLED = true;
+ /**
+ * Configuration to skip the Prefixes filter for given request. This
configuration is evaluated and if {@code true}
+ * 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 {@link #CONFIG_PROP_ENABLED} still show the
"real state".
+ *
+ * @since 2.0.14
+ * @configurationSource {@link
RepositorySystemSession#getConfigProperties()}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationRepoIdSuffix Yes
+ * @configurationDefaultValue {@link #DEFAULT_SKIPPED}
+ */
+ public static final String CONFIG_PROP_SKIPPED =
+ RemoteRepositoryFilterSourceSupport.CONFIG_PROPS_PREFIX + NAME +
".skipped";
+
+ public static final boolean DEFAULT_SKIPPED = false;
+
+ /**
+ * 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
+ * {@link #CONFIG_PROP_ENABLED} with repository ID suffix.
+ *
+ * @since 2.0.14
+ * @configurationSource {@link
RepositorySystemSession#getConfigProperties()}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationRepoIdSuffix Yes
+ * @configurationDefaultValue {@link #DEFAULT_USE_MIRRORED_REPOSITORIES}
+ */
+ public static final String CONFIG_PROP_USE_MIRRORED_REPOSITORIES =
+ RemoteRepositoryFilterSourceSupport.CONFIG_PROPS_PREFIX + NAME +
".useMirroredRepositories";
+
+ public static final boolean DEFAULT_USE_MIRRORED_REPOSITORIES = false;
+
+ /**
+ * 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
+ * {@link #CONFIG_PROP_ENABLED} with repository ID suffix.
+ * <em>Note: as of today, nothing sets this on remote repositories, but is
added for future.</em>
+ *
+ * @since 2.0.14
+ * @configurationSource {@link
RepositorySystemSession#getConfigProperties()}
+ * @configurationType {@link java.lang.Boolean}
+ * @configurationRepoIdSuffix Yes
+ * @configurationDefaultValue {@link #DEFAULT_USE_REPOSITORY_MANAGERS}
+ */
+ public static final String CONFIG_PROP_USE_REPOSITORY_MANAGERS =
+ RemoteRepositoryFilterSourceSupport.CONFIG_PROPS_PREFIX + NAME +
".useRepositoryManagers";
+
+ public static final boolean DEFAULT_USE_REPOSITORY_MANAGERS = false;
+
/**
* The basedir where to store filter files. If path is relative, it is
resolved from local repository root.
*
@@ -146,8 +195,6 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
private final ConcurrentHashMap<RemoteRepository, RepositoryLayout>
layouts;
- private final ConcurrentHashMap<RemoteRepository, Boolean> ongoingUpdates;
-
@Inject
public PrefixesRemoteRepositoryFilterSource(
Supplier<MetadataResolver> metadataResolver,
@@ -158,20 +205,26 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
this.repositoryLayoutProvider =
requireNonNull(repositoryLayoutProvider);
this.prefixes = new ConcurrentHashMap<>();
this.layouts = new ConcurrentHashMap<>();
- this.ongoingUpdates = new ConcurrentHashMap<>();
}
@Override
protected boolean isEnabled(RepositorySystemSession session) {
- return ConfigUtils.getBoolean(session, DEFAULT_ENABLED,
CONFIG_PROP_ENABLED);
+ return ConfigUtils.getBoolean(session, DEFAULT_ENABLED,
CONFIG_PROP_ENABLED)
+ && !ConfigUtils.getBoolean(session, DEFAULT_SKIPPED,
CONFIG_PROP_SKIPPED);
}
private boolean isRepositoryFilteringEnabled(RepositorySystemSession
session, RemoteRepository remoteRepository) {
if (isEnabled(session)) {
return ConfigUtils.getBoolean(
- session,
- ConfigUtils.getBoolean(session, true, CONFIG_PROP_ENABLED
+ ".*"),
- CONFIG_PROP_ENABLED + "." + remoteRepository.getId());
+ session,
+ DEFAULT_ENABLED,
+ CONFIG_PROP_ENABLED + "." +
remoteRepository.getId(),
+ CONFIG_PROP_ENABLED + ".*")
+ && !ConfigUtils.getBoolean(
+ session,
+ DEFAULT_SKIPPED,
+ CONFIG_PROP_SKIPPED + "." +
remoteRepository.getId(),
+ CONFIG_PROP_SKIPPED + ".*");
}
return false;
}
@@ -190,7 +243,7 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
* @return the layout instance of {@code null} if layout not supported.
*/
private RepositoryLayout cacheLayout(RepositorySystemSession session,
RemoteRepository remoteRepository) {
- return layouts.computeIfAbsent(remoteRepository, r -> {
+ return layouts.computeIfAbsent(normalizeRemoteRepository(session,
remoteRepository), r -> {
try {
return repositoryLayoutProvider.newRepositoryLayout(session,
remoteRepository);
} catch (NoRepositoryLayoutException e) {
@@ -201,22 +254,9 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
private PrefixTree cachePrefixTree(
RepositorySystemSession session, Path basedir, RemoteRepository
remoteRepository) {
- return ongoingUpdatesGuard(
- remoteRepository,
- () -> prefixes.computeIfAbsent(
- remoteRepository, r -> loadPrefixTree(session,
basedir, remoteRepository)),
- () -> PrefixTree.SENTINEL);
- }
-
- private <T> T ongoingUpdatesGuard(RemoteRepository remoteRepository,
Supplier<T> unblocked, Supplier<T> blocked) {
- if (!remoteRepository.isBlocked() && null ==
ongoingUpdates.putIfAbsent(remoteRepository, Boolean.TRUE)) {
- try {
- return unblocked.get();
- } finally {
- ongoingUpdates.remove(remoteRepository);
- }
- }
- return blocked.get();
+ return prefixes.computeIfAbsent(
+ normalizeRemoteRepository(session, remoteRepository),
+ r -> loadPrefixTree(session, basedir, remoteRepository));
}
private PrefixTree loadPrefixTree(
@@ -225,8 +265,12 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
String origin = "user-provided";
Path filePath = resolvePrefixesFromLocalConfiguration(session,
baseDir, remoteRepository);
if (filePath == null) {
- origin = "auto-discovered";
- filePath = resolvePrefixesFromRemoteRepository(session,
remoteRepository);
+ if (!supportedResolvePrefixesForRemoteRepository(session,
remoteRepository)) {
+ origin = "unsupported";
+ } else {
+ origin = "auto-discovered";
+ filePath = resolvePrefixesFromRemoteRepository(session,
remoteRepository);
+ }
}
if (filePath != null) {
PrefixesSource prefixesSource =
PrefixesSource.of(remoteRepository, filePath);
@@ -273,6 +317,18 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
}
}
+ private boolean supportedResolvePrefixesForRemoteRepository(
+ RepositorySystemSession session, RemoteRepository
remoteRepository) {
+ if (remoteRepository.isRepositoryManager()) {
+ return ConfigUtils.getBoolean(
+ session, DEFAULT_USE_REPOSITORY_MANAGERS,
CONFIG_PROP_USE_REPOSITORY_MANAGERS);
+ } else {
+ return remoteRepository.getMirroredRepositories().isEmpty()
+ || ConfigUtils.getBoolean(
+ session, DEFAULT_USE_MIRRORED_REPOSITORIES,
CONFIG_PROP_USE_MIRRORED_REPOSITORIES);
+ }
+ }
+
private Path resolvePrefixesFromRemoteRepository(
RepositorySystemSession session, RemoteRepository
remoteRepository) {
MetadataResolver mr = metadataResolver.get();
@@ -282,35 +338,22 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
RemoteRepository prepared = rm.aggregateRepositories(
session, Collections.emptyList(),
Collections.singletonList(remoteRepository), true)
.get(0);
- // make it unique
- RemoteRepository unique = new RemoteRepository.Builder(prepared)
-
.setId(RepositoryIdHelper.remoteRepositoryUniqueId(remoteRepository))
- .build();
- // supplier for path
- Supplier<Path> supplier = () -> {
- MetadataRequest request =
- new MetadataRequest(new
DefaultMetadata(PREFIX_FILE_PATH, Metadata.Nature.RELEASE_OR_SNAPSHOT));
- // use unique repository; this will result in prefix
(repository metadata) cached under unique id
- request.setRepository(unique);
- request.setDeleteLocalCopyIfMissing(true);
- request.setFavorLocalRepository(true);
- MetadataResult result = mr.resolveMetadata(
- new
DefaultRepositorySystemSession(session).setTransferListener(null),
- Collections.singleton(request))
- .get(0);
- if (result.isResolved()) {
- return result.getMetadata().getPath();
- } else {
- return null;
- }
- };
-
- // prevent recursive calls; but we need extra work if not dealing
with Central (as in that case outer call
- // shields us)
- if (Objects.equals(prepared.getId(), unique.getId())) {
- return supplier.get();
+ // retrieve prefix as metadata from repository
+ MetadataRequest request =
+ new MetadataRequest(new DefaultMetadata(PREFIX_FILE_PATH,
Metadata.Nature.RELEASE_OR_SNAPSHOT));
+ request.setRepository(prepared);
+ request.setDeleteLocalCopyIfMissing(true);
+ request.setFavorLocalRepository(true);
+ MetadataResult result = mr.resolveMetadata(
+ new DefaultRepositorySystemSession(session)
+ .setTransferListener(null)
+ .setConfigProperty(CONFIG_PROP_SKIPPED,
Boolean.TRUE.toString()),
+ Collections.singleton(request))
+ .get(0);
+ if (result.isResolved()) {
+ return result.getMetadata().getPath();
} else {
- return ongoingUpdatesGuard(unique, supplier, () -> null);
+ return null;
}
}
return null;
@@ -347,15 +390,15 @@ public final class PrefixesRemoteRepositoryFilterSource
extends RemoteRepository
repositoryLayout.getLocation(metadata, false).getPath());
}
- private Result acceptPrefix(RemoteRepository remoteRepository, String
path) {
- PrefixTree prefixTree = cachePrefixTree(session, basedir,
remoteRepository);
+ private Result acceptPrefix(RemoteRepository repository, String path) {
+ PrefixTree prefixTree = cachePrefixTree(session, basedir,
repository);
if (PrefixTree.SENTINEL == prefixTree) {
return NOT_PRESENT_RESULT;
}
if (prefixTree.acceptedPath(path)) {
- return new SimpleResult(true, "Path " + path + " allowed from
" + remoteRepository);
+ return new SimpleResult(true, "Path " + path + " allowed from
" + repository);
} else {
- return new SimpleResult(false, "Prefix " + path + " NOT
allowed from " + remoteRepository);
+ return new SimpleResult(false, "Path " + path + " NOT allowed
from " + repository);
}
}
}
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 5d1744920..bb4c1e3ff 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,9 +21,11 @@ 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;
+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.util.DirectoryUtils;
@@ -76,6 +78,29 @@ 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>
+ */
+ protected RemoteRepository normalizeRemoteRepository(
+ RepositorySystemSession session, RemoteRepository
remoteRepository) {
+ return new RemoteRepository.Builder(remoteRepository)
+ .setProxy(null)
+ .setAuthentication(null)
+ .setMirroredRepositories(null)
+ .setRepositoryManager(false)
+ .build();
+ }
+
/**
* Simple {@link RemoteRepositoryFilter.Result} immutable implementation.
*/
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 4db77039b..7d7c21782 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
@@ -25,6 +25,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
@@ -35,11 +36,17 @@ import
org.eclipse.aether.internal.impl.DefaultArtifactPredicateFactory;
import org.eclipse.aether.internal.impl.DefaultRepositoryLayoutProvider;
import org.eclipse.aether.internal.impl.Maven2RepositoryLayoutFactory;
import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.resolution.MetadataRequest;
import org.eclipse.aether.resolution.MetadataResult;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
+import org.junit.jupiter.api.Test;
import static
org.eclipse.aether.internal.impl.checksum.Checksums.checksumsSelector;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -54,9 +61,24 @@ public class PrefixesRemoteRepositoryFilterSourceTest
extends RemoteRepositoryFi
// in test we do not resolve; just reply failed resolution
MetadataResult failed = new MetadataResult(new MetadataRequest());
MetadataResolver metadataResolver = mock(MetadataResolver.class);
- RemoteRepositoryManager remoteRepositoryManager =
mock(RemoteRepositoryManager.class);
+ RemoteRepositoryManager remoteRepositoryManager = new
RemoteRepositoryManager() {
+ @Override
+ public List<RemoteRepository> aggregateRepositories(
+ RepositorySystemSession session,
+ List<RemoteRepository> dominantRepositories,
+ List<RemoteRepository> recessiveRepositories,
+ boolean recessiveIsRaw) {
+ return recessiveRepositories;
+ }
+
+ @Override
+ public RepositoryPolicy getPolicy(
+ RepositorySystemSession session, RemoteRepository
repository, boolean releases, boolean snapshots) {
+ throw new UnsupportedOperationException("not implemented");
+ }
+ };
when(metadataResolver.resolveMetadata(any(RepositorySystemSession.class),
any(Collection.class)))
- .thenReturn(Collections.singletonList(failed));
+ .thenThrow(new IllegalStateException("should not enter here"));
DefaultRepositoryLayoutProvider layoutProvider = new
DefaultRepositoryLayoutProvider(Collections.singletonMap(
Maven2RepositoryLayoutFactory.NAME,
new Maven2RepositoryLayoutFactory(
@@ -91,4 +113,20 @@ public class PrefixesRemoteRepositoryFilterSourceTest
extends RemoteRepositoryFi
throw new UncheckedIOException(e);
}
}
+
+ @Test
+ void notAcceptedArtifactFromMirror() {
+ RemoteRepository mirror = new RemoteRepository.Builder("mirror",
"default", "https://irrelevant.com")
+ .addMirroredRepository(remoteRepository)
+ .build();
+ enableSource(session, true);
+
+ RemoteRepositoryFilter filter =
subject.getRemoteRepositoryFilter(session);
+ assertNotNull(filter);
+
+ RemoteRepositoryFilter.Result result = filter.acceptArtifact(mirror,
acceptedArtifact);
+
+ assertTrue(result.isAccepted());
+ assertEquals("Prefix file not present", result.reasoning());
+ }
}
diff --git
a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceTestSupport.java
b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceTestSupport.java
index 9a149585f..c364aac3b 100644
---
a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceTestSupport.java
+++
b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/RemoteRepositoryFilterSourceTestSupport.java
@@ -34,15 +34,15 @@ import static org.junit.jupiter.api.Assertions.*;
* UT helper for {@link RemoteRepositoryFilterSource} UTs.
*/
public abstract class RemoteRepositoryFilterSourceTestSupport {
- private final Artifact acceptedArtifact = new
DefaultArtifact("org.one:aid:1.0");
+ protected final Artifact acceptedArtifact = new
DefaultArtifact("org.one:aid:1.0");
- private final Artifact notAcceptedArtifact = new
DefaultArtifact("org.two:aid:1.0");
+ protected final Artifact notAcceptedArtifact = new
DefaultArtifact("org.two:aid:1.0");
- private DefaultRepositorySystemSession session;
+ protected DefaultRepositorySystemSession session;
- private RemoteRepository remoteRepository;
+ protected RemoteRepository remoteRepository;
- private RemoteRepositoryFilterSource subject;
+ protected RemoteRepositoryFilterSource subject;
@BeforeEach
void setup() {
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 edcf42e8e..5c8bbbbcd 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
@@ -75,6 +75,11 @@ public final class RepositoryIdHelper {
* 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>.
*/
public static String remoteRepositoryUniqueId(RemoteRepository repository)
{
if (CENTRAL_DIRECT_ONLY.test(repository)) {
diff --git a/src/site/markdown/configuration.md
b/src/site/markdown/configuration.md
index e30b9316e..7e1d84040 100644
--- a/src/site/markdown/configuration.md
+++ b/src/site/markdown/configuration.md
@@ -101,8 +101,12 @@ To modify this file, edit the template and regenerate.
| `"aether.remoteRepositoryFilter.groupId"` | `Boolean` | Configuration to
enable the GroupId filter (enabled by default). Can be fine-tuned per
repository using repository ID suffixes. <strong>Important:</strong> For this
filter to take effect, you must provide configuration files. Without
configuration files, the enabled filter remains dormant and does not interfere
with resolution. <strong>Configuration Files:</strong> <ul> <li>Location:
Directory specified by <code>#CONFIG_PROP_BASED [...]
| `"aether.remoteRepositoryFilter.groupId.basedir"` | `String` | The basedir
where to store filter files. If path is relative, it is resolved from local
repository root. | `".remoteRepositoryFilters"` | 1.9.0 | No | Session
Configuration |
| `"aether.remoteRepositoryFilter.groupId.record"` | `Boolean` | Should filter
go into "record" mode (and collect encountered artifacts)? | `false` | 1.9.0
| No | Session Configuration |
-| `"aether.remoteRepositoryFilter.prefixes"` | `Boolean` | Configuration to
enable the Prefixes filter (enabled by default). Can be fine-tuned per
repository using repository ID suffixes. <strong>Important:</strong> For this
filter to take effect, configuration files must be available. Without
configuration files, the enabled filter remains dormant and does not interfere
with resolution. <strong>Configuration File Resolution:</strong> <ol>
<li><strong>User-provided files:</strong> Checke [...]
+| `"aether.remoteRepositoryFilter.groupId.skipped"` | `Boolean` |
Configuration to skip the GroupId filter for given request. This configuration
is evaluated and if <code>true</code> the GroupId remote filter will not kick
in. | `false` | 2.0.14 | Yes | Session Configuration |
+| `"aether.remoteRepositoryFilter.prefixes"` | `Boolean` | Configuration to
enable the Prefixes filter (enabled by default). Can be fine-tuned per
repository using repository ID suffixes. <strong>Important:</strong> For this
filter to take effect, configuration files must be available. Without
configuration files, the enabled filter remains dormant and does not interfere
with resolution. <strong>Configuration File Resolution:</strong> <ol>
<li><strong>User-provided files:</strong> Checke [...]
| `"aether.remoteRepositoryFilter.prefixes.basedir"` | `String` | The basedir
where to store filter files. If path is relative, it is resolved from local
repository root. | `".remoteRepositoryFilters"` | 1.9.0 | No | Session
Configuration |
+| `"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.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 |