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