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