001package org.eclipse.aether.internal.impl;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import javax.inject.Inject;
023import javax.inject.Named;
024import javax.inject.Singleton;
025
026import java.io.File;
027import java.io.IOException;
028import java.util.ArrayList;
029import java.util.Collection;
030import java.util.Collections;
031import java.util.Iterator;
032import java.util.List;
033import static java.util.Objects.requireNonNull;
034
035import java.util.Map;
036import java.util.concurrent.atomic.AtomicBoolean;
037
038import org.eclipse.aether.RepositoryEvent;
039import org.eclipse.aether.RepositoryEvent.EventType;
040import org.eclipse.aether.RepositorySystemSession;
041import org.eclipse.aether.RequestTrace;
042import org.eclipse.aether.SyncContext;
043import org.eclipse.aether.artifact.Artifact;
044import org.eclipse.aether.artifact.ArtifactProperties;
045import org.eclipse.aether.impl.ArtifactResolver;
046import org.eclipse.aether.impl.OfflineController;
047import org.eclipse.aether.impl.RemoteRepositoryFilterManager;
048import org.eclipse.aether.impl.RemoteRepositoryManager;
049import org.eclipse.aether.impl.RepositoryConnectorProvider;
050import org.eclipse.aether.impl.RepositoryEventDispatcher;
051import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor;
052import org.eclipse.aether.spi.synccontext.SyncContextFactory;
053import org.eclipse.aether.impl.UpdateCheck;
054import org.eclipse.aether.impl.UpdateCheckManager;
055import org.eclipse.aether.impl.VersionResolver;
056import org.eclipse.aether.repository.ArtifactRepository;
057import org.eclipse.aether.repository.LocalArtifactRegistration;
058import org.eclipse.aether.repository.LocalArtifactRequest;
059import org.eclipse.aether.repository.LocalArtifactResult;
060import org.eclipse.aether.repository.LocalRepository;
061import org.eclipse.aether.repository.LocalRepositoryManager;
062import org.eclipse.aether.repository.RemoteRepository;
063import org.eclipse.aether.repository.RepositoryPolicy;
064import org.eclipse.aether.repository.WorkspaceReader;
065import org.eclipse.aether.resolution.ArtifactRequest;
066import org.eclipse.aether.resolution.ArtifactResolutionException;
067import org.eclipse.aether.resolution.ArtifactResult;
068import org.eclipse.aether.resolution.ResolutionErrorPolicy;
069import org.eclipse.aether.resolution.VersionRequest;
070import org.eclipse.aether.resolution.VersionResolutionException;
071import org.eclipse.aether.resolution.VersionResult;
072import org.eclipse.aether.spi.connector.ArtifactDownload;
073import org.eclipse.aether.spi.connector.RepositoryConnector;
074import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
075import org.eclipse.aether.spi.io.FileProcessor;
076import org.eclipse.aether.spi.locator.Service;
077import org.eclipse.aether.spi.locator.ServiceLocator;
078import org.eclipse.aether.transfer.ArtifactNotFoundException;
079import org.eclipse.aether.transfer.ArtifactTransferException;
080import org.eclipse.aether.transfer.NoRepositoryConnectorException;
081import org.eclipse.aether.transfer.RepositoryOfflineException;
082import org.eclipse.aether.util.ConfigUtils;
083import org.slf4j.Logger;
084import org.slf4j.LoggerFactory;
085
086/**
087 *
088 */
089@Singleton
090@Named
091public class DefaultArtifactResolver
092        implements ArtifactResolver, Service
093{
094
095    /**
096     * Configuration to enable "snapshot normalization", downloaded snapshots from remote with timestamped file names
097     * will have file names converted back to baseVersion. Default: {@code true}.
098     */
099    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            try ( RepositoryConnector connector =
579                          repositoryConnectorProvider.newRepositoryConnector( session, group.repository ) )
580            {
581                connector.get( downloads, null );
582            }
583        }
584        catch ( NoRepositoryConnectorException e )
585        {
586            for ( ArtifactDownload download : downloads )
587            {
588                download.setException( new ArtifactTransferException( download.getArtifact(), group.repository, e ) );
589            }
590        }
591
592        evaluateDownloads( session, group );
593    }
594
595    private List<ArtifactDownload> gatherDownloads( RepositorySystemSession session, ResolutionGroup group )
596    {
597        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
598        List<ArtifactDownload> downloads = new ArrayList<>();
599
600        for ( ResolutionItem item : group.items )
601        {
602            Artifact artifact = item.artifact;
603
604            if ( item.resolved.get() )
605            {
606                // resolved in previous resolution group
607                continue;
608            }
609
610            ArtifactDownload download = new ArtifactDownload();
611            download.setArtifact( artifact );
612            download.setRequestContext( item.request.getRequestContext() );
613            download.setListener( SafeTransferListener.wrap( session ) );
614            download.setTrace( item.trace );
615            if ( item.local.getFile() != null )
616            {
617                download.setFile( item.local.getFile() );
618                download.setExistenceCheck( true );
619            }
620            else
621            {
622                String path =
623                        lrm.getPathForRemoteArtifact( artifact, group.repository, item.request.getRequestContext() );
624                download.setFile( new File( lrm.getRepository().getBasedir(), path ) );
625            }
626
627            boolean snapshot = artifact.isSnapshot();
628            RepositoryPolicy policy =
629                    remoteRepositoryManager.getPolicy( session, group.repository, !snapshot, snapshot );
630
631            int errorPolicy = Utils.getPolicy( session, artifact, group.repository );
632            if ( ( errorPolicy & ResolutionErrorPolicy.CACHE_ALL ) != 0 )
633            {
634                UpdateCheck<Artifact, ArtifactTransferException> check = new UpdateCheck<>();
635                check.setItem( artifact );
636                check.setFile( download.getFile() );
637                check.setFileValid( false );
638                check.setRepository( group.repository );
639                check.setPolicy( policy.getUpdatePolicy() );
640                item.updateCheck = check;
641                updateCheckManager.checkArtifact( session, check );
642                if ( !check.isRequired() )
643                {
644                    item.result.addException( check.getException() );
645                    continue;
646                }
647            }
648
649            download.setChecksumPolicy( policy.getChecksumPolicy() );
650            download.setRepositories( item.repository.getMirroredRepositories() );
651            downloads.add( download );
652            item.download = download;
653        }
654
655        return downloads;
656    }
657
658    private void evaluateDownloads( RepositorySystemSession session, ResolutionGroup group )
659    {
660        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
661
662        for ( ResolutionItem item : group.items )
663        {
664            ArtifactDownload download = item.download;
665            if ( download == null )
666            {
667                continue;
668            }
669
670            Artifact artifact = download.getArtifact();
671            if ( download.getException() == null )
672            {
673                item.resolved.set( true );
674                item.result.setRepository( group.repository );
675                try
676                {
677                    artifact = artifact.setFile( getFile( session, artifact, download.getFile() ) );
678                    item.result.setArtifact( artifact );
679
680                    lrm.add( session, new LocalArtifactRegistration(
681                            artifact, group.repository, download.getSupportedContexts() ) );
682                }
683                catch ( ArtifactTransferException e )
684                {
685                    download.setException( e );
686                    item.result.addException( e );
687                }
688            }
689            else
690            {
691                item.result.addException( download.getException() );
692            }
693
694            /*
695             * NOTE: Touch after registration with local repo to ensure concurrent resolution is not rejected with
696             * "already updated" via session data when actual update to local repo is still pending.
697             */
698            if ( item.updateCheck != null )
699            {
700                item.updateCheck.setException( download.getException() );
701                updateCheckManager.touchArtifact( session, item.updateCheck );
702            }
703
704            artifactDownloaded( session, download.getTrace(), artifact, group.repository, download.getException() );
705            if ( download.getException() == null )
706            {
707                artifactResolved( session, download.getTrace(), artifact, group.repository, null );
708            }
709        }
710    }
711
712    private void artifactResolving( RepositorySystemSession session, RequestTrace trace, Artifact artifact )
713    {
714        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_RESOLVING );
715        event.setTrace( trace );
716        event.setArtifact( artifact );
717
718        repositoryEventDispatcher.dispatch( event.build() );
719    }
720
721    private void artifactResolved( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
722                                   ArtifactRepository repository, List<Exception> exceptions )
723    {
724        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_RESOLVED );
725        event.setTrace( trace );
726        event.setArtifact( artifact );
727        event.setRepository( repository );
728        event.setExceptions( exceptions );
729        if ( artifact != null )
730        {
731            event.setFile( artifact.getFile() );
732        }
733
734        repositoryEventDispatcher.dispatch( event.build() );
735    }
736
737    private void artifactDownloading( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
738                                      RemoteRepository repository )
739    {
740        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DOWNLOADING );
741        event.setTrace( trace );
742        event.setArtifact( artifact );
743        event.setRepository( repository );
744
745        repositoryEventDispatcher.dispatch( event.build() );
746    }
747
748    private void artifactDownloaded( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
749                                     RemoteRepository repository, Exception exception )
750    {
751        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DOWNLOADED );
752        event.setTrace( trace );
753        event.setArtifact( artifact );
754        event.setRepository( repository );
755        event.setException( exception );
756        if ( artifact != null )
757        {
758            event.setFile( artifact.getFile() );
759        }
760
761        repositoryEventDispatcher.dispatch( event.build() );
762    }
763
764    static class ResolutionGroup
765    {
766
767        final RemoteRepository repository;
768
769        final List<ResolutionItem> items = new ArrayList<>();
770
771        ResolutionGroup( RemoteRepository repository )
772        {
773            this.repository = repository;
774        }
775
776        boolean matches( RemoteRepository repo )
777        {
778            return repository.getUrl().equals( repo.getUrl() )
779                    && repository.getContentType().equals( repo.getContentType() )
780                    && repository.isRepositoryManager() == repo.isRepositoryManager();
781        }
782
783    }
784
785    static class ResolutionItem
786    {
787
788        final RequestTrace trace;
789
790        final ArtifactRequest request;
791
792        final ArtifactResult result;
793
794        final LocalArtifactResult local;
795
796        final RemoteRepository repository;
797
798        final Artifact artifact;
799
800        final AtomicBoolean resolved;
801
802        ArtifactDownload download;
803
804        UpdateCheck<Artifact, ArtifactTransferException> updateCheck;
805
806        ResolutionItem( RequestTrace trace, Artifact artifact, AtomicBoolean resolved, ArtifactResult result,
807                        LocalArtifactResult local, RemoteRepository repository )
808        {
809            this.trace = trace;
810            this.artifact = artifact;
811            this.resolved = resolved;
812            this.result = result;
813            this.request = result.getRequest();
814            this.local = local;
815            this.repository = repository;
816        }
817
818    }
819
820}