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