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