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