001package org.apache.maven.artifact.resolver;
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.util.ArrayList;
024import java.util.Collections;
025import java.util.LinkedHashMap;
026import java.util.LinkedHashSet;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030import java.util.concurrent.CountDownLatch;
031import java.util.concurrent.Executor;
032import java.util.concurrent.ExecutorService;
033import java.util.concurrent.LinkedBlockingQueue;
034import java.util.concurrent.ThreadFactory;
035import java.util.concurrent.ThreadPoolExecutor;
036import java.util.concurrent.TimeUnit;
037import java.util.concurrent.atomic.AtomicInteger;
038import java.util.regex.Matcher;
039
040import org.apache.maven.RepositoryUtils;
041import org.apache.maven.artifact.Artifact;
042import org.apache.maven.artifact.factory.ArtifactFactory;
043import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
044import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
045import org.apache.maven.artifact.metadata.ResolutionGroup;
046import org.apache.maven.artifact.repository.ArtifactRepository;
047import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager;
048import org.apache.maven.artifact.repository.RepositoryRequest;
049import org.apache.maven.artifact.repository.metadata.Snapshot;
050import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata;
051import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
052import org.apache.maven.execution.MavenSession;
053import org.apache.maven.plugin.LegacySupport;
054import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest;
055import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest;
056import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver;
057import org.apache.maven.wagon.events.TransferListener;
058import org.codehaus.plexus.PlexusContainer;
059import org.codehaus.plexus.component.annotations.Component;
060import org.codehaus.plexus.component.annotations.Requirement;
061import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
062import org.codehaus.plexus.logging.Logger;
063import org.eclipse.aether.RepositorySystem;
064import org.eclipse.aether.RepositorySystemSession;
065import org.eclipse.aether.repository.LocalRepositoryManager;
066import org.eclipse.aether.resolution.ArtifactRequest;
067import org.eclipse.aether.resolution.ArtifactResult;
068
069/**
070 * @author Jason van Zyl
071 */
072@Component( role = ArtifactResolver.class )
073public class DefaultArtifactResolver
074    implements ArtifactResolver
075{
076    @Requirement 
077    private Logger logger;
078
079    @Requirement
080    protected ArtifactFactory artifactFactory;
081
082    @Requirement
083    private ArtifactCollector artifactCollector;
084
085    @Requirement
086    private ResolutionErrorHandler resolutionErrorHandler;
087
088    @Requirement
089    private ArtifactMetadataSource source;
090    
091    @Requirement
092    private PlexusContainer container;
093
094    @Requirement
095    private LegacySupport legacySupport;
096
097    @Requirement
098    private RepositorySystem repoSystem;
099
100    private final Executor executor;
101
102    public DefaultArtifactResolver()
103    {
104        int threads = Integer.getInteger( "maven.artifact.threads", 5 );
105        if ( threads <= 1 )
106        {
107            executor = new Executor()
108            {
109                public void execute( Runnable command )
110                {
111                    command.run();
112                }
113            };
114        }
115        else
116        {
117            executor =
118                new ThreadPoolExecutor( threads, threads, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
119                                        new DaemonThreadCreator() );
120        }
121    }
122
123    @Override
124    protected void finalize()
125        throws Throwable
126    {
127        if ( executor instanceof ExecutorService )
128        {
129            ( (ExecutorService) executor ).shutdown();
130        }
131    }
132
133    private RepositorySystemSession getSession( ArtifactRepository localRepository )
134    {
135        return LegacyLocalRepositoryManager.overlay( localRepository, legacySupport.getRepositorySession(), repoSystem );
136    }
137
138    private void injectSession1( RepositoryRequest request, MavenSession session )
139    {
140        if ( session != null )
141        {
142            request.setOffline( session.isOffline() );
143            request.setForceUpdate( session.getRequest().isUpdateSnapshots() );
144        }
145    }
146
147    private void injectSession2( ArtifactResolutionRequest request, MavenSession session )
148    {
149        injectSession1( request, session );
150
151        if ( session != null )
152        {
153            request.setServers( session.getRequest().getServers() );
154            request.setMirrors( session.getRequest().getMirrors() );
155            request.setProxies( session.getRequest().getProxies() );
156        }
157    }
158
159    public void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories,
160                         ArtifactRepository localRepository, TransferListener resolutionListener )
161        throws ArtifactResolutionException, ArtifactNotFoundException
162    {
163        resolve( artifact, remoteRepositories, getSession( localRepository ) );
164    }
165
166    public void resolveAlways( Artifact artifact, List<ArtifactRepository> remoteRepositories,
167                               ArtifactRepository localRepository )
168        throws ArtifactResolutionException, ArtifactNotFoundException
169    {
170        resolve( artifact, remoteRepositories, getSession( localRepository ) );
171    }
172
173    private void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories,
174                          RepositorySystemSession session )
175        throws ArtifactResolutionException, ArtifactNotFoundException
176    {
177        if ( artifact == null )
178        {
179            return;
180        }
181        
182        if ( Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) )
183        {
184            File systemFile = artifact.getFile();
185
186            if ( systemFile == null )
187            {
188                throw new ArtifactNotFoundException( "System artifact: " + artifact + " has no file attached", artifact );
189            }
190
191            if ( !systemFile.exists() )
192            {
193                throw new ArtifactNotFoundException( "System artifact: " + artifact + " not found in path: " + systemFile, artifact );
194            }
195
196            if ( !systemFile.isFile() )
197            {
198                throw new ArtifactNotFoundException( "System artifact: " + artifact + " is not a file: " + systemFile, artifact );
199            }
200
201            artifact.setResolved( true );
202            
203            return;
204        }
205
206        if ( !artifact.isResolved() )
207        {
208            ArtifactResult result;
209
210            try
211            {
212                ArtifactRequest artifactRequest = new ArtifactRequest();
213                artifactRequest.setArtifact( RepositoryUtils.toArtifact( artifact ) );
214                artifactRequest.setRepositories( RepositoryUtils.toRepos( remoteRepositories ) );
215
216                // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
217                LocalRepositoryManager lrm = session.getLocalRepositoryManager();
218                String path = lrm.getPathForLocalArtifact( artifactRequest.getArtifact() );
219                artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) );
220
221                result = repoSystem.resolveArtifact( session, artifactRequest );
222            }
223            catch ( org.eclipse.aether.resolution.ArtifactResolutionException e )
224            {
225                if ( e.getCause() instanceof org.eclipse.aether.transfer.ArtifactNotFoundException )
226                {
227                    throw new ArtifactNotFoundException( e.getMessage(), artifact, remoteRepositories, e );
228                }
229                else
230                {
231                    throw new ArtifactResolutionException( e.getMessage(), artifact, remoteRepositories, e );
232                }
233            }
234
235            artifact.selectVersion( result.getArtifact().getVersion() );
236            artifact.setFile( result.getArtifact().getFile() );
237            artifact.setResolved( true );
238
239            if ( artifact.isSnapshot() )
240            {
241                Matcher matcher = Artifact.VERSION_FILE_PATTERN.matcher( artifact.getVersion() );
242                if ( matcher.matches() )
243                {
244                    Snapshot snapshot = new Snapshot();
245                    snapshot.setTimestamp( matcher.group( 2 ) );
246                    try
247                    {
248                        snapshot.setBuildNumber( Integer.parseInt( matcher.group( 3 ) ) );
249                        artifact.addMetadata( new SnapshotArtifactRepositoryMetadata( artifact, snapshot ) );
250                    }
251                    catch ( NumberFormatException e )
252                    {
253                        logger.warn( "Invalid artifact version " + artifact.getVersion() + ": " + e.getMessage() );
254                    }
255                }
256            }
257        }
258    }
259
260    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
261                                                         ArtifactRepository localRepository,
262                                                         List<ArtifactRepository> remoteRepositories,
263                                                         ArtifactMetadataSource source, ArtifactFilter filter )
264        throws ArtifactResolutionException, ArtifactNotFoundException
265    {
266        return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository,
267                                    remoteRepositories, source, filter );
268
269    }
270
271    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
272                                                         Map managedVersions, ArtifactRepository localRepository,
273                                                         List<ArtifactRepository> remoteRepositories,
274                                                         ArtifactMetadataSource source )
275        throws ArtifactResolutionException, ArtifactNotFoundException
276    {
277        return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
278                                    remoteRepositories, source, null );
279    }
280
281    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
282                                                         Map managedVersions, ArtifactRepository localRepository,
283                                                         List<ArtifactRepository> remoteRepositories,
284                                                         ArtifactMetadataSource source, ArtifactFilter filter )
285        throws ArtifactResolutionException, ArtifactNotFoundException
286    {
287        return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
288                                    remoteRepositories, source, filter, null );
289    }
290
291    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
292                                                         List<ArtifactRepository> remoteRepositories,
293                                                         ArtifactRepository localRepository,
294                                                         ArtifactMetadataSource source )
295        throws ArtifactResolutionException, ArtifactNotFoundException
296    {
297        return resolveTransitively( artifacts, originatingArtifact, localRepository, remoteRepositories, source, null );
298    }
299
300    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
301                                                         List<ArtifactRepository> remoteRepositories,
302                                                         ArtifactRepository localRepository,
303                                                         ArtifactMetadataSource source,
304                                                         List<ResolutionListener> listeners )
305        throws ArtifactResolutionException, ArtifactNotFoundException
306    {
307        return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository,
308                                    remoteRepositories, source, null, listeners );
309    }
310
311    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
312                                                         Map managedVersions, ArtifactRepository localRepository,
313                                                         List<ArtifactRepository> remoteRepositories,
314                                                         ArtifactMetadataSource source, ArtifactFilter filter,
315                                                         List<ResolutionListener> listeners )
316        throws ArtifactResolutionException, ArtifactNotFoundException
317    {
318        return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
319                                    remoteRepositories, source, filter, listeners, null );
320    }
321
322    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
323                                                         Map managedVersions, ArtifactRepository localRepository,
324                                                         List<ArtifactRepository> remoteRepositories,
325                                                         ArtifactMetadataSource source, ArtifactFilter filter,
326                                                         List<ResolutionListener> listeners,
327                                                         List<ConflictResolver> conflictResolvers )
328        throws ArtifactResolutionException, ArtifactNotFoundException
329    {
330        ArtifactResolutionRequest request = new ArtifactResolutionRequest()
331            .setArtifact( originatingArtifact )
332            .setResolveRoot( false )
333            // This is required by the surefire plugin
334            .setArtifactDependencies( artifacts )            
335            .setManagedVersionMap( managedVersions )
336            .setLocalRepository( localRepository )
337            .setRemoteRepositories( remoteRepositories )
338            .setCollectionFilter( filter )
339            .setListeners( listeners );
340
341        injectSession2( request, legacySupport.getSession() );
342
343        return resolveWithExceptions( request );
344    }
345
346    public ArtifactResolutionResult resolveWithExceptions( ArtifactResolutionRequest request )
347        throws ArtifactResolutionException, ArtifactNotFoundException
348    {
349        ArtifactResolutionResult result = resolve( request );
350
351        // We have collected all the problems so let's mimic the way the old code worked and just blow up right here.
352        // That's right lets just let it rip right here and send a big incomprehensible blob of text at unsuspecting
353        // users. Bad dog!
354
355        resolutionErrorHandler.throwErrors( request, result );
356
357        return result;
358    }
359
360    // ------------------------------------------------------------------------
361    //
362    // ------------------------------------------------------------------------
363
364    public ArtifactResolutionResult resolve( ArtifactResolutionRequest request )
365    {
366        Artifact rootArtifact = request.getArtifact();
367        Set<Artifact> artifacts = request.getArtifactDependencies();
368        Map<String, Artifact> managedVersions = request.getManagedVersionMap();
369        List<ResolutionListener> listeners = request.getListeners();
370        ArtifactFilter collectionFilter = request.getCollectionFilter();                       
371        ArtifactFilter resolutionFilter = request.getResolutionFilter();
372        RepositorySystemSession session = getSession( request.getLocalRepository() );
373        
374        //TODO: hack because metadata isn't generated in m2e correctly and i want to run the maven i have in the workspace
375        if ( source == null )
376        {
377            try
378            {
379                source = container.lookup( ArtifactMetadataSource.class );
380            }
381            catch ( ComponentLookupException e )
382            {
383                // won't happen
384            }
385        }
386
387        if ( listeners == null )
388        {
389            listeners = new ArrayList<ResolutionListener>();
390
391            if ( logger.isDebugEnabled() )
392            {
393                listeners.add( new DebugResolutionListener( logger ) );
394            }
395
396            listeners.add( new WarningResolutionListener( logger ) );
397        }
398
399        ArtifactResolutionResult result = new ArtifactResolutionResult();
400
401        // The root artifact may, or may not be resolved so we need to check before we attempt to resolve.
402        // This is often an artifact like a POM that is taken from disk and we already have hold of the
403        // file reference. But this may be a Maven Plugin that we need to resolve from a remote repository
404        // as well as its dependencies.
405                        
406        if ( request.isResolveRoot() /* && rootArtifact.getFile() == null */ )
407        {            
408            try
409            {
410                resolve( rootArtifact, request.getRemoteRepositories(), session );
411            }
412            catch ( ArtifactResolutionException e )
413            {
414                result.addErrorArtifactException( e );
415                return result;
416            }
417            catch ( ArtifactNotFoundException e )
418            {
419                result.addMissingArtifact( request.getArtifact() );
420                return result;
421            }
422        }
423
424        ArtifactResolutionRequest collectionRequest = request;
425
426        if ( request.isResolveTransitively() )
427        {
428            MetadataResolutionRequest metadataRequest = new DefaultMetadataResolutionRequest( request );
429
430            metadataRequest.setArtifact( rootArtifact );
431            metadataRequest.setResolveManagedVersions( managedVersions == null );
432
433            try
434            {
435                ResolutionGroup resolutionGroup = source.retrieve( metadataRequest );
436
437                if ( managedVersions == null )
438                {
439                    managedVersions = resolutionGroup.getManagedVersions();
440                }
441
442                Set<Artifact> directArtifacts = resolutionGroup.getArtifacts();
443
444                if ( artifacts == null || artifacts.isEmpty() )
445                {
446                    artifacts = directArtifacts;
447                }
448                else
449                {
450                    List<Artifact> allArtifacts = new ArrayList<Artifact>();
451                    allArtifacts.addAll( artifacts );
452                    allArtifacts.addAll( directArtifacts );
453
454                    Map<String, Artifact> mergedArtifacts = new LinkedHashMap<String, Artifact>();
455                    for ( Artifact artifact : allArtifacts )
456                    {
457                        String conflictId = artifact.getDependencyConflictId();
458                        if ( !mergedArtifacts.containsKey( conflictId ) )
459                        {
460                            mergedArtifacts.put( conflictId, artifact );
461                        }
462                    }
463
464                    artifacts = new LinkedHashSet<Artifact>( mergedArtifacts.values() );
465                }
466
467                collectionRequest = new ArtifactResolutionRequest( request );
468                collectionRequest.setServers( request.getServers() );
469                collectionRequest.setMirrors( request.getMirrors() );
470                collectionRequest.setProxies( request.getProxies() );
471                collectionRequest.setRemoteRepositories( resolutionGroup.getResolutionRepositories() );
472            }
473            catch ( ArtifactMetadataRetrievalException e )
474            {
475                ArtifactResolutionException are =
476                    new ArtifactResolutionException( "Unable to get dependency information for " + rootArtifact.getId()
477                        + ": " + e.getMessage(), rootArtifact, metadataRequest.getRemoteRepositories(), e );
478                result.addMetadataResolutionException( are );
479                return result;
480            }
481        }
482        
483        if ( artifacts == null || artifacts.isEmpty() )
484        {
485            if ( request.isResolveRoot() )
486            {
487                result.addArtifact( rootArtifact );
488            }
489            return result;
490        } 
491
492        // After the collection we will have the artifact object in the result but they will not be resolved yet.
493        result =
494            artifactCollector.collect( artifacts, rootArtifact, managedVersions, collectionRequest, source,
495                                       collectionFilter, listeners, null );
496                        
497        // We have metadata retrieval problems, or there are cycles that have been detected
498        // so we give this back to the calling code and let them deal with this information
499        // appropriately.
500
501        if ( result.hasMetadataResolutionExceptions() || result.hasVersionRangeViolations() || result.hasCircularDependencyExceptions() )
502        {
503            return result;
504        }
505
506        if ( result.getArtifactResolutionNodes() != null )
507        {
508            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
509
510            CountDownLatch latch = new CountDownLatch( result.getArtifactResolutionNodes().size() );
511
512            for ( ResolutionNode node : result.getArtifactResolutionNodes() )
513            {
514                Artifact artifact = node.getArtifact();
515
516                if ( resolutionFilter == null || resolutionFilter.include( artifact ) )
517                {
518                    executor.execute( new ResolveTask( classLoader, latch, artifact, session,
519                                                       node.getRemoteRepositories(), result ) );
520                }
521                else
522                {
523                    latch.countDown();
524                }
525            }
526            try
527            {
528                latch.await();
529            }
530            catch ( InterruptedException e )
531            {
532                result.addErrorArtifactException( new ArtifactResolutionException( "Resolution interrupted",
533                                                                                   rootArtifact, e ) );
534            }
535        }
536
537        // We want to send the root artifact back in the result but we need to do this after the other dependencies
538        // have been resolved.
539        if ( request.isResolveRoot() )
540        {            
541            // Add the root artifact (as the first artifact to retain logical order of class path!)
542            Set<Artifact> allArtifacts = new LinkedHashSet<Artifact>();
543            allArtifacts.add( rootArtifact );
544            allArtifacts.addAll( result.getArtifacts() );
545            result.setArtifacts( allArtifacts );
546        }                        
547                 
548        return result;
549    }
550
551    public void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories, ArtifactRepository localRepository )
552        throws ArtifactResolutionException, ArtifactNotFoundException
553    {
554        resolve( artifact, remoteRepositories, localRepository, null );
555    }
556
557    /**
558     * ThreadCreator for creating daemon threads with fixed ThreadGroup-name.
559     */
560    static final class DaemonThreadCreator
561        implements ThreadFactory
562    {
563        static final String THREADGROUP_NAME = "org.apache.maven.artifact.resolver.DefaultArtifactResolver";
564
565        static final ThreadGroup GROUP = new ThreadGroup( THREADGROUP_NAME );
566
567        static final AtomicInteger THREAD_NUMBER = new AtomicInteger( 1 );
568
569        public Thread newThread( Runnable r )
570        {
571            Thread newThread = new Thread( GROUP, r, "resolver-" + THREAD_NUMBER.getAndIncrement() );
572            newThread.setDaemon( true );
573            return newThread;
574        }
575    }
576
577    private class ResolveTask
578        implements Runnable
579    {
580
581        private final ClassLoader classLoader;
582
583        private final CountDownLatch latch;
584
585        private final Artifact artifact;
586
587        private final RepositorySystemSession session;
588
589        private final List<ArtifactRepository> remoteRepositories;
590
591        private final ArtifactResolutionResult result;
592
593        public ResolveTask( ClassLoader classLoader, CountDownLatch latch, Artifact artifact, RepositorySystemSession session,
594                            List<ArtifactRepository> remoteRepositories, ArtifactResolutionResult result )
595        {
596            this.classLoader = classLoader;
597            this.latch = latch;
598            this.artifact = artifact;
599            this.session = session;
600            this.remoteRepositories = remoteRepositories;
601            this.result = result;
602        }
603
604        public void run()
605        {
606            ClassLoader old = Thread.currentThread().getContextClassLoader();
607            try
608            {
609                Thread.currentThread().setContextClassLoader( classLoader );
610                resolve( artifact, remoteRepositories, session );
611            }
612            catch ( ArtifactNotFoundException anfe )
613            {
614                // These are cases where the artifact just isn't present in any of the remote repositories
615                // because it wasn't deployed, or it was deployed in the wrong place.
616
617                synchronized ( result )
618                {
619                    result.addMissingArtifact( artifact );
620                }
621            }
622            catch ( ArtifactResolutionException e )
623            {
624                // This is really a wagon TransferFailedException so something went wrong after we successfully
625                // retrieved the metadata.
626
627                synchronized ( result )
628                {
629                    result.addErrorArtifactException( e );
630                }
631            }
632            finally
633            {
634                latch.countDown();
635                Thread.currentThread().setContextClassLoader( old );
636
637            }
638        }
639
640    }
641
642}