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