001    package 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    
022    import java.io.File;
023    import java.util.ArrayList;
024    import java.util.Collections;
025    import java.util.LinkedHashMap;
026    import java.util.LinkedHashSet;
027    import java.util.List;
028    import java.util.Map;
029    import java.util.Set;
030    import java.util.concurrent.CountDownLatch;
031    import java.util.concurrent.Executor;
032    import java.util.concurrent.ExecutorService;
033    import java.util.concurrent.LinkedBlockingQueue;
034    import java.util.concurrent.ThreadFactory;
035    import java.util.concurrent.ThreadPoolExecutor;
036    import java.util.concurrent.TimeUnit;
037    import java.util.concurrent.atomic.AtomicInteger;
038    import java.util.regex.Matcher;
039    
040    import org.apache.maven.RepositoryUtils;
041    import org.apache.maven.artifact.Artifact;
042    import org.apache.maven.artifact.factory.ArtifactFactory;
043    import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
044    import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
045    import org.apache.maven.artifact.metadata.ResolutionGroup;
046    import org.apache.maven.artifact.repository.ArtifactRepository;
047    import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager;
048    import org.apache.maven.artifact.repository.RepositoryRequest;
049    import org.apache.maven.artifact.repository.metadata.Snapshot;
050    import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata;
051    import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
052    import org.apache.maven.execution.MavenSession;
053    import org.apache.maven.plugin.LegacySupport;
054    import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest;
055    import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest;
056    import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver;
057    import org.apache.maven.wagon.events.TransferListener;
058    import org.codehaus.plexus.PlexusContainer;
059    import org.codehaus.plexus.component.annotations.Component;
060    import org.codehaus.plexus.component.annotations.Requirement;
061    import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
062    import org.codehaus.plexus.logging.Logger;
063    import org.eclipse.aether.RepositorySystem;
064    import org.eclipse.aether.RepositorySystemSession;
065    import org.eclipse.aether.repository.LocalRepositoryManager;
066    import org.eclipse.aether.resolution.ArtifactRequest;
067    import org.eclipse.aether.resolution.ArtifactResult;
068    
069    /**
070     * @author Jason van Zyl
071     */
072    @Component( role = ArtifactResolver.class )
073    public 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    }