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