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