View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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.locator.Service;
72  import org.eclipse.aether.spi.locator.ServiceLocator;
73  import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor;
74  import org.eclipse.aether.spi.synccontext.SyncContextFactory;
75  import org.eclipse.aether.transfer.ArtifactFilteredOutException;
76  import org.eclipse.aether.transfer.ArtifactNotFoundException;
77  import org.eclipse.aether.transfer.ArtifactTransferException;
78  import org.eclipse.aether.transfer.NoRepositoryConnectorException;
79  import org.eclipse.aether.transfer.RepositoryOfflineException;
80  import org.eclipse.aether.util.ConfigUtils;
81  import org.slf4j.Logger;
82  import org.slf4j.LoggerFactory;
83  
84  import static java.util.Objects.requireNonNull;
85  
86  /**
87   *
88   */
89  @Singleton
90  @Named
91  public class DefaultArtifactResolver implements ArtifactResolver, Service {
92  
93      /**
94       * Configuration to enable "snapshot normalization", downloaded snapshots from remote with timestamped file names
95       * will have file names converted back to baseVersion. Default: {@code true}.
96       */
97      private static final String CONFIG_PROP_SNAPSHOT_NORMALIZATION = "aether.artifactResolver.snapshotNormalization";
98  
99      /**
100      * Configuration to enable "interoperability" with Simple LRM, but this breaks RRF feature, hence this configuration
101      * is IGNORED when RRF is used, and is warmly recommended to leave it disabled even if no RRF is being used.
102      * Default: {@code false}.
103      */
104     private static final String CONFIG_PROP_SIMPLE_LRM_INTEROP = "aether.artifactResolver.simpleLrmInterop";
105 
106     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultArtifactResolver.class);
107 
108     private FileProcessor fileProcessor;
109 
110     private RepositoryEventDispatcher repositoryEventDispatcher;
111 
112     private VersionResolver versionResolver;
113 
114     private UpdateCheckManager updateCheckManager;
115 
116     private RepositoryConnectorProvider repositoryConnectorProvider;
117 
118     private RemoteRepositoryManager remoteRepositoryManager;
119 
120     private SyncContextFactory syncContextFactory;
121 
122     private OfflineController offlineController;
123 
124     private Map<String, ArtifactResolverPostProcessor> artifactResolverPostProcessors;
125 
126     private RemoteRepositoryFilterManager remoteRepositoryFilterManager;
127 
128     public DefaultArtifactResolver() {
129         // enables default constructor
130     }
131 
132     @SuppressWarnings("checkstyle:parameternumber")
133     @Inject
134     DefaultArtifactResolver(
135             FileProcessor fileProcessor,
136             RepositoryEventDispatcher repositoryEventDispatcher,
137             VersionResolver versionResolver,
138             UpdateCheckManager updateCheckManager,
139             RepositoryConnectorProvider repositoryConnectorProvider,
140             RemoteRepositoryManager remoteRepositoryManager,
141             SyncContextFactory syncContextFactory,
142             OfflineController offlineController,
143             Map<String, ArtifactResolverPostProcessor> artifactResolverPostProcessors,
144             RemoteRepositoryFilterManager remoteRepositoryFilterManager) {
145         setFileProcessor(fileProcessor);
146         setRepositoryEventDispatcher(repositoryEventDispatcher);
147         setVersionResolver(versionResolver);
148         setUpdateCheckManager(updateCheckManager);
149         setRepositoryConnectorProvider(repositoryConnectorProvider);
150         setRemoteRepositoryManager(remoteRepositoryManager);
151         setSyncContextFactory(syncContextFactory);
152         setOfflineController(offlineController);
153         setArtifactResolverPostProcessors(artifactResolverPostProcessors);
154         setRemoteRepositoryFilterManager(remoteRepositoryFilterManager);
155     }
156 
157     public void initService(ServiceLocator locator) {
158         setFileProcessor(locator.getService(FileProcessor.class));
159         setRepositoryEventDispatcher(locator.getService(RepositoryEventDispatcher.class));
160         setVersionResolver(locator.getService(VersionResolver.class));
161         setUpdateCheckManager(locator.getService(UpdateCheckManager.class));
162         setRepositoryConnectorProvider(locator.getService(RepositoryConnectorProvider.class));
163         setRemoteRepositoryManager(locator.getService(RemoteRepositoryManager.class));
164         setSyncContextFactory(locator.getService(SyncContextFactory.class));
165         setOfflineController(locator.getService(OfflineController.class));
166         setArtifactResolverPostProcessors(Collections.emptyMap());
167         setRemoteRepositoryFilterManager(locator.getService(RemoteRepositoryFilterManager.class));
168     }
169 
170     /**
171      * @deprecated not used any more since MRESOLVER-36 move to slf4j, added back in MRESOLVER-64 for compatibility
172      */
173     @Deprecated
174     public DefaultArtifactResolver setLoggerFactory(org.eclipse.aether.spi.log.LoggerFactory loggerFactory) {
175         // this.logger = NullLoggerFactory.getSafeLogger( loggerFactory, getClass() );
176         return this;
177     }
178 
179     public DefaultArtifactResolver setFileProcessor(FileProcessor fileProcessor) {
180         this.fileProcessor = requireNonNull(fileProcessor, "file processor cannot be null");
181         return this;
182     }
183 
184     public DefaultArtifactResolver setRepositoryEventDispatcher(RepositoryEventDispatcher repositoryEventDispatcher) {
185         this.repositoryEventDispatcher =
186                 requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null");
187         return this;
188     }
189 
190     public DefaultArtifactResolver setVersionResolver(VersionResolver versionResolver) {
191         this.versionResolver = requireNonNull(versionResolver, "version resolver cannot be null");
192         return this;
193     }
194 
195     public DefaultArtifactResolver setUpdateCheckManager(UpdateCheckManager updateCheckManager) {
196         this.updateCheckManager = requireNonNull(updateCheckManager, "update check manager cannot be null");
197         return this;
198     }
199 
200     public DefaultArtifactResolver setRepositoryConnectorProvider(
201             RepositoryConnectorProvider repositoryConnectorProvider) {
202         this.repositoryConnectorProvider =
203                 requireNonNull(repositoryConnectorProvider, "repository connector provider cannot be null");
204         return this;
205     }
206 
207     public DefaultArtifactResolver setRemoteRepositoryManager(RemoteRepositoryManager remoteRepositoryManager) {
208         this.remoteRepositoryManager =
209                 requireNonNull(remoteRepositoryManager, "remote repository provider cannot be null");
210         return this;
211     }
212 
213     public DefaultArtifactResolver setSyncContextFactory(SyncContextFactory syncContextFactory) {
214         this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
215         return this;
216     }
217 
218     public DefaultArtifactResolver setOfflineController(OfflineController offlineController) {
219         this.offlineController = requireNonNull(offlineController, "offline controller cannot be null");
220         return this;
221     }
222 
223     public DefaultArtifactResolver setArtifactResolverPostProcessors(
224             Map<String, ArtifactResolverPostProcessor> artifactResolverPostProcessors) {
225         this.artifactResolverPostProcessors =
226                 requireNonNull(artifactResolverPostProcessors, "artifact resolver post-processors cannot be null");
227         return this;
228     }
229 
230     public DefaultArtifactResolver setRemoteRepositoryFilterManager(
231             RemoteRepositoryFilterManager remoteRepositoryFilterManager) {
232         this.remoteRepositoryFilterManager =
233                 requireNonNull(remoteRepositoryFilterManager, "remote repository filter manager cannot be null");
234         return this;
235     }
236 
237     public ArtifactResult resolveArtifact(RepositorySystemSession session, ArtifactRequest request)
238             throws ArtifactResolutionException {
239         requireNonNull(session, "session cannot be null");
240         requireNonNull(request, "request cannot be null");
241 
242         return resolveArtifacts(session, Collections.singleton(request)).get(0);
243     }
244 
245     public List<ArtifactResult> resolveArtifacts(
246             RepositorySystemSession session, Collection<? extends ArtifactRequest> requests)
247             throws ArtifactResolutionException {
248         requireNonNull(session, "session cannot be null");
249         requireNonNull(requests, "requests cannot be null");
250         try (SyncContext shared = syncContextFactory.newInstance(session, true);
251                 SyncContext exclusive = syncContextFactory.newInstance(session, false)) {
252             Collection<Artifact> artifacts = new ArrayList<>(requests.size());
253             for (ArtifactRequest request : requests) {
254                 if (request.getArtifact().getProperty(ArtifactProperties.LOCAL_PATH, null) != null) {
255                     continue;
256                 }
257                 artifacts.add(request.getArtifact());
258             }
259 
260             return resolve(shared, exclusive, artifacts, session, requests);
261         }
262     }
263 
264     @SuppressWarnings("checkstyle:methodlength")
265     private List<ArtifactResult> resolve(
266             SyncContext shared,
267             SyncContext exclusive,
268             Collection<Artifact> subjects,
269             RepositorySystemSession session,
270             Collection<? extends ArtifactRequest> requests)
271             throws ArtifactResolutionException {
272         SyncContext current = shared;
273         try {
274             while (true) {
275                 current.acquire(subjects, null);
276 
277                 boolean failures = false;
278                 final List<ArtifactResult> results = new ArrayList<>(requests.size());
279                 final boolean simpleLrmInterop = ConfigUtils.getBoolean(session, false, CONFIG_PROP_SIMPLE_LRM_INTEROP);
280                 final LocalRepositoryManager lrm = session.getLocalRepositoryManager();
281                 final WorkspaceReader workspace = session.getWorkspaceReader();
282                 final List<ResolutionGroup> groups = new ArrayList<>();
283                 // filter != null: means "filtering applied", if null no filtering applied (behave as before)
284                 final RemoteRepositoryFilter filter = remoteRepositoryFilterManager.getRemoteRepositoryFilter(session);
285 
286                 for (ArtifactRequest request : requests) {
287                     RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
288 
289                     ArtifactResult result = new ArtifactResult(request);
290                     results.add(result);
291 
292                     Artifact artifact = request.getArtifact();
293 
294                     if (current == shared) {
295                         artifactResolving(session, trace, artifact);
296                     }
297 
298                     String localPath = artifact.getProperty(ArtifactProperties.LOCAL_PATH, null);
299                     if (localPath != null) {
300                         // unhosted artifact, just validate file
301                         File file = new File(localPath);
302                         if (!file.isFile()) {
303                             failures = true;
304                             result.addException(new ArtifactNotFoundException(artifact, null));
305                         } else {
306                             artifact = artifact.setFile(file);
307                             result.setArtifact(artifact);
308                             artifactResolved(session, trace, artifact, null, result.getExceptions());
309                         }
310                         continue;
311                     }
312 
313                     List<RemoteRepository> remoteRepositories = request.getRepositories();
314                     List<RemoteRepository> filteredRemoteRepositories = new ArrayList<>(remoteRepositories);
315                     if (filter != null) {
316                         for (RemoteRepository repository : remoteRepositories) {
317                             RemoteRepositoryFilter.Result filterResult = filter.acceptArtifact(repository, artifact);
318                             if (!filterResult.isAccepted()) {
319                                 result.addException(new ArtifactFilteredOutException(
320                                         artifact, repository, filterResult.reasoning()));
321                                 filteredRemoteRepositories.remove(repository);
322                             }
323                         }
324                     }
325 
326                     VersionResult versionResult;
327                     try {
328                         VersionRequest versionRequest =
329                                 new VersionRequest(artifact, filteredRemoteRepositories, request.getRequestContext());
330                         versionRequest.setTrace(trace);
331                         versionResult = versionResolver.resolveVersion(session, versionRequest);
332                     } catch (VersionResolutionException e) {
333                         result.addException(e);
334                         continue;
335                     }
336 
337                     artifact = artifact.setVersion(versionResult.getVersion());
338 
339                     if (versionResult.getRepository() != null) {
340                         if (versionResult.getRepository() instanceof RemoteRepository) {
341                             filteredRemoteRepositories =
342                                     Collections.singletonList((RemoteRepository) versionResult.getRepository());
343                         } else {
344                             filteredRemoteRepositories = Collections.emptyList();
345                         }
346                     }
347 
348                     if (workspace != null) {
349                         File file = workspace.findArtifact(artifact);
350                         if (file != null) {
351                             artifact = artifact.setFile(file);
352                             result.setArtifact(artifact);
353                             result.setRepository(workspace.getRepository());
354                             artifactResolved(session, trace, artifact, result.getRepository(), null);
355                             continue;
356                         }
357                     }
358 
359                     LocalArtifactResult local = lrm.find(
360                             session,
361                             new LocalArtifactRequest(
362                                     artifact, filteredRemoteRepositories, request.getRequestContext()));
363                     result.setLocalArtifactResult(local);
364                     boolean found = (filter != null && local.isAvailable()) || isLocallyInstalled(local, versionResult);
365                     // with filtering it is availability that drives logic
366                     // without filtering it is simply presence of file that drives the logic
367                     // "interop" logic with simple LRM leads to RRF breakage: hence is ignored when filtering in effect
368                     if (found) {
369                         if (local.getRepository() != null) {
370                             result.setRepository(local.getRepository());
371                         } else {
372                             result.setRepository(lrm.getRepository());
373                         }
374 
375                         try {
376                             artifact = artifact.setFile(getFile(session, artifact, local.getFile()));
377                             result.setArtifact(artifact);
378                             artifactResolved(session, trace, artifact, result.getRepository(), null);
379                         } catch (ArtifactTransferException e) {
380                             result.addException(e);
381                         }
382                         if (filter == null && simpleLrmInterop && !local.isAvailable()) {
383                             /*
384                              * NOTE: Interop with simple local repository: An artifact installed by a simple local repo
385                              * manager will not show up in the repository tracking file of the enhanced local repository.
386                              * If however the maven-metadata-local.xml tells us the artifact was installed locally, we
387                              * sync the repository tracking file.
388                              */
389                             lrm.add(session, new LocalArtifactRegistration(artifact));
390                         }
391 
392                         continue;
393                     }
394 
395                     if (local.getFile() != null) {
396                         LOGGER.info(
397                                 "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 {}",
398                                 artifact,
399                                 remoteRepositories);
400                     }
401 
402                     LOGGER.debug("Resolving artifact {} from {}", artifact, remoteRepositories);
403                     AtomicBoolean resolved = new AtomicBoolean(false);
404                     Iterator<ResolutionGroup> groupIt = groups.iterator();
405                     for (RemoteRepository repo : filteredRemoteRepositories) {
406                         if (!repo.getPolicy(artifact.isSnapshot()).isEnabled()) {
407                             continue;
408                         }
409 
410                         try {
411                             Utils.checkOffline(session, offlineController, repo);
412                         } catch (RepositoryOfflineException e) {
413                             Exception exception = new ArtifactNotFoundException(
414                                     artifact,
415                                     repo,
416                                     "Cannot access " + repo.getId() + " ("
417                                             + repo.getUrl() + ") in offline mode and the artifact " + artifact
418                                             + " has not been downloaded from it before.",
419                                     e);
420                             result.addException(exception);
421                             continue;
422                         }
423 
424                         ResolutionGroup group = null;
425                         while (groupIt.hasNext()) {
426                             ResolutionGroup t = groupIt.next();
427                             if (t.matches(repo)) {
428                                 group = t;
429                                 break;
430                             }
431                         }
432                         if (group == null) {
433                             group = new ResolutionGroup(repo);
434                             groups.add(group);
435                             groupIt = Collections.emptyIterator();
436                         }
437                         group.items.add(new ResolutionItem(trace, artifact, resolved, result, local, repo));
438                     }
439                 }
440 
441                 if (!groups.isEmpty() && current == shared) {
442                     current.close();
443                     current = exclusive;
444                     continue;
445                 }
446 
447                 for (ResolutionGroup group : groups) {
448                     performDownloads(session, group);
449                 }
450 
451                 for (ArtifactResolverPostProcessor artifactResolverPostProcessor :
452                         artifactResolverPostProcessors.values()) {
453                     artifactResolverPostProcessor.postProcess(session, results);
454                 }
455 
456                 for (ArtifactResult result : results) {
457                     ArtifactRequest request = result.getRequest();
458 
459                     Artifact artifact = result.getArtifact();
460                     if (artifact == null || artifact.getFile() == null) {
461                         failures = true;
462                         if (result.getExceptions().isEmpty()) {
463                             Exception exception = new ArtifactNotFoundException(request.getArtifact(), null);
464                             result.addException(exception);
465                         }
466                         RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
467                         artifactResolved(session, trace, request.getArtifact(), null, result.getExceptions());
468                     }
469                 }
470 
471                 if (failures) {
472                     throw new ArtifactResolutionException(results);
473                 }
474 
475                 return results;
476             }
477         } finally {
478             current.close();
479         }
480     }
481 
482     private boolean isLocallyInstalled(LocalArtifactResult lar, VersionResult vr) {
483         if (lar.isAvailable()) {
484             return true;
485         }
486         if (lar.getFile() != null) {
487             if (vr.getRepository() instanceof LocalRepository) {
488                 // resolution of (snapshot) version found locally installed artifact
489                 return true;
490             } else if (vr.getRepository() == null
491                     && lar.getRequest().getRepositories().isEmpty()) {
492                 // resolution of version range found locally installed artifact
493                 return true;
494             }
495         }
496         return false;
497     }
498 
499     private File getFile(RepositorySystemSession session, Artifact artifact, File file)
500             throws ArtifactTransferException {
501         if (artifact.isSnapshot()
502                 && !artifact.getVersion().equals(artifact.getBaseVersion())
503                 && ConfigUtils.getBoolean(session, true, CONFIG_PROP_SNAPSHOT_NORMALIZATION)) {
504             String name = file.getName().replace(artifact.getVersion(), artifact.getBaseVersion());
505             File dst = new File(file.getParent(), name);
506 
507             boolean copy = dst.length() != file.length() || dst.lastModified() != file.lastModified();
508             if (copy) {
509                 try {
510                     fileProcessor.copy(file, dst);
511                     dst.setLastModified(file.lastModified());
512                 } catch (IOException e) {
513                     throw new ArtifactTransferException(artifact, null, e);
514                 }
515             }
516 
517             file = dst;
518         }
519 
520         return file;
521     }
522 
523     private void performDownloads(RepositorySystemSession session, ResolutionGroup group) {
524         List<ArtifactDownload> downloads = gatherDownloads(session, group);
525         if (downloads.isEmpty()) {
526             return;
527         }
528 
529         for (ArtifactDownload download : downloads) {
530             artifactDownloading(session, download.getTrace(), download.getArtifact(), group.repository);
531         }
532 
533         try {
534             try (RepositoryConnector connector =
535                     repositoryConnectorProvider.newRepositoryConnector(session, group.repository)) {
536                 connector.get(downloads, null);
537             }
538         } catch (NoRepositoryConnectorException e) {
539             for (ArtifactDownload download : downloads) {
540                 download.setException(new ArtifactTransferException(download.getArtifact(), group.repository, e));
541             }
542         }
543 
544         evaluateDownloads(session, group);
545     }
546 
547     private List<ArtifactDownload> gatherDownloads(RepositorySystemSession session, ResolutionGroup group) {
548         LocalRepositoryManager lrm = session.getLocalRepositoryManager();
549         List<ArtifactDownload> downloads = new ArrayList<>();
550 
551         for (ResolutionItem item : group.items) {
552             Artifact artifact = item.artifact;
553 
554             if (item.resolved.get()) {
555                 // resolved in previous resolution group
556                 continue;
557             }
558 
559             ArtifactDownload download = new ArtifactDownload();
560             download.setArtifact(artifact);
561             download.setRequestContext(item.request.getRequestContext());
562             download.setListener(SafeTransferListener.wrap(session));
563             download.setTrace(item.trace);
564             if (item.local.getFile() != null) {
565                 download.setFile(item.local.getFile());
566                 download.setExistenceCheck(true);
567             } else {
568                 String path =
569                         lrm.getPathForRemoteArtifact(artifact, group.repository, item.request.getRequestContext());
570                 download.setFile(new File(lrm.getRepository().getBasedir(), path));
571             }
572 
573             boolean snapshot = artifact.isSnapshot();
574             RepositoryPolicy policy = remoteRepositoryManager.getPolicy(session, group.repository, !snapshot, snapshot);
575 
576             int errorPolicy = Utils.getPolicy(session, artifact, group.repository);
577             if ((errorPolicy & ResolutionErrorPolicy.CACHE_ALL) != 0) {
578                 UpdateCheck<Artifact, ArtifactTransferException> check = new UpdateCheck<>();
579                 check.setItem(artifact);
580                 check.setFile(download.getFile());
581                 check.setFileValid(false);
582                 check.setRepository(group.repository);
583                 check.setPolicy(policy.getUpdatePolicy());
584                 item.updateCheck = check;
585                 updateCheckManager.checkArtifact(session, check);
586                 if (!check.isRequired()) {
587                     item.result.addException(check.getException());
588                     continue;
589                 }
590             }
591 
592             download.setChecksumPolicy(policy.getChecksumPolicy());
593             download.setRepositories(item.repository.getMirroredRepositories());
594             downloads.add(download);
595             item.download = download;
596         }
597 
598         return downloads;
599     }
600 
601     private void evaluateDownloads(RepositorySystemSession session, ResolutionGroup group) {
602         LocalRepositoryManager lrm = session.getLocalRepositoryManager();
603 
604         for (ResolutionItem item : group.items) {
605             ArtifactDownload download = item.download;
606             if (download == null) {
607                 continue;
608             }
609 
610             Artifact artifact = download.getArtifact();
611             if (download.getException() == null) {
612                 item.resolved.set(true);
613                 item.result.setRepository(group.repository);
614                 try {
615                     artifact = artifact.setFile(getFile(session, artifact, download.getFile()));
616                     item.result.setArtifact(artifact);
617 
618                     lrm.add(
619                             session,
620                             new LocalArtifactRegistration(artifact, group.repository, download.getSupportedContexts()));
621                 } catch (ArtifactTransferException e) {
622                     download.setException(e);
623                     item.result.addException(e);
624                 }
625             } else {
626                 item.result.addException(download.getException());
627             }
628 
629             /*
630              * NOTE: Touch after registration with local repo to ensure concurrent resolution is not rejected with
631              * "already updated" via session data when actual update to local repo is still pending.
632              */
633             if (item.updateCheck != null) {
634                 item.updateCheck.setException(download.getException());
635                 updateCheckManager.touchArtifact(session, item.updateCheck);
636             }
637 
638             artifactDownloaded(session, download.getTrace(), artifact, group.repository, download.getException());
639             if (download.getException() == null) {
640                 artifactResolved(session, download.getTrace(), artifact, group.repository, null);
641             }
642         }
643     }
644 
645     private void artifactResolving(RepositorySystemSession session, RequestTrace trace, Artifact artifact) {
646         RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_RESOLVING);
647         event.setTrace(trace);
648         event.setArtifact(artifact);
649 
650         repositoryEventDispatcher.dispatch(event.build());
651     }
652 
653     private void artifactResolved(
654             RepositorySystemSession session,
655             RequestTrace trace,
656             Artifact artifact,
657             ArtifactRepository repository,
658             List<Exception> exceptions) {
659         RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_RESOLVED);
660         event.setTrace(trace);
661         event.setArtifact(artifact);
662         event.setRepository(repository);
663         event.setExceptions(exceptions);
664         if (artifact != null) {
665             event.setFile(artifact.getFile());
666         }
667 
668         repositoryEventDispatcher.dispatch(event.build());
669     }
670 
671     private void artifactDownloading(
672             RepositorySystemSession session, RequestTrace trace, Artifact artifact, RemoteRepository repository) {
673         RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DOWNLOADING);
674         event.setTrace(trace);
675         event.setArtifact(artifact);
676         event.setRepository(repository);
677 
678         repositoryEventDispatcher.dispatch(event.build());
679     }
680 
681     private void artifactDownloaded(
682             RepositorySystemSession session,
683             RequestTrace trace,
684             Artifact artifact,
685             RemoteRepository repository,
686             Exception exception) {
687         RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DOWNLOADED);
688         event.setTrace(trace);
689         event.setArtifact(artifact);
690         event.setRepository(repository);
691         event.setException(exception);
692         if (artifact != null) {
693             event.setFile(artifact.getFile());
694         }
695 
696         repositoryEventDispatcher.dispatch(event.build());
697     }
698 
699     static class ResolutionGroup {
700 
701         final RemoteRepository repository;
702 
703         final List<ResolutionItem> items = new ArrayList<>();
704 
705         ResolutionGroup(RemoteRepository repository) {
706             this.repository = repository;
707         }
708 
709         boolean matches(RemoteRepository repo) {
710             return repository.getUrl().equals(repo.getUrl())
711                     && repository.getContentType().equals(repo.getContentType())
712                     && repository.isRepositoryManager() == repo.isRepositoryManager();
713         }
714     }
715 
716     static class ResolutionItem {
717 
718         final RequestTrace trace;
719 
720         final ArtifactRequest request;
721 
722         final ArtifactResult result;
723 
724         final LocalArtifactResult local;
725 
726         final RemoteRepository repository;
727 
728         final Artifact artifact;
729 
730         final AtomicBoolean resolved;
731 
732         ArtifactDownload download;
733 
734         UpdateCheck<Artifact, ArtifactTransferException> updateCheck;
735 
736         ResolutionItem(
737                 RequestTrace trace,
738                 Artifact artifact,
739                 AtomicBoolean resolved,
740                 ArtifactResult result,
741                 LocalArtifactResult local,
742                 RemoteRepository repository) {
743             this.trace = trace;
744             this.artifact = artifact;
745             this.resolved = resolved;
746             this.result = result;
747             this.request = result.getRequest();
748             this.local = local;
749             this.repository = repository;
750         }
751     }
752 }