1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.internal.impl;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.ListIterator;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.stream.Collectors;
31
32 import org.eclipse.aether.ConfigurationProperties;
33 import org.eclipse.aether.Keys;
34 import org.eclipse.aether.RepositoryCache;
35 import org.eclipse.aether.RepositorySystemSession;
36 import org.eclipse.aether.impl.RemoteRepositoryManager;
37 import org.eclipse.aether.impl.UpdatePolicyAnalyzer;
38 import org.eclipse.aether.repository.Authentication;
39 import org.eclipse.aether.repository.AuthenticationSelector;
40 import org.eclipse.aether.repository.MirrorSelector;
41 import org.eclipse.aether.repository.Proxy;
42 import org.eclipse.aether.repository.ProxySelector;
43 import org.eclipse.aether.repository.RemoteRepository;
44 import org.eclipse.aether.repository.RepositoryKeyFunction;
45 import org.eclipse.aether.repository.RepositoryPolicy;
46 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
47 import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory;
48 import org.eclipse.aether.util.ConfigUtils;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import static java.util.Objects.requireNonNull;
53
54
55
56 @Singleton
57 @Named
58 public class DefaultRemoteRepositoryManager implements RemoteRepositoryManager {
59
60 private static final String CONFIG_PROPS_PREFIX =
61 ConfigurationProperties.PREFIX_AETHER + "remoteRepositoryManager.";
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 public static final String CONFIG_PROP_AUTH_TO_DESCRIPTOR_REPOSITORIES =
80 CONFIG_PROPS_PREFIX + "authToDescriptorRepositories";
81
82 public static final boolean DEFAULT_AUTH_TO_DESCRIPTOR_REPOSITORIES = false;
83
84 private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRemoteRepositoryManager.class);
85
86 private final UpdatePolicyAnalyzer updatePolicyAnalyzer;
87
88 private final ChecksumPolicyProvider checksumPolicyProvider;
89
90 private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory;
91
92 @Inject
93 public DefaultRemoteRepositoryManager(
94 UpdatePolicyAnalyzer updatePolicyAnalyzer,
95 ChecksumPolicyProvider checksumPolicyProvider,
96 RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) {
97 this.updatePolicyAnalyzer = requireNonNull(updatePolicyAnalyzer, "update policy analyzer cannot be null");
98 this.checksumPolicyProvider = requireNonNull(checksumPolicyProvider, "checksum policy provider cannot be null");
99 this.repositoryKeyFunctionFactory =
100 requireNonNull(repositoryKeyFunctionFactory, "repository key function factory cannot be null");
101 }
102
103 @Override
104 public List<RemoteRepository> aggregateRepositories(
105 RepositorySystemSession session,
106 List<RemoteRepository> dominantRepositories,
107 List<RemoteRepository> recessiveRepositories,
108 boolean recessiveIsRaw) {
109 return aggregateRepositories(session, dominantRepositories, recessiveRepositories, recessiveIsRaw, false);
110 }
111
112 @Override
113 public List<RemoteRepository> aggregateRepositories(
114 RepositorySystemSession session,
115 List<RemoteRepository> dominantRepositories,
116 List<RemoteRepository> recessiveRepositories,
117 boolean recessiveIsRaw,
118 boolean recessiveIsFromDescriptor) {
119 requireNonNull(session, "session cannot be null");
120 requireNonNull(dominantRepositories, "dominantRepositories cannot be null");
121 requireNonNull(recessiveRepositories, "recessiveRepositories cannot be null");
122 if (recessiveRepositories.isEmpty()) {
123 return dominantRepositories;
124 }
125
126 RepositoryKeyFunction repositoryKeyFunction = repositoryKeyFunctionFactory.systemRepositoryKeyFunction(session);
127 MirrorSelector mirrorSelector = session.getMirrorSelector();
128 AuthenticationSelector authSelector = session.getAuthenticationSelector();
129 ProxySelector proxySelector = session.getProxySelector();
130
131 boolean authToDescriptorRepositories = ConfigUtils.getBoolean(
132 session, DEFAULT_AUTH_TO_DESCRIPTOR_REPOSITORIES, CONFIG_PROP_AUTH_TO_DESCRIPTOR_REPOSITORIES);
133
134 List<RemoteRepository> result = new ArrayList<>(dominantRepositories);
135
136 next:
137 for (RemoteRepository recessiveRepository : recessiveRepositories) {
138 RemoteRepository repository = recessiveRepository;
139 boolean mirrored = false;
140
141 if (recessiveIsRaw) {
142 RemoteRepository mirrorRepository = mirrorSelector.getMirror(recessiveRepository);
143
144 if (mirrorRepository != null) {
145 logMirror(session, recessiveRepository, mirrorRepository);
146 repository = mirrorRepository;
147 mirrored = true;
148 }
149 }
150
151 String key = repositoryKeyFunction.apply(repository, null);
152
153 for (ListIterator<RemoteRepository> it = result.listIterator(); it.hasNext(); ) {
154 RemoteRepository dominantRepository = it.next();
155
156 if (key.equals(repositoryKeyFunction.apply(dominantRepository, null))) {
157 if (!dominantRepository.getMirroredRepositories().isEmpty()
158 && !repository.getMirroredRepositories().isEmpty()) {
159 RemoteRepository mergedRepository = mergeMirrors(
160 session, repositoryKeyFunction, dominantRepository, repository, recessiveIsRaw);
161 if (mergedRepository != dominantRepository) {
162 it.set(mergedRepository);
163 }
164 }
165
166 continue next;
167 }
168 }
169
170 if (recessiveIsRaw) {
171 RemoteRepository.Builder builder = null;
172 Authentication auth = authSelector.getAuthentication(repository);
173 if (auth != null) {
174 if (!recessiveIsFromDescriptor || mirrored || authToDescriptorRepositories) {
175 builder = new RemoteRepository.Builder(repository);
176 builder.setAuthentication(auth);
177 } else if (auth != repository.getAuthentication()) {
178 logWarnOnce(
179 session,
180 "Not applying session authentication to repository {} ({}) declared by a remote"
181 + " artifact descriptor; set {}=true to restore the legacy behavior of"
182 + " matching credentials to such repositories by repository ID",
183 repository.getId(),
184 repository.getUrl(),
185 CONFIG_PROP_AUTH_TO_DESCRIPTOR_REPOSITORIES);
186 }
187 }
188 Proxy proxy = proxySelector.getProxy(repository);
189 if (proxy != null) {
190 if (builder == null) {
191 builder = new RemoteRepository.Builder(repository);
192 }
193 builder.setProxy(proxy);
194 }
195 if (builder != null) {
196 repository = builder.build();
197 }
198 }
199
200 result.add(repository);
201 }
202
203 return result.stream()
204 .map(r -> new RemoteRepository.Builder(r)
205 .setIntent(RemoteRepository.Intent.RESOLUTION)
206 .build())
207 .collect(Collectors.toList());
208 }
209
210 private void logMirror(RepositorySystemSession session, RemoteRepository original, RemoteRepository mirror) {
211 if (!LOGGER.isDebugEnabled()) {
212 return;
213 }
214 RepositoryCache cache = session.getCache();
215 if (cache != null) {
216 Object key = Keys.of(mirror.getId(), mirror.getUrl(), original.getId(), original.getUrl());
217 if (cache.get(session, key) != null) {
218 return;
219 }
220 cache.put(session, key, Boolean.TRUE);
221 }
222 LOGGER.debug(
223 "Using mirror {} ({}) for {} ({}).",
224 mirror.getId(),
225 mirror.getUrl(),
226 original.getId(),
227 original.getUrl());
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244 public static final String CONFIG_PROP_RAW_CHECKSUM_POLICY_DOWNGRADE =
245 "aether.remoteRepositoryManager.rawRepositoryChecksumPolicyDowngrade";
246
247 public static final boolean DEFAULT_RAW_CHECKSUM_POLICY_DOWNGRADE = false;
248
249 private static RepositoryPolicy clampChecksumPolicy(
250 RepositorySystemSession session, RepositoryPolicy merged, RepositoryPolicy floor, RemoteRepository rec) {
251 if (merged == null || floor == null) {
252 return merged;
253 }
254 if (checksumPolicyRank(merged.getChecksumPolicy()) < checksumPolicyRank(floor.getChecksumPolicy())) {
255 logWarnOnce(
256 session,
257 "Ignoring checksum policy '{}' contributed by repository {} ({}) declared by a remote artifact"
258 + " descriptor: it would downgrade the checksum policy '{}' of the mirror serving it;"
259 + " set {}=true to restore the legacy weakest-wins merge",
260 merged.getChecksumPolicy(),
261 rec.getId(),
262 rec.getUrl(),
263 floor.getChecksumPolicy(),
264 CONFIG_PROP_RAW_CHECKSUM_POLICY_DOWNGRADE);
265 return new RepositoryPolicy(
266 merged.isEnabled(),
267 merged.getArtifactUpdatePolicy(),
268 merged.getMetadataUpdatePolicy(),
269 floor.getChecksumPolicy());
270 }
271 return merged;
272 }
273
274 private static int checksumPolicyRank(String policy) {
275 if (policy == null) {
276 return -1;
277 }
278 switch (policy) {
279 case RepositoryPolicy.CHECKSUM_POLICY_FAIL:
280 return 2;
281 case RepositoryPolicy.CHECKSUM_POLICY_WARN:
282 return 1;
283 case RepositoryPolicy.CHECKSUM_POLICY_IGNORE:
284 return 0;
285 default:
286
287
288 return -1;
289 }
290 }
291
292 private RemoteRepository mergeMirrors(
293 RepositorySystemSession session,
294 RepositoryKeyFunction repositoryKeyFunction,
295 RemoteRepository dominant,
296 RemoteRepository recessive,
297 boolean recessiveIsRaw) {
298 boolean rawChecksumPolicyDowngrade = org.eclipse.aether.util.ConfigUtils.getBoolean(
299 session, DEFAULT_RAW_CHECKSUM_POLICY_DOWNGRADE, CONFIG_PROP_RAW_CHECKSUM_POLICY_DOWNGRADE);
300 RemoteRepository.Builder merged = null;
301 RepositoryPolicy releases = null, snapshots = null;
302
303 next:
304 for (RemoteRepository rec : recessive.getMirroredRepositories()) {
305 String recKey = repositoryKeyFunction.apply(rec, null);
306
307 for (RemoteRepository dom : dominant.getMirroredRepositories()) {
308 if (recKey.equals(repositoryKeyFunction.apply(dom, null))) {
309 continue next;
310 }
311 }
312
313 if (merged == null) {
314 merged = new RemoteRepository.Builder(dominant);
315 releases = dominant.getPolicy(false);
316 snapshots = dominant.getPolicy(true);
317 }
318
319 releases = merge(session, releases, rec.getPolicy(false), false);
320 snapshots = merge(session, snapshots, rec.getPolicy(true), false);
321
322 if (recessiveIsRaw && !rawChecksumPolicyDowngrade) {
323
324
325 releases = clampChecksumPolicy(session, releases, dominant.getPolicy(false), rec);
326 snapshots = clampChecksumPolicy(session, snapshots, dominant.getPolicy(true), rec);
327 }
328
329 merged.addMirroredRepository(rec);
330 }
331
332 if (merged == null) {
333 return dominant;
334 }
335 return merged.setReleasePolicy(releases).setSnapshotPolicy(snapshots).build();
336 }
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352 public static final String CONFIG_PROP_NATURE_MERGE_WEAKEST_CHECKSUM_POLICY =
353 "aether.remoteRepositoryManager.natureMergeWeakestChecksumPolicy";
354
355 public static final boolean DEFAULT_NATURE_MERGE_WEAKEST_CHECKSUM_POLICY = false;
356
357 @Override
358 public RepositoryPolicy getPolicy(
359 RepositorySystemSession session, RemoteRepository repository, boolean releases, boolean snapshots) {
360 requireNonNull(session, "session cannot be null");
361 requireNonNull(repository, "repository cannot be null");
362 RepositoryPolicy policy1 = releases ? repository.getPolicy(false) : null;
363 RepositoryPolicy policy2 = snapshots ? repository.getPolicy(true) : null;
364 return merge(session, policy1, policy2, true);
365 }
366
367 private RepositoryPolicy merge(
368 RepositorySystemSession session, RepositoryPolicy policy1, RepositoryPolicy policy2, boolean globalPolicy) {
369 RepositoryPolicy policy;
370
371 if (policy2 == null) {
372 if (globalPolicy) {
373 policy = merge(
374 policy1,
375 session.getArtifactUpdatePolicy(),
376 session.getMetadataUpdatePolicy(),
377 session.getChecksumPolicy());
378 } else {
379 policy = policy1;
380 }
381 } else if (policy1 == null) {
382 if (globalPolicy) {
383 policy = merge(
384 policy2,
385 session.getArtifactUpdatePolicy(),
386 session.getMetadataUpdatePolicy(),
387 session.getChecksumPolicy());
388 } else {
389 policy = policy2;
390 }
391 } else if (!policy2.isEnabled()) {
392 if (globalPolicy) {
393 policy = merge(
394 policy1,
395 session.getArtifactUpdatePolicy(),
396 session.getMetadataUpdatePolicy(),
397 session.getChecksumPolicy());
398 } else {
399 policy = policy1;
400 }
401 } else if (!policy1.isEnabled()) {
402 if (globalPolicy) {
403 policy = merge(
404 policy2,
405 session.getArtifactUpdatePolicy(),
406 session.getMetadataUpdatePolicy(),
407 session.getChecksumPolicy());
408 } else {
409 policy = policy2;
410 }
411 } else {
412 String checksums = session.getChecksumPolicy();
413
414 if (globalPolicy && checksums != null && !checksums.isEmpty()) {
415
416 } else if (globalPolicy
417 && !org.eclipse.aether.util.ConfigUtils.getBoolean(
418 session,
419 DEFAULT_NATURE_MERGE_WEAKEST_CHECKSUM_POLICY,
420 CONFIG_PROP_NATURE_MERGE_WEAKEST_CHECKSUM_POLICY)) {
421
422
423
424 checksums = strongerChecksumPolicy(policy1.getChecksumPolicy(), policy2.getChecksumPolicy());
425 } else {
426 checksums = checksumPolicyProvider.getEffectiveChecksumPolicy(
427 session, policy1.getChecksumPolicy(), policy2.getChecksumPolicy());
428 }
429
430 String artifactUpdates = session.getArtifactUpdatePolicy();
431
432 if (globalPolicy && artifactUpdates != null && !artifactUpdates.isEmpty()) {
433
434 } else {
435 artifactUpdates = updatePolicyAnalyzer.getEffectiveUpdatePolicy(
436 session, policy1.getArtifactUpdatePolicy(), policy2.getArtifactUpdatePolicy());
437 }
438 String metadataUpdates = session.getMetadataUpdatePolicy();
439 if (globalPolicy && metadataUpdates != null && !metadataUpdates.isEmpty()) {
440
441 } else {
442 metadataUpdates = updatePolicyAnalyzer.getEffectiveUpdatePolicy(
443 session, policy1.getMetadataUpdatePolicy(), policy2.getMetadataUpdatePolicy());
444 }
445
446 policy = new RepositoryPolicy(true, artifactUpdates, metadataUpdates, checksums);
447 }
448
449 return policy;
450 }
451
452 private RepositoryPolicy merge(
453 RepositoryPolicy policy, String artifactUpdates, String metadataUpdates, String checksums) {
454 if (policy != null) {
455 if (artifactUpdates == null || artifactUpdates.isEmpty()) {
456 artifactUpdates = policy.getArtifactUpdatePolicy();
457 }
458 if (metadataUpdates == null || metadataUpdates.isEmpty()) {
459 metadataUpdates = policy.getMetadataUpdatePolicy();
460 }
461 if (checksums == null || checksums.isEmpty()) {
462 checksums = policy.getChecksumPolicy();
463 }
464 if (!policy.getArtifactUpdatePolicy().equals(artifactUpdates)
465 || !policy.getMetadataUpdatePolicy().equals(metadataUpdates)
466 || !policy.getChecksumPolicy().equals(checksums)) {
467 policy = new RepositoryPolicy(policy.isEnabled(), artifactUpdates, metadataUpdates, checksums);
468 }
469 }
470 return policy;
471 }
472
473 private static String strongerChecksumPolicy(String policy1, String policy2) {
474 if (checksumPolicyStrength(policy2) > checksumPolicyStrength(policy1)) {
475 return policy2;
476 }
477 return policy1;
478 }
479
480 private static int checksumPolicyStrength(String policy) {
481 if (policy == null) {
482 return -1;
483 }
484 switch (policy) {
485 case RepositoryPolicy.CHECKSUM_POLICY_FAIL:
486 return 2;
487 case RepositoryPolicy.CHECKSUM_POLICY_WARN:
488 return 1;
489 case RepositoryPolicy.CHECKSUM_POLICY_IGNORE:
490 return 0;
491 default:
492
493
494 return -1;
495 }
496 }
497
498 @SuppressWarnings("unchecked")
499 private static void logWarnOnce(RepositorySystemSession session, String message, Object... args) {
500 if (!LOGGER.isWarnEnabled()) {
501 return;
502 }
503 Object[] keys = new Object[args.length + 1];
504 keys[0] = message;
505 System.arraycopy(args, 0, keys, 1, args.length);
506 Object key = Keys.of(keys);
507 ((Map<Object, Boolean>) session.getData()
508 .computeIfAbsent(
509 Keys.of(DefaultRemoteRepositoryManager.class, "logWarnOnce"), ConcurrentHashMap::new))
510 .computeIfAbsent(key, k -> {
511 LOGGER.warn(message, args);
512 return true;
513 });
514 }
515 }