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