Skip to content

Commit 170b043

Browse files
authored
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 #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 timeouts). Hence, this fix is to be followed by fix for #1644 Fixes #1654 Fixes #1667
1 parent 471c7f4 commit 170b043

File tree

8 files changed

+220
-79
lines changed

8 files changed

+220
-79
lines changed

maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/DefaultRemoteRepositoryFilterManager.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ public DefaultRemoteRepositoryFilterManager(Map<String, RemoteRepositoryFilterSo
5959

6060
@Override
6161
public RemoteRepositoryFilter getRemoteRepositoryFilter(RepositorySystemSession session) {
62-
return (RemoteRepositoryFilter) session.getData().computeIfAbsent(INSTANCE_KEY, () -> {
62+
// use session specific key to distinguish between "derived" sessions
63+
String instanceSpecificKey = INSTANCE_KEY + "." + session.hashCode();
64+
return (RemoteRepositoryFilter) session.getData().computeIfAbsent(instanceSpecificKey, () -> {
6365
HashMap<String, RemoteRepositoryFilter> filters = new HashMap<>();
6466
for (Map.Entry<String, RemoteRepositoryFilterSource> entry : sources.entrySet()) {
6567
RemoteRepositoryFilter filter = entry.getValue().getRemoteRepositoryFilter(session);

maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,21 @@ public final class GroupIdRemoteRepositoryFilterSource extends RemoteRepositoryF
109109

110110
public static final boolean DEFAULT_ENABLED = true;
111111

112+
/**
113+
* Configuration to skip the GroupId filter for given request. This configuration is evaluated and if {@code true}
114+
* the GroupId remote filter will not kick in.
115+
*
116+
* @since 2.0.14
117+
* @configurationSource {@link RepositorySystemSession#getConfigProperties()}
118+
* @configurationType {@link java.lang.Boolean}
119+
* @configurationRepoIdSuffix Yes
120+
* @configurationDefaultValue {@link #DEFAULT_SKIPPED}
121+
*/
122+
public static final String CONFIG_PROP_SKIPPED =
123+
RemoteRepositoryFilterSourceSupport.CONFIG_PROPS_PREFIX + NAME + ".skipped";
124+
125+
public static final boolean DEFAULT_SKIPPED = false;
126+
112127
/**
113128
* The basedir where to store filter files. If path is relative, it is resolved from local repository root.
114129
*
@@ -160,15 +175,22 @@ public GroupIdRemoteRepositoryFilterSource(
160175

161176
@Override
162177
protected boolean isEnabled(RepositorySystemSession session) {
163-
return ConfigUtils.getBoolean(session, DEFAULT_ENABLED, CONFIG_PROP_ENABLED);
178+
return ConfigUtils.getBoolean(session, DEFAULT_ENABLED, CONFIG_PROP_ENABLED)
179+
&& !ConfigUtils.getBoolean(session, DEFAULT_SKIPPED, CONFIG_PROP_SKIPPED);
164180
}
165181

166182
private boolean isRepositoryFilteringEnabled(RepositorySystemSession session, RemoteRepository remoteRepository) {
167183
if (isEnabled(session)) {
168184
return ConfigUtils.getBoolean(
169-
session,
170-
ConfigUtils.getBoolean(session, true, CONFIG_PROP_ENABLED + ".*"),
171-
CONFIG_PROP_ENABLED + "." + remoteRepository.getId());
185+
session,
186+
DEFAULT_ENABLED,
187+
CONFIG_PROP_ENABLED + "." + remoteRepository.getId(),
188+
CONFIG_PROP_ENABLED + ".*")
189+
&& !ConfigUtils.getBoolean(
190+
session,
191+
DEFAULT_SKIPPED,
192+
CONFIG_PROP_SKIPPED + "." + remoteRepository.getId(),
193+
CONFIG_PROP_SKIPPED + ".*");
172194
}
173195
return false;
174196
}
@@ -193,10 +215,11 @@ public void postProcess(RepositorySystemSession session, List<ArtifactResult> ar
193215
if (isRepositoryFilteringEnabled(session, remoteRepository)) {
194216
ruleFile(session, remoteRepository); // populate it; needed for save
195217
String line = "=" + artifactResult.getArtifact().getGroupId();
218+
RemoteRepository normalized = normalizeRemoteRepository(session, remoteRepository);
196219
recordedRules
197-
.computeIfAbsent(remoteRepository, k -> new TreeSet<>())
220+
.computeIfAbsent(normalized, k -> new TreeSet<>())
198221
.add(line);
199-
rules.compute(remoteRepository, (k, v) -> {
222+
rules.compute(normalized, (k, v) -> {
200223
if (v == null || v == GroupTree.SENTINEL) {
201224
v = new GroupTree("");
202225
}
@@ -210,15 +233,16 @@ public void postProcess(RepositorySystemSession session, List<ArtifactResult> ar
210233
}
211234

212235
private Path ruleFile(RepositorySystemSession session, RemoteRepository remoteRepository) {
213-
return ruleFiles.computeIfAbsent(remoteRepository, r -> getBasedir(
236+
return ruleFiles.computeIfAbsent(normalizeRemoteRepository(session, remoteRepository), r -> getBasedir(
214237
session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, false)
215238
.resolve(GROUP_ID_FILE_PREFIX
216239
+ RepositoryIdHelper.cachedIdToPathSegment(session).apply(remoteRepository)
217240
+ GROUP_ID_FILE_SUFFIX));
218241
}
219242

220243
private GroupTree cacheRules(RepositorySystemSession session, RemoteRepository remoteRepository) {
221-
return rules.computeIfAbsent(remoteRepository, r -> loadRepositoryRules(session, r));
244+
return rules.computeIfAbsent(
245+
normalizeRemoteRepository(session, remoteRepository), r -> loadRepositoryRules(session, r));
222246
}
223247

224248
private GroupTree loadRepositoryRules(RepositorySystemSession session, RemoteRepository remoteRepository) {

maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java

Lines changed: 105 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import java.nio.file.Files;
2626
import java.nio.file.Path;
2727
import java.util.Collections;
28-
import java.util.Objects;
2928
import java.util.concurrent.ConcurrentHashMap;
3029
import java.util.function.Supplier;
3130

@@ -106,9 +105,7 @@ public final class PrefixesRemoteRepositoryFilterSource extends RemoteRepository
106105
* <strong>Initial setup:</strong> Don't provide any files - rely on auto-discovery as repositories are accessed.
107106
* <strong>Override when needed:</strong> Create {@code prefixes-myrepoId.txt} files in {@code .mvn/rrf/} and
108107
* commit to version control.
109-
* <strong>Caching:</strong> Auto-discovered prefix files are cached in the local repository with unique IDs
110-
* (using {@link RepositoryIdHelper#remoteRepositoryUniqueId(RemoteRepository)}) to prevent conflicts that
111-
* could cause build failures.
108+
* <strong>Caching:</strong> Auto-discovered prefix files are cached in the local repository.
112109
*
113110
* @configurationSource {@link RepositorySystemSession#getConfigProperties()}
114111
* @configurationType {@link java.lang.Boolean}
@@ -119,6 +116,58 @@ public final class PrefixesRemoteRepositoryFilterSource extends RemoteRepository
119116

120117
public static final boolean DEFAULT_ENABLED = true;
121118

119+
/**
120+
* Configuration to skip the Prefixes filter for given request. This configuration is evaluated and if {@code true}
121+
* the prefixes remote filter will not kick in. Main use case is by filter itself, to prevent recursion during
122+
* discovery of remote prefixes file, but this also allows other components to control prefix filter discovery, while
123+
* leaving configuration like {@link #CONFIG_PROP_ENABLED} still show the "real state".
124+
*
125+
* @since 2.0.14
126+
* @configurationSource {@link RepositorySystemSession#getConfigProperties()}
127+
* @configurationType {@link java.lang.Boolean}
128+
* @configurationRepoIdSuffix Yes
129+
* @configurationDefaultValue {@link #DEFAULT_SKIPPED}
130+
*/
131+
public static final String CONFIG_PROP_SKIPPED =
132+
RemoteRepositoryFilterSourceSupport.CONFIG_PROPS_PREFIX + NAME + ".skipped";
133+
134+
public static final boolean DEFAULT_SKIPPED = false;
135+
136+
/**
137+
* Configuration to allow Prefixes filter to auto-discover prefixes from mirrored repositories as well. For this to
138+
* work <em>Maven should be aware</em> that given remote repository is mirror and is usually backed by MRM. Given
139+
* multiple MRM implementations messes up prefixes file, is better to just skip these. In other case, one may use
140+
* {@link #CONFIG_PROP_ENABLED} with repository ID suffix.
141+
*
142+
* @since 2.0.14
143+
* @configurationSource {@link RepositorySystemSession#getConfigProperties()}
144+
* @configurationType {@link java.lang.Boolean}
145+
* @configurationRepoIdSuffix Yes
146+
* @configurationDefaultValue {@link #DEFAULT_USE_MIRRORED_REPOSITORIES}
147+
*/
148+
public static final String CONFIG_PROP_USE_MIRRORED_REPOSITORIES =
149+
RemoteRepositoryFilterSourceSupport.CONFIG_PROPS_PREFIX + NAME + ".useMirroredRepositories";
150+
151+
public static final boolean DEFAULT_USE_MIRRORED_REPOSITORIES = false;
152+
153+
/**
154+
* Configuration to allow Prefixes filter to auto-discover prefixes from repository managers as well. For this to
155+
* work <em>Maven should be aware</em> that given remote repository is backed by repository manager.
156+
* Given multiple MRM implementations messes up prefixes file, is better to just skip these. In other case, one may use
157+
* {@link #CONFIG_PROP_ENABLED} with repository ID suffix.
158+
* <em>Note: as of today, nothing sets this on remote repositories, but is added for future.</em>
159+
*
160+
* @since 2.0.14
161+
* @configurationSource {@link RepositorySystemSession#getConfigProperties()}
162+
* @configurationType {@link java.lang.Boolean}
163+
* @configurationRepoIdSuffix Yes
164+
* @configurationDefaultValue {@link #DEFAULT_USE_REPOSITORY_MANAGERS}
165+
*/
166+
public static final String CONFIG_PROP_USE_REPOSITORY_MANAGERS =
167+
RemoteRepositoryFilterSourceSupport.CONFIG_PROPS_PREFIX + NAME + ".useRepositoryManagers";
168+
169+
public static final boolean DEFAULT_USE_REPOSITORY_MANAGERS = false;
170+
122171
/**
123172
* The basedir where to store filter files. If path is relative, it is resolved from local repository root.
124173
*
@@ -146,8 +195,6 @@ public final class PrefixesRemoteRepositoryFilterSource extends RemoteRepository
146195

147196
private final ConcurrentHashMap<RemoteRepository, RepositoryLayout> layouts;
148197

149-
private final ConcurrentHashMap<RemoteRepository, Boolean> ongoingUpdates;
150-
151198
@Inject
152199
public PrefixesRemoteRepositoryFilterSource(
153200
Supplier<MetadataResolver> metadataResolver,
@@ -158,20 +205,26 @@ public PrefixesRemoteRepositoryFilterSource(
158205
this.repositoryLayoutProvider = requireNonNull(repositoryLayoutProvider);
159206
this.prefixes = new ConcurrentHashMap<>();
160207
this.layouts = new ConcurrentHashMap<>();
161-
this.ongoingUpdates = new ConcurrentHashMap<>();
162208
}
163209

164210
@Override
165211
protected boolean isEnabled(RepositorySystemSession session) {
166-
return ConfigUtils.getBoolean(session, DEFAULT_ENABLED, CONFIG_PROP_ENABLED);
212+
return ConfigUtils.getBoolean(session, DEFAULT_ENABLED, CONFIG_PROP_ENABLED)
213+
&& !ConfigUtils.getBoolean(session, DEFAULT_SKIPPED, CONFIG_PROP_SKIPPED);
167214
}
168215

169216
private boolean isRepositoryFilteringEnabled(RepositorySystemSession session, RemoteRepository remoteRepository) {
170217
if (isEnabled(session)) {
171218
return ConfigUtils.getBoolean(
172-
session,
173-
ConfigUtils.getBoolean(session, true, CONFIG_PROP_ENABLED + ".*"),
174-
CONFIG_PROP_ENABLED + "." + remoteRepository.getId());
219+
session,
220+
DEFAULT_ENABLED,
221+
CONFIG_PROP_ENABLED + "." + remoteRepository.getId(),
222+
CONFIG_PROP_ENABLED + ".*")
223+
&& !ConfigUtils.getBoolean(
224+
session,
225+
DEFAULT_SKIPPED,
226+
CONFIG_PROP_SKIPPED + "." + remoteRepository.getId(),
227+
CONFIG_PROP_SKIPPED + ".*");
175228
}
176229
return false;
177230
}
@@ -190,7 +243,7 @@ public RemoteRepositoryFilter getRemoteRepositoryFilter(RepositorySystemSession
190243
* @return the layout instance of {@code null} if layout not supported.
191244
*/
192245
private RepositoryLayout cacheLayout(RepositorySystemSession session, RemoteRepository remoteRepository) {
193-
return layouts.computeIfAbsent(remoteRepository, r -> {
246+
return layouts.computeIfAbsent(normalizeRemoteRepository(session, remoteRepository), r -> {
194247
try {
195248
return repositoryLayoutProvider.newRepositoryLayout(session, remoteRepository);
196249
} catch (NoRepositoryLayoutException e) {
@@ -201,22 +254,9 @@ private RepositoryLayout cacheLayout(RepositorySystemSession session, RemoteRepo
201254

202255
private PrefixTree cachePrefixTree(
203256
RepositorySystemSession session, Path basedir, RemoteRepository remoteRepository) {
204-
return ongoingUpdatesGuard(
205-
remoteRepository,
206-
() -> prefixes.computeIfAbsent(
207-
remoteRepository, r -> loadPrefixTree(session, basedir, remoteRepository)),
208-
() -> PrefixTree.SENTINEL);
209-
}
210-
211-
private <T> T ongoingUpdatesGuard(RemoteRepository remoteRepository, Supplier<T> unblocked, Supplier<T> blocked) {
212-
if (!remoteRepository.isBlocked() && null == ongoingUpdates.putIfAbsent(remoteRepository, Boolean.TRUE)) {
213-
try {
214-
return unblocked.get();
215-
} finally {
216-
ongoingUpdates.remove(remoteRepository);
217-
}
218-
}
219-
return blocked.get();
257+
return prefixes.computeIfAbsent(
258+
normalizeRemoteRepository(session, remoteRepository),
259+
r -> loadPrefixTree(session, basedir, remoteRepository));
220260
}
221261

222262
private PrefixTree loadPrefixTree(
@@ -225,8 +265,12 @@ private PrefixTree loadPrefixTree(
225265
String origin = "user-provided";
226266
Path filePath = resolvePrefixesFromLocalConfiguration(session, baseDir, remoteRepository);
227267
if (filePath == null) {
228-
origin = "auto-discovered";
229-
filePath = resolvePrefixesFromRemoteRepository(session, remoteRepository);
268+
if (!supportedResolvePrefixesForRemoteRepository(session, remoteRepository)) {
269+
origin = "unsupported";
270+
} else {
271+
origin = "auto-discovered";
272+
filePath = resolvePrefixesFromRemoteRepository(session, remoteRepository);
273+
}
230274
}
231275
if (filePath != null) {
232276
PrefixesSource prefixesSource = PrefixesSource.of(remoteRepository, filePath);
@@ -273,6 +317,18 @@ private Path resolvePrefixesFromLocalConfiguration(
273317
}
274318
}
275319

320+
private boolean supportedResolvePrefixesForRemoteRepository(
321+
RepositorySystemSession session, RemoteRepository remoteRepository) {
322+
if (remoteRepository.isRepositoryManager()) {
323+
return ConfigUtils.getBoolean(
324+
session, DEFAULT_USE_REPOSITORY_MANAGERS, CONFIG_PROP_USE_REPOSITORY_MANAGERS);
325+
} else {
326+
return remoteRepository.getMirroredRepositories().isEmpty()
327+
|| ConfigUtils.getBoolean(
328+
session, DEFAULT_USE_MIRRORED_REPOSITORIES, CONFIG_PROP_USE_MIRRORED_REPOSITORIES);
329+
}
330+
}
331+
276332
private Path resolvePrefixesFromRemoteRepository(
277333
RepositorySystemSession session, RemoteRepository remoteRepository) {
278334
MetadataResolver mr = metadataResolver.get();
@@ -282,35 +338,22 @@ private Path resolvePrefixesFromRemoteRepository(
282338
RemoteRepository prepared = rm.aggregateRepositories(
283339
session, Collections.emptyList(), Collections.singletonList(remoteRepository), true)
284340
.get(0);
285-
// make it unique
286-
RemoteRepository unique = new RemoteRepository.Builder(prepared)
287-
.setId(RepositoryIdHelper.remoteRepositoryUniqueId(remoteRepository))
288-
.build();
289-
// supplier for path
290-
Supplier<Path> supplier = () -> {
291-
MetadataRequest request =
292-
new MetadataRequest(new DefaultMetadata(PREFIX_FILE_PATH, Metadata.Nature.RELEASE_OR_SNAPSHOT));
293-
// use unique repository; this will result in prefix (repository metadata) cached under unique id
294-
request.setRepository(unique);
295-
request.setDeleteLocalCopyIfMissing(true);
296-
request.setFavorLocalRepository(true);
297-
MetadataResult result = mr.resolveMetadata(
298-
new DefaultRepositorySystemSession(session).setTransferListener(null),
299-
Collections.singleton(request))
300-
.get(0);
301-
if (result.isResolved()) {
302-
return result.getMetadata().getPath();
303-
} else {
304-
return null;
305-
}
306-
};
307-
308-
// prevent recursive calls; but we need extra work if not dealing with Central (as in that case outer call
309-
// shields us)
310-
if (Objects.equals(prepared.getId(), unique.getId())) {
311-
return supplier.get();
341+
// retrieve prefix as metadata from repository
342+
MetadataRequest request =
343+
new MetadataRequest(new DefaultMetadata(PREFIX_FILE_PATH, Metadata.Nature.RELEASE_OR_SNAPSHOT));
344+
request.setRepository(prepared);
345+
request.setDeleteLocalCopyIfMissing(true);
346+
request.setFavorLocalRepository(true);
347+
MetadataResult result = mr.resolveMetadata(
348+
new DefaultRepositorySystemSession(session)
349+
.setTransferListener(null)
350+
.setConfigProperty(CONFIG_PROP_SKIPPED, Boolean.TRUE.toString()),
351+
Collections.singleton(request))
352+
.get(0);
353+
if (result.isResolved()) {
354+
return result.getMetadata().getPath();
312355
} else {
313-
return ongoingUpdatesGuard(unique, supplier, () -> null);
356+
return null;
314357
}
315358
}
316359
return null;
@@ -347,15 +390,15 @@ public Result acceptMetadata(RemoteRepository remoteRepository, Metadata metadat
347390
repositoryLayout.getLocation(metadata, false).getPath());
348391
}
349392

350-
private Result acceptPrefix(RemoteRepository remoteRepository, String path) {
351-
PrefixTree prefixTree = cachePrefixTree(session, basedir, remoteRepository);
393+
private Result acceptPrefix(RemoteRepository repository, String path) {
394+
PrefixTree prefixTree = cachePrefixTree(session, basedir, repository);
352395
if (PrefixTree.SENTINEL == prefixTree) {
353396
return NOT_PRESENT_RESULT;
354397
}
355398
if (prefixTree.acceptedPath(path)) {
356-
return new SimpleResult(true, "Path " + path + " allowed from " + remoteRepository);
399+
return new SimpleResult(true, "Path " + path + " allowed from " + repository);
357400
} else {
358-
return new SimpleResult(false, "Prefix " + path + " NOT allowed from " + remoteRepository);
401+
return new SimpleResult(false, "Path " + path + " NOT allowed from " + repository);
359402
}
360403
}
361404
}

0 commit comments

Comments
 (0)