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