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