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