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