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