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