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