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