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            LOGGER.debug( "Resolving artifact {} from {}", artifact, repos );
357            AtomicBoolean resolved = new AtomicBoolean( false );
358            Iterator<ResolutionGroup> groupIt = groups.iterator();
359            for ( RemoteRepository repo : repos )
360            {
361                if ( !repo.getPolicy( artifact.isSnapshot() ).isEnabled() )
362                {
363                    continue;
364                }
365
366                try
367                {
368                    Utils.checkOffline( session, offlineController, repo );
369                }
370                catch ( RepositoryOfflineException e )
371                {
372                    Exception exception =
373                        new ArtifactNotFoundException( artifact, repo, "Cannot access " + repo.getId() + " ("
374                            + repo.getUrl() + ") in offline mode and the artifact " + artifact
375                            + " has not been downloaded from it before.", e );
376                    result.addException( exception );
377                    continue;
378                }
379
380                ResolutionGroup group = null;
381                while ( groupIt.hasNext() )
382                {
383                    ResolutionGroup t = groupIt.next();
384                    if ( t.matches( repo ) )
385                    {
386                        group = t;
387                        break;
388                    }
389                }
390                if ( group == null )
391                {
392                    group = new ResolutionGroup( repo );
393                    groups.add( group );
394                    groupIt = Collections.<ResolutionGroup>emptyList().iterator();
395                }
396                group.items.add( new ResolutionItem( trace, artifact, resolved, result, local, repo ) );
397            }
398        }
399
400        for ( ResolutionGroup group : groups )
401        {
402            performDownloads( session, group );
403        }
404
405        for ( ArtifactResult result : results )
406        {
407            ArtifactRequest request = result.getRequest();
408
409            Artifact artifact = result.getArtifact();
410            if ( artifact == null || artifact.getFile() == null )
411            {
412                failures = true;
413                if ( result.getExceptions().isEmpty() )
414                {
415                    Exception exception = new ArtifactNotFoundException( request.getArtifact(), null );
416                    result.addException( exception );
417                }
418                RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
419                artifactResolved( session, trace, request.getArtifact(), null, result.getExceptions() );
420            }
421        }
422
423        if ( failures )
424        {
425            throw new ArtifactResolutionException( results );
426        }
427
428        return results;
429    }
430
431    private boolean isLocallyInstalled( LocalArtifactResult lar, VersionResult vr )
432    {
433        if ( lar.isAvailable() )
434        {
435            return true;
436        }
437        if ( lar.getFile() != null )
438        {
439            if ( vr.getRepository() instanceof LocalRepository )
440            {
441                // resolution of (snapshot) version found locally installed artifact
442                return true;
443            }
444            else if ( vr.getRepository() == null && lar.getRequest().getRepositories().isEmpty() )
445            {
446                // resolution of version range found locally installed artifact
447                return true;
448            }
449        }
450        return false;
451    }
452
453    private File getFile( RepositorySystemSession session, Artifact artifact, File file )
454        throws ArtifactTransferException
455    {
456        if ( artifact.isSnapshot() && !artifact.getVersion().equals( artifact.getBaseVersion() )
457            && ConfigUtils.getBoolean( session, true, CONFIG_PROP_SNAPSHOT_NORMALIZATION ) )
458        {
459            String name = file.getName().replace( artifact.getVersion(), artifact.getBaseVersion() );
460            File dst = new File( file.getParent(), name );
461
462            boolean copy = dst.length() != file.length() || dst.lastModified() != file.lastModified();
463            if ( copy )
464            {
465                try
466                {
467                    fileProcessor.copy( file, dst );
468                    dst.setLastModified( file.lastModified() );
469                }
470                catch ( IOException e )
471                {
472                    throw new ArtifactTransferException( artifact, null, e );
473                }
474            }
475
476            file = dst;
477        }
478
479        return file;
480    }
481
482    private void performDownloads( RepositorySystemSession session, ResolutionGroup group )
483    {
484        List<ArtifactDownload> downloads = gatherDownloads( session, group );
485        if ( downloads.isEmpty() )
486        {
487            return;
488        }
489
490        for ( ArtifactDownload download : downloads )
491        {
492            artifactDownloading( session, download.getTrace(), download.getArtifact(), group.repository );
493        }
494
495        try
496        {
497            RemoteRepository repo = group.repository;
498            if ( repo.isBlocked() )
499            {
500                if ( repo.getMirroredRepositories().isEmpty() )
501                {
502                    throw new NoRepositoryConnectorException( repo, "Blocked repository: " + repo );
503                }
504                else
505                {
506                    throw new NoRepositoryConnectorException( repo, "Blocked mirror for repositories: "
507                        + repo.getMirroredRepositories() );
508                }
509            }
510
511            try ( RepositoryConnector connector =
512                          repositoryConnectorProvider.newRepositoryConnector( session, group.repository ) )
513            {
514                connector.get( downloads, null );
515            }
516        }
517        catch ( NoRepositoryConnectorException e )
518        {
519            for ( ArtifactDownload download : downloads )
520            {
521                download.setException( new ArtifactTransferException( download.getArtifact(), group.repository, e ) );
522            }
523        }
524
525        evaluateDownloads( session, group );
526    }
527
528    private List<ArtifactDownload> gatherDownloads( RepositorySystemSession session, ResolutionGroup group )
529    {
530        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
531        List<ArtifactDownload> downloads = new ArrayList<>();
532
533        for ( ResolutionItem item : group.items )
534        {
535            Artifact artifact = item.artifact;
536
537            if ( item.resolved.get() )
538            {
539                // resolved in previous resolution group
540                continue;
541            }
542
543            ArtifactDownload download = new ArtifactDownload();
544            download.setArtifact( artifact );
545            download.setRequestContext( item.request.getRequestContext() );
546            download.setListener( SafeTransferListener.wrap( session ) );
547            download.setTrace( item.trace );
548            if ( item.local.getFile() != null )
549            {
550                download.setFile( item.local.getFile() );
551                download.setExistenceCheck( true );
552            }
553            else
554            {
555                String path =
556                    lrm.getPathForRemoteArtifact( artifact, group.repository, item.request.getRequestContext() );
557                download.setFile( new File( lrm.getRepository().getBasedir(), path ) );
558            }
559
560            boolean snapshot = artifact.isSnapshot();
561            RepositoryPolicy policy =
562                remoteRepositoryManager.getPolicy( session, group.repository, !snapshot, snapshot );
563
564            int errorPolicy = Utils.getPolicy( session, artifact, group.repository );
565            if ( ( errorPolicy & ResolutionErrorPolicy.CACHE_ALL ) != 0 )
566            {
567                UpdateCheck<Artifact, ArtifactTransferException> check = new UpdateCheck<>();
568                check.setItem( artifact );
569                check.setFile( download.getFile() );
570                check.setFileValid( false );
571                check.setRepository( group.repository );
572                check.setPolicy( policy.getUpdatePolicy() );
573                item.updateCheck = check;
574                updateCheckManager.checkArtifact( session, check );
575                if ( !check.isRequired() )
576                {
577                    item.result.addException( check.getException() );
578                    continue;
579                }
580            }
581
582            download.setChecksumPolicy( policy.getChecksumPolicy() );
583            download.setRepositories( item.repository.getMirroredRepositories() );
584            downloads.add( download );
585            item.download = download;
586        }
587
588        return downloads;
589    }
590
591    private void evaluateDownloads( RepositorySystemSession session, ResolutionGroup group )
592    {
593        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
594
595        for ( ResolutionItem item : group.items )
596        {
597            ArtifactDownload download = item.download;
598            if ( download == null )
599            {
600                continue;
601            }
602
603            Artifact artifact = download.getArtifact();
604            if ( download.getException() == null )
605            {
606                item.resolved.set( true );
607                item.result.setRepository( group.repository );
608                try
609                {
610                    artifact = artifact.setFile( getFile( session, artifact, download.getFile() ) );
611                    item.result.setArtifact( artifact );
612
613                    lrm.add( session, new LocalArtifactRegistration(
614                            artifact, group.repository, download.getSupportedContexts() ) );
615                }
616                catch ( ArtifactTransferException e )
617                {
618                    download.setException( e );
619                    item.result.addException( e );
620                }
621            }
622            else
623            {
624                item.result.addException( download.getException() );
625            }
626
627            /*
628             * NOTE: Touch after registration with local repo to ensure concurrent resolution is not rejected with
629             * "already updated" via session data when actual update to local repo is still pending.
630             */
631            if ( item.updateCheck != null )
632            {
633                item.updateCheck.setException( download.getException() );
634                updateCheckManager.touchArtifact( session, item.updateCheck );
635            }
636
637            artifactDownloaded( session, download.getTrace(), artifact, group.repository, download.getException() );
638            if ( download.getException() == null )
639            {
640                artifactResolved( session, download.getTrace(), artifact, group.repository, null );
641            }
642        }
643    }
644
645    private void artifactResolving( RepositorySystemSession session, RequestTrace trace, Artifact artifact )
646    {
647        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_RESOLVING );
648        event.setTrace( trace );
649        event.setArtifact( artifact );
650
651        repositoryEventDispatcher.dispatch( event.build() );
652    }
653
654    private void artifactResolved( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
655                                   ArtifactRepository repository, List<Exception> exceptions )
656    {
657        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_RESOLVED );
658        event.setTrace( trace );
659        event.setArtifact( artifact );
660        event.setRepository( repository );
661        event.setExceptions( exceptions );
662        if ( artifact != null )
663        {
664            event.setFile( artifact.getFile() );
665        }
666
667        repositoryEventDispatcher.dispatch( event.build() );
668    }
669
670    private void artifactDownloading( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
671                                      RemoteRepository repository )
672    {
673        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DOWNLOADING );
674        event.setTrace( trace );
675        event.setArtifact( artifact );
676        event.setRepository( repository );
677
678        repositoryEventDispatcher.dispatch( event.build() );
679    }
680
681    private void artifactDownloaded( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
682                                     RemoteRepository repository, Exception exception )
683    {
684        RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DOWNLOADED );
685        event.setTrace( trace );
686        event.setArtifact( artifact );
687        event.setRepository( repository );
688        event.setException( exception );
689        if ( artifact != null )
690        {
691            event.setFile( artifact.getFile() );
692        }
693
694        repositoryEventDispatcher.dispatch( event.build() );
695    }
696
697    static class ResolutionGroup
698    {
699
700        final RemoteRepository repository;
701
702        final List<ResolutionItem> items = new ArrayList<>();
703
704        ResolutionGroup( RemoteRepository repository )
705        {
706            this.repository = repository;
707        }
708
709        boolean matches( RemoteRepository repo )
710        {
711            return repository.getUrl().equals( repo.getUrl() )
712                && repository.getContentType().equals( repo.getContentType() )
713                && repository.isRepositoryManager() == repo.isRepositoryManager();
714        }
715
716    }
717
718    static class ResolutionItem
719    {
720
721        final RequestTrace trace;
722
723        final ArtifactRequest request;
724
725        final ArtifactResult result;
726
727        final LocalArtifactResult local;
728
729        final RemoteRepository repository;
730
731        final Artifact artifact;
732
733        final AtomicBoolean resolved;
734
735        ArtifactDownload download;
736
737        UpdateCheck<Artifact, ArtifactTransferException> updateCheck;
738
739        ResolutionItem( RequestTrace trace, Artifact artifact, AtomicBoolean resolved, ArtifactResult result,
740                        LocalArtifactResult local, RemoteRepository repository )
741        {
742            this.trace = trace;
743            this.artifact = artifact;
744            this.resolved = resolved;
745            this.result = result;
746            this.request = result.getRequest();
747            this.local = local;
748            this.repository = repository;
749        }
750
751    }
752
753}