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 = new ThreadPoolExecutor( threads, threads, 3, TimeUnit.SECONDS,
119                                               new LinkedBlockingQueue<Runnable>(), new DaemonThreadCreator() );
120        }
121    }
122
123    private RepositorySystemSession getSession( ArtifactRepository localRepository )
124    {
125        return LegacyLocalRepositoryManager.overlay( localRepository, legacySupport.getRepositorySession(),
126                                                     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",
180                                                     artifact );
181            }
182
183            if ( !systemFile.exists() )
184            {
185                throw new ArtifactNotFoundException( "System artifact: " + artifact + " not found in path: "
186                    + systemFile, artifact );
187            }
188
189            if ( !systemFile.isFile() )
190            {
191                throw new ArtifactNotFoundException( "System artifact: " + artifact + " is not a file: " + systemFile,
192                                                     artifact );
193            }
194
195            artifact.setResolved( true );
196
197            return;
198        }
199
200        if ( !artifact.isResolved() )
201        {
202            ArtifactResult result;
203
204            try
205            {
206                ArtifactRequest artifactRequest = new ArtifactRequest();
207                artifactRequest.setArtifact( RepositoryUtils.toArtifact( artifact ) );
208                artifactRequest.setRepositories( RepositoryUtils.toRepos( remoteRepositories ) );
209
210                // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
211                LocalRepositoryManager lrm = session.getLocalRepositoryManager();
212                String path = lrm.getPathForLocalArtifact( artifactRequest.getArtifact() );
213                artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) );
214
215                result = repoSystem.resolveArtifact( session, artifactRequest );
216            }
217            catch ( org.eclipse.aether.resolution.ArtifactResolutionException e )
218            {
219                if ( e.getCause() instanceof org.eclipse.aether.transfer.ArtifactNotFoundException )
220                {
221                    throw new ArtifactNotFoundException( e.getMessage(), artifact, remoteRepositories, e );
222                }
223                else
224                {
225                    throw new ArtifactResolutionException( e.getMessage(), artifact, remoteRepositories, e );
226                }
227            }
228
229            artifact.selectVersion( result.getArtifact().getVersion() );
230            artifact.setFile( result.getArtifact().getFile() );
231            artifact.setResolved( true );
232
233            if ( artifact.isSnapshot() )
234            {
235                Matcher matcher = Artifact.VERSION_FILE_PATTERN.matcher( artifact.getVersion() );
236                if ( matcher.matches() )
237                {
238                    Snapshot snapshot = new Snapshot();
239                    snapshot.setTimestamp( matcher.group( 2 ) );
240                    try
241                    {
242                        snapshot.setBuildNumber( Integer.parseInt( matcher.group( 3 ) ) );
243                        artifact.addMetadata( new SnapshotArtifactRepositoryMetadata( artifact, snapshot ) );
244                    }
245                    catch ( NumberFormatException e )
246                    {
247                        logger.warn( "Invalid artifact version " + artifact.getVersion() + ": " + e.getMessage() );
248                    }
249                }
250            }
251        }
252    }
253
254    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
255                                                         ArtifactRepository localRepository,
256                                                         List<ArtifactRepository> remoteRepositories,
257                                                         ArtifactMetadataSource source, ArtifactFilter filter )
258                                                             throws ArtifactResolutionException,
259                                                             ArtifactNotFoundException
260    {
261        return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository,
262                                    remoteRepositories, source, filter );
263
264    }
265
266    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
267                                                         Map<String, Artifact> managedVersions,
268                                                         ArtifactRepository localRepository,
269                                                         List<ArtifactRepository> remoteRepositories,
270                                                         ArtifactMetadataSource source )
271                                                             throws ArtifactResolutionException,
272                                                             ArtifactNotFoundException
273    {
274        return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
275                                    remoteRepositories, source, null );
276    }
277
278    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
279                                                         Map<String, Artifact> managedVersions,
280                                                         ArtifactRepository localRepository,
281                                                         List<ArtifactRepository> remoteRepositories,
282                                                         ArtifactMetadataSource source, ArtifactFilter filter )
283                                                             throws ArtifactResolutionException,
284                                                             ArtifactNotFoundException
285    {
286        return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
287                                    remoteRepositories, source, filter, null );
288    }
289
290    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
291                                                         List<ArtifactRepository> remoteRepositories,
292                                                         ArtifactRepository localRepository,
293                                                         ArtifactMetadataSource source )
294                                                             throws ArtifactResolutionException,
295                                                             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,
306                                                             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<String, Artifact> managedVersions,
314                                                         ArtifactRepository localRepository,
315                                                         List<ArtifactRepository> remoteRepositories,
316                                                         ArtifactMetadataSource source, ArtifactFilter filter,
317                                                         List<ResolutionListener> listeners )
318                                                             throws ArtifactResolutionException,
319                                                             ArtifactNotFoundException
320    {
321        return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
322                                    remoteRepositories, source, filter, listeners, null );
323    }
324
325    public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
326                                                         Map<String, Artifact> managedVersions,
327                                                         ArtifactRepository localRepository,
328                                                         List<ArtifactRepository> remoteRepositories,
329                                                         ArtifactMetadataSource source, ArtifactFilter filter,
330                                                         List<ResolutionListener> listeners,
331                                                         List<ConflictResolver> conflictResolvers )
332                                                             throws ArtifactResolutionException,
333                                                             ArtifactNotFoundException
334    {
335        ArtifactResolutionRequest request =
336            new ArtifactResolutionRequest().setArtifact( originatingArtifact ).setResolveRoot( false )
337            // This is required by the surefire plugin
338            .setArtifactDependencies( artifacts ).setManagedVersionMap( managedVersions ).setLocalRepository( localRepository ).setRemoteRepositories( remoteRepositories ).setCollectionFilter( filter ).setListeners( listeners );
339
340        injectSession2( request, legacySupport.getSession() );
341
342        return resolveWithExceptions( request );
343    }
344
345    public ArtifactResolutionResult resolveWithExceptions( ArtifactResolutionRequest request )
346        throws ArtifactResolutionException, ArtifactNotFoundException
347    {
348        ArtifactResolutionResult result = resolve( request );
349
350        // We have collected all the problems so let's mimic the way the old code worked and just blow up right here.
351        // That's right lets just let it rip right here and send a big incomprehensible blob of text at unsuspecting
352        // users. Bad dog!
353
354        resolutionErrorHandler.throwErrors( request, result );
355
356        return result;
357    }
358
359    // ------------------------------------------------------------------------
360    //
361    // ------------------------------------------------------------------------
362
363    public ArtifactResolutionResult resolve( ArtifactResolutionRequest request )
364    {
365        Artifact rootArtifact = request.getArtifact();
366        Set<Artifact> artifacts = request.getArtifactDependencies();
367        Map<String, Artifact> managedVersions = request.getManagedVersionMap();
368        List<ResolutionListener> listeners = request.getListeners();
369        ArtifactFilter collectionFilter = request.getCollectionFilter();
370        ArtifactFilter resolutionFilter = request.getResolutionFilter();
371        RepositorySystemSession session = getSession( request.getLocalRepository() );
372
373        // TODO: hack because metadata isn't generated in m2e correctly and i want to run the maven i have in the
374        // 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<>();
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<>();
451                    allArtifacts.addAll( artifacts );
452                    allArtifacts.addAll( directArtifacts );
453
454                    Map<String, Artifact> mergedArtifacts = new LinkedHashMap<>();
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<>( 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 = artifactCollector.collect( artifacts, rootArtifact, managedVersions, collectionRequest, source,
494                                            collectionFilter, listeners, null );
495
496        // We have metadata retrieval problems, or there are cycles that have been detected
497        // so we give this back to the calling code and let them deal with this information
498        // appropriately.
499
500        if ( result.hasMetadataResolutionExceptions() || result.hasVersionRangeViolations()
501            || 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<>();
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,
552                         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            newThread.setContextClassLoader( null );
575            return newThread;
576        }
577    }
578
579    private class ResolveTask
580        implements Runnable
581    {
582
583        private final ClassLoader classLoader;
584
585        private final CountDownLatch latch;
586
587        private final Artifact artifact;
588
589        private final RepositorySystemSession session;
590
591        private final List<ArtifactRepository> remoteRepositories;
592
593        private final ArtifactResolutionResult result;
594
595        public ResolveTask( ClassLoader classLoader, CountDownLatch latch, Artifact artifact,
596                            RepositorySystemSession session, List<ArtifactRepository> remoteRepositories,
597                            ArtifactResolutionResult result )
598        {
599            this.classLoader = classLoader;
600            this.latch = latch;
601            this.artifact = artifact;
602            this.session = session;
603            this.remoteRepositories = remoteRepositories;
604            this.result = result;
605        }
606
607        public void run()
608        {
609            ClassLoader old = Thread.currentThread().getContextClassLoader();
610            try
611            {
612                Thread.currentThread().setContextClassLoader( classLoader );
613                resolve( artifact, remoteRepositories, session );
614            }
615            catch ( ArtifactNotFoundException anfe )
616            {
617                // These are cases where the artifact just isn't present in any of the remote repositories
618                // because it wasn't deployed, or it was deployed in the wrong place.
619
620                synchronized ( result )
621                {
622                    result.addMissingArtifact( artifact );
623                }
624            }
625            catch ( ArtifactResolutionException e )
626            {
627                // This is really a wagon TransferFailedException so something went wrong after we successfully
628                // retrieved the metadata.
629
630                synchronized ( result )
631                {
632                    result.addErrorArtifactException( e );
633                }
634            }
635            finally
636            {
637                latch.countDown();
638                Thread.currentThread().setContextClassLoader( old );
639
640            }
641        }
642
643    }
644
645    @Override
646    public void dispose()
647    {
648        if ( executor instanceof ExecutorService )
649        {
650            ( (ExecutorService) executor ).shutdownNow();
651        }
652    }
653
654}