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