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