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