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.io.IOException;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.nio.file.attribute.FileTime;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.concurrent.atomic.AtomicBoolean;
37
38 import org.eclipse.aether.ConfigurationProperties;
39 import org.eclipse.aether.RepositoryEvent;
40 import org.eclipse.aether.RepositoryEvent.EventType;
41 import org.eclipse.aether.RepositorySystemSession;
42 import org.eclipse.aether.RequestTrace;
43 import org.eclipse.aether.SyncContext;
44 import org.eclipse.aether.artifact.Artifact;
45 import org.eclipse.aether.impl.ArtifactResolver;
46 import org.eclipse.aether.impl.OfflineController;
47 import org.eclipse.aether.impl.RemoteRepositoryFilterManager;
48 import org.eclipse.aether.impl.RemoteRepositoryManager;
49 import org.eclipse.aether.impl.RepositoryConnectorProvider;
50 import org.eclipse.aether.impl.RepositoryEventDispatcher;
51 import org.eclipse.aether.impl.UpdateCheck;
52 import org.eclipse.aether.impl.UpdateCheckManager;
53 import org.eclipse.aether.impl.VersionResolver;
54 import org.eclipse.aether.repository.*;
55 import org.eclipse.aether.resolution.ArtifactRequest;
56 import org.eclipse.aether.resolution.ArtifactResolutionException;
57 import org.eclipse.aether.resolution.ArtifactResult;
58 import org.eclipse.aether.resolution.ResolutionErrorPolicy;
59 import org.eclipse.aether.resolution.VersionRequest;
60 import org.eclipse.aether.resolution.VersionResolutionException;
61 import org.eclipse.aether.resolution.VersionResult;
62 import org.eclipse.aether.spi.connector.ArtifactDownload;
63 import org.eclipse.aether.spi.connector.RepositoryConnector;
64 import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
65 import org.eclipse.aether.spi.io.PathProcessor;
66 import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor;
67 import org.eclipse.aether.spi.synccontext.SyncContextFactory;
68 import org.eclipse.aether.transfer.ArtifactFilteredOutException;
69 import org.eclipse.aether.transfer.ArtifactNotFoundException;
70 import org.eclipse.aether.transfer.ArtifactTransferException;
71 import org.eclipse.aether.transfer.NoRepositoryConnectorException;
72 import org.eclipse.aether.transfer.RepositoryOfflineException;
73 import org.eclipse.aether.util.ConfigUtils;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76
77 import static java.util.Objects.requireNonNull;
78
79
80
81
82 @Singleton
83 @Named
84 public class DefaultArtifactResolver implements ArtifactResolver {
85
86 public static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_AETHER + "artifactResolver.";
87
88
89
90
91
92
93
94
95
96
97
98 public static final String CONFIG_PROP_SNAPSHOT_NORMALIZATION = CONFIG_PROPS_PREFIX + "snapshotNormalization";
99
100 public static final boolean DEFAULT_SNAPSHOT_NORMALIZATION = true;
101
102
103
104
105
106
107
108
109
110 public static final String CONFIG_PROP_SIMPLE_LRM_INTEROP = CONFIG_PROPS_PREFIX + "simpleLrmInterop";
111
112 public static final boolean DEFAULT_SIMPLE_LRM_INTEROP = false;
113
114 private static final Logger LOGGER = LoggerFactory.getLogger(DefaultArtifactResolver.class);
115
116 private final PathProcessor pathProcessor;
117
118 private final RepositoryEventDispatcher repositoryEventDispatcher;
119
120 private final VersionResolver versionResolver;
121
122 private final UpdateCheckManager updateCheckManager;
123
124 private final RepositoryConnectorProvider repositoryConnectorProvider;
125
126 private final RemoteRepositoryManager remoteRepositoryManager;
127
128 private final SyncContextFactory syncContextFactory;
129
130 private final OfflineController offlineController;
131
132 private final Map<String, ArtifactResolverPostProcessor> artifactResolverPostProcessors;
133
134 private final RemoteRepositoryFilterManager remoteRepositoryFilterManager;
135
136 @SuppressWarnings("checkstyle:parameternumber")
137 @Inject
138 public DefaultArtifactResolver(
139 PathProcessor pathProcessor,
140 RepositoryEventDispatcher repositoryEventDispatcher,
141 VersionResolver versionResolver,
142 UpdateCheckManager updateCheckManager,
143 RepositoryConnectorProvider repositoryConnectorProvider,
144 RemoteRepositoryManager remoteRepositoryManager,
145 SyncContextFactory syncContextFactory,
146 OfflineController offlineController,
147 Map<String, ArtifactResolverPostProcessor> artifactResolverPostProcessors,
148 RemoteRepositoryFilterManager remoteRepositoryFilterManager) {
149 this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null");
150 this.repositoryEventDispatcher =
151 requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null");
152 this.versionResolver = requireNonNull(versionResolver, "version resolver cannot be null");
153 this.updateCheckManager = requireNonNull(updateCheckManager, "update check manager cannot be null");
154 this.repositoryConnectorProvider =
155 requireNonNull(repositoryConnectorProvider, "repository connector provider cannot be null");
156 this.remoteRepositoryManager =
157 requireNonNull(remoteRepositoryManager, "remote repository provider cannot be null");
158 this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
159 this.offlineController = requireNonNull(offlineController, "offline controller cannot be null");
160 this.artifactResolverPostProcessors =
161 requireNonNull(artifactResolverPostProcessors, "artifact resolver post-processors cannot be null");
162 this.remoteRepositoryFilterManager =
163 requireNonNull(remoteRepositoryFilterManager, "remote repository filter manager cannot be null");
164 }
165
166 @Override
167 public ArtifactResult resolveArtifact(RepositorySystemSession session, ArtifactRequest request)
168 throws ArtifactResolutionException {
169 requireNonNull(session, "session cannot be null");
170 requireNonNull(request, "request cannot be null");
171
172 return resolveArtifacts(session, Collections.singleton(request)).get(0);
173 }
174
175 @Override
176 public List<ArtifactResult> resolveArtifacts(
177 RepositorySystemSession session, Collection<? extends ArtifactRequest> requests)
178 throws ArtifactResolutionException {
179 requireNonNull(session, "session cannot be null");
180 requireNonNull(requests, "requests cannot be null");
181 try (SyncContext shared = syncContextFactory.newInstance(session, true);
182 SyncContext exclusive = syncContextFactory.newInstance(session, false)) {
183 Collection<Artifact> artifacts = new ArrayList<>(requests.size());
184 for (ArtifactRequest request : requests) {
185 if (session.getSystemScopeHandler().getSystemPath(request.getArtifact()) != null) {
186 continue;
187 }
188 artifacts.add(request.getArtifact());
189 }
190
191 return resolve(shared, exclusive, artifacts, session, requests);
192 }
193 }
194
195 @SuppressWarnings("checkstyle:methodlength")
196 private List<ArtifactResult> resolve(
197 SyncContext shared,
198 SyncContext exclusive,
199 Collection<Artifact> subjects,
200 RepositorySystemSession session,
201 Collection<? extends ArtifactRequest> requests)
202 throws ArtifactResolutionException {
203 SyncContext current = shared;
204 try {
205 while (true) {
206 current.acquire(subjects, null);
207
208 boolean failures = false;
209 final List<ArtifactResult> results = new ArrayList<>(requests.size());
210 final boolean simpleLrmInterop =
211 ConfigUtils.getBoolean(session, DEFAULT_SIMPLE_LRM_INTEROP, CONFIG_PROP_SIMPLE_LRM_INTEROP);
212 final LocalRepositoryManager lrm = session.getLocalRepositoryManager();
213 final WorkspaceReader workspace = session.getWorkspaceReader();
214 final List<ResolutionGroup> groups = new ArrayList<>();
215
216 final RemoteRepositoryFilter filter = remoteRepositoryFilterManager.getRemoteRepositoryFilter(session);
217
218 for (ArtifactRequest request : requests) {
219 RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
220
221 ArtifactResult result = new ArtifactResult(request);
222 results.add(result);
223
224 Artifact artifact = request.getArtifact();
225
226 if (current == shared) {
227 artifactResolving(session, trace, artifact);
228 }
229
230 String localPath = session.getSystemScopeHandler().getSystemPath(artifact);
231 if (localPath != null) {
232
233 Path path = Paths.get(localPath);
234 if (!Files.isRegularFile(path)) {
235 failures = true;
236 result.addException(
237 ArtifactResult.NO_REPOSITORY, new ArtifactNotFoundException(artifact, localPath));
238 } else {
239 artifact = artifact.setPath(path);
240 result.setArtifact(artifact);
241 artifactResolved(session, trace, artifact, null, result.getExceptions());
242 }
243 continue;
244 }
245
246 List<RemoteRepository> remoteRepositories = request.getRepositories();
247 List<RemoteRepository> filteredRemoteRepositories = new ArrayList<>(remoteRepositories);
248 if (filter != null) {
249 for (RemoteRepository repository : remoteRepositories) {
250 RemoteRepositoryFilter.Result filterResult = filter.acceptArtifact(repository, artifact);
251 if (!filterResult.isAccepted()) {
252 result.addException(
253 repository,
254 new ArtifactFilteredOutException(
255 artifact, repository, filterResult.reasoning()));
256 filteredRemoteRepositories.remove(repository);
257 }
258 }
259 }
260
261 VersionResult versionResult;
262 try {
263 VersionRequest versionRequest =
264 new VersionRequest(artifact, filteredRemoteRepositories, request.getRequestContext());
265 versionRequest.setTrace(trace);
266 versionResult = versionResolver.resolveVersion(session, versionRequest);
267 } catch (VersionResolutionException e) {
268 if (filteredRemoteRepositories.isEmpty()) {
269 result.addException(lrm.getRepository(), e);
270 } else {
271 filteredRemoteRepositories.forEach(r -> result.addException(r, e));
272 }
273 continue;
274 }
275
276 artifact = artifact.setVersion(versionResult.getVersion());
277
278 if (versionResult.getRepository() != null) {
279 if (versionResult.getRepository() instanceof RemoteRepository) {
280 filteredRemoteRepositories =
281 Collections.singletonList((RemoteRepository) versionResult.getRepository());
282 } else {
283 filteredRemoteRepositories = Collections.emptyList();
284 }
285 }
286
287 if (workspace != null) {
288 Path path = workspace.findArtifactPath(artifact);
289 if (path != null) {
290 artifact = artifact.setPath(path);
291 result.setArtifact(artifact);
292 result.setRepository(workspace.getRepository());
293 artifactResolved(session, trace, artifact, result.getRepository(), null);
294 continue;
295 }
296 }
297
298 LocalArtifactResult local = lrm.find(
299 session,
300 new LocalArtifactRequest(
301 artifact, filteredRemoteRepositories, request.getRequestContext()));
302 result.setLocalArtifactResult(local);
303 boolean found = (filter != null && local.isAvailable()) || isLocallyInstalled(local, versionResult);
304
305
306
307 if (found) {
308 if (local.getRepository() != null) {
309 result.setRepository(local.getRepository());
310 } else {
311 result.setRepository(lrm.getRepository());
312 }
313
314 try {
315 artifact = artifact.setPath(getPath(session, artifact, local.getPath()));
316 result.setArtifact(artifact);
317 artifactResolved(session, trace, artifact, result.getRepository(), null);
318 } catch (ArtifactTransferException e) {
319 result.addException(lrm.getRepository(), e);
320 }
321 if (filter == null && simpleLrmInterop && !local.isAvailable()) {
322
323
324
325
326
327
328 lrm.add(session, new LocalArtifactRegistration(artifact));
329 }
330
331 continue;
332 }
333
334 if (local.getPath() != null) {
335 LOGGER.info(
336 "Artifact {} is present in the local repository, but cached from a remote repository ID that is unavailable in current build context, verifying that is downloadable from {}",
337 artifact,
338 remoteRepositories);
339 }
340
341 LOGGER.debug("Resolving artifact {} from {}", artifact, remoteRepositories);
342 AtomicBoolean resolved = new AtomicBoolean(false);
343 Iterator<ResolutionGroup> groupIt = groups.iterator();
344 for (RemoteRepository repo : filteredRemoteRepositories) {
345 if (!repo.getPolicy(artifact.isSnapshot()).isEnabled()) {
346 continue;
347 }
348
349 try {
350 Utils.checkOffline(session, offlineController, repo);
351 } catch (RepositoryOfflineException e) {
352 Exception exception = new ArtifactNotFoundException(
353 artifact,
354 repo,
355 "Cannot access " + repo.getId() + " ("
356 + repo.getUrl() + ") in offline mode and the artifact " + artifact
357 + " has not been downloaded from it before.",
358 e);
359 result.addException(repo, exception);
360 continue;
361 }
362
363 ResolutionGroup group = null;
364 while (groupIt.hasNext()) {
365 ResolutionGroup t = groupIt.next();
366 if (t.matches(repo)) {
367 group = t;
368 break;
369 }
370 }
371 if (group == null) {
372 group = new ResolutionGroup(repo);
373 groups.add(group);
374 groupIt = Collections.emptyIterator();
375 }
376 group.items.add(new ResolutionItem(trace, artifact, resolved, result, local, repo));
377 }
378 }
379
380 if (!groups.isEmpty() && current == shared) {
381 current.close();
382 current = exclusive;
383 continue;
384 }
385
386 for (ResolutionGroup group : groups) {
387 performDownloads(session, group);
388 }
389
390 for (ArtifactResolverPostProcessor artifactResolverPostProcessor :
391 artifactResolverPostProcessors.values()) {
392 artifactResolverPostProcessor.postProcess(session, results);
393 }
394
395 for (ArtifactResult result : results) {
396 ArtifactRequest request = result.getRequest();
397
398 Artifact artifact = result.getArtifact();
399 if (artifact == null || artifact.getPath() == null) {
400 failures = true;
401 if (result.getExceptions().isEmpty()) {
402 Exception exception =
403 new ArtifactNotFoundException(request.getArtifact(), (RemoteRepository) null);
404 result.addException(result.getRepository(), exception);
405 }
406 RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
407 artifactResolved(session, trace, request.getArtifact(), null, result.getExceptions());
408 }
409 }
410
411 if (failures) {
412 throw new ArtifactResolutionException(results);
413 }
414
415 return results;
416 }
417 } finally {
418 current.close();
419 }
420 }
421
422 private boolean isLocallyInstalled(LocalArtifactResult lar, VersionResult vr) {
423 if (lar.isAvailable()) {
424 return true;
425 }
426 if (lar.getPath() != null) {
427
428 if (vr.getRepository() instanceof LocalRepository) {
429
430 return true;
431 } else {
432 return vr.getRepository() == null
433 && lar.getRequest().getRepositories().isEmpty();
434 }
435 }
436 return false;
437 }
438
439 private Path getPath(RepositorySystemSession session, Artifact artifact, Path path)
440 throws ArtifactTransferException {
441 if (artifact.isSnapshot()
442 && !artifact.getVersion().equals(artifact.getBaseVersion())
443 && ConfigUtils.getBoolean(
444 session, DEFAULT_SNAPSHOT_NORMALIZATION, CONFIG_PROP_SNAPSHOT_NORMALIZATION)) {
445 String name = path.getFileName().toString().replace(artifact.getVersion(), artifact.getBaseVersion());
446 Path dst = path.getParent().resolve(name);
447
448 try {
449 long pathLastModified = pathProcessor.lastModified(path, 0L);
450 boolean copy = pathProcessor.size(dst, 0L) != pathProcessor.size(path, 0L)
451 || pathProcessor.lastModified(dst, 0L) != pathLastModified;
452 if (copy) {
453 pathProcessor.copy(path, dst);
454 Files.setLastModifiedTime(dst, FileTime.fromMillis(pathLastModified));
455 }
456 } catch (IOException e) {
457 throw new ArtifactTransferException(artifact, null, e);
458 }
459
460 path = dst;
461 }
462
463 return path;
464 }
465
466 private void performDownloads(RepositorySystemSession session, ResolutionGroup group) {
467 List<ArtifactDownload> downloads = gatherDownloads(session, group);
468 if (downloads.isEmpty()) {
469 return;
470 }
471
472 for (ArtifactDownload download : downloads) {
473 artifactDownloading(session, download.getTrace(), download.getArtifact(), group.repository);
474 }
475
476 try {
477 try (RepositoryConnector connector =
478 repositoryConnectorProvider.newRepositoryConnector(session, group.repository)) {
479 connector.get(downloads, null);
480 }
481 } catch (NoRepositoryConnectorException e) {
482 for (ArtifactDownload download : downloads) {
483 download.setException(new ArtifactTransferException(download.getArtifact(), group.repository, e));
484 }
485 }
486
487 evaluateDownloads(session, group);
488 }
489
490 private List<ArtifactDownload> gatherDownloads(RepositorySystemSession session, ResolutionGroup group) {
491 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
492 List<ArtifactDownload> downloads = new ArrayList<>();
493
494 for (ResolutionItem item : group.items) {
495 Artifact artifact = item.artifact;
496
497 if (item.resolved.get()) {
498
499 continue;
500 }
501
502 ArtifactDownload download = new ArtifactDownload();
503 download.setArtifact(artifact);
504 download.setRequestContext(item.request.getRequestContext());
505 download.setListener(SafeTransferListener.wrap(session));
506 download.setTrace(item.trace);
507 if (item.local.getPath() != null) {
508 download.setPath(item.local.getPath());
509 download.setExistenceCheck(true);
510 } else {
511 String path =
512 lrm.getPathForRemoteArtifact(artifact, group.repository, item.request.getRequestContext());
513 download.setPath(lrm.getRepository().getBasePath().resolve(path));
514 }
515
516 boolean snapshot = artifact.isSnapshot();
517 RepositoryPolicy policy = remoteRepositoryManager.getPolicy(session, group.repository, !snapshot, snapshot);
518
519 int errorPolicy = Utils.getPolicy(session, artifact, group.repository);
520 if ((errorPolicy & ResolutionErrorPolicy.CACHE_ALL) != 0) {
521 UpdateCheck<Artifact, ArtifactTransferException> check = new UpdateCheck<>();
522 check.setItem(artifact);
523 check.setPath(download.getPath());
524 check.setFileValid(false);
525 check.setRepository(group.repository);
526 check.setArtifactPolicy(policy.getArtifactUpdatePolicy());
527 check.setMetadataPolicy(policy.getMetadataUpdatePolicy());
528 item.updateCheck = check;
529 updateCheckManager.checkArtifact(session, check);
530 if (!check.isRequired()) {
531 item.result.addException(group.repository, check.getException());
532 continue;
533 }
534 }
535
536 download.setChecksumPolicy(policy.getChecksumPolicy());
537 download.setRepositories(item.repository.getMirroredRepositories());
538 downloads.add(download);
539 item.download = download;
540 }
541
542 return downloads;
543 }
544
545 private void evaluateDownloads(RepositorySystemSession session, ResolutionGroup group) {
546 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
547
548 for (ResolutionItem item : group.items) {
549 ArtifactDownload download = item.download;
550 if (download == null) {
551 continue;
552 }
553
554 Artifact artifact = download.getArtifact();
555 if (download.getException() == null) {
556 item.resolved.set(true);
557 item.result.setRepository(group.repository);
558 try {
559 artifact = artifact.setPath(getPath(session, artifact, download.getPath()));
560 item.result.setArtifact(artifact);
561
562 lrm.add(
563 session,
564 new LocalArtifactRegistration(artifact, group.repository, download.getSupportedContexts()));
565 } catch (ArtifactTransferException e) {
566 download.setException(e);
567 item.result.addException(group.repository, e);
568 }
569 } else {
570 item.result.addException(group.repository, download.getException());
571 }
572
573
574
575
576
577 if (item.updateCheck != null) {
578 item.updateCheck.setException(download.getException());
579 updateCheckManager.touchArtifact(session, item.updateCheck);
580 }
581
582 artifactDownloaded(session, download.getTrace(), artifact, group.repository, download.getException());
583 if (download.getException() == null) {
584 artifactResolved(session, download.getTrace(), artifact, group.repository, null);
585 }
586 }
587 }
588
589 private void artifactResolving(RepositorySystemSession session, RequestTrace trace, Artifact artifact) {
590 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_RESOLVING);
591 event.setTrace(trace);
592 event.setArtifact(artifact);
593
594 repositoryEventDispatcher.dispatch(event.build());
595 }
596
597 private void artifactResolved(
598 RepositorySystemSession session,
599 RequestTrace trace,
600 Artifact artifact,
601 ArtifactRepository repository,
602 Collection<Exception> exceptions) {
603 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_RESOLVED);
604 event.setTrace(trace);
605 event.setArtifact(artifact);
606 event.setRepository(repository);
607 event.setExceptions(exceptions != null ? new ArrayList<>(exceptions) : null);
608 if (artifact != null) {
609 event.setPath(artifact.getPath());
610 }
611
612 repositoryEventDispatcher.dispatch(event.build());
613 }
614
615 private void artifactDownloading(
616 RepositorySystemSession session, RequestTrace trace, Artifact artifact, RemoteRepository repository) {
617 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DOWNLOADING);
618 event.setTrace(trace);
619 event.setArtifact(artifact);
620 event.setRepository(repository);
621
622 repositoryEventDispatcher.dispatch(event.build());
623 }
624
625 private void artifactDownloaded(
626 RepositorySystemSession session,
627 RequestTrace trace,
628 Artifact artifact,
629 RemoteRepository repository,
630 Exception exception) {
631 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DOWNLOADED);
632 event.setTrace(trace);
633 event.setArtifact(artifact);
634 event.setRepository(repository);
635 event.setException(exception);
636 if (artifact != null) {
637 event.setPath(artifact.getPath());
638 }
639
640 repositoryEventDispatcher.dispatch(event.build());
641 }
642
643 static class ResolutionGroup {
644
645 final RemoteRepository repository;
646
647 final List<ResolutionItem> items = new ArrayList<>();
648
649 ResolutionGroup(RemoteRepository repository) {
650 this.repository = repository;
651 }
652
653 boolean matches(RemoteRepository repo) {
654 return repository.getUrl().equals(repo.getUrl())
655 && repository.getContentType().equals(repo.getContentType())
656 && repository.isRepositoryManager() == repo.isRepositoryManager();
657 }
658 }
659
660 static class ResolutionItem {
661
662 final RequestTrace trace;
663
664 final ArtifactRequest request;
665
666 final ArtifactResult result;
667
668 final LocalArtifactResult local;
669
670 final RemoteRepository repository;
671
672 final Artifact artifact;
673
674 final AtomicBoolean resolved;
675
676 ArtifactDownload download;
677
678 UpdateCheck<Artifact, ArtifactTransferException> updateCheck;
679
680 ResolutionItem(
681 RequestTrace trace,
682 Artifact artifact,
683 AtomicBoolean resolved,
684 ArtifactResult result,
685 LocalArtifactResult local,
686 RemoteRepository repository) {
687 this.trace = trace;
688 this.artifact = artifact;
689 this.resolved = resolved;
690 this.result = result;
691 this.request = result.getRequest();
692 this.local = local;
693 this.repository = repository;
694 }
695 }
696 }