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     }
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 }