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