View Javadoc

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