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