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(
262                 artifacts, originatingArtifact, Collections.<String, Artifact>emptyMap(), localRepository,
263                                     remoteRepositories, source, filter );
264 
265     }
266 
267     public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
268                                                          Map<String, Artifact> managedVersions,
269                                                          ArtifactRepository localRepository,
270                                                          List<ArtifactRepository> remoteRepositories,
271                                                          ArtifactMetadataSource source )
272                                                              throws ArtifactResolutionException,
273                                                              ArtifactNotFoundException
274     {
275         return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
276                                     remoteRepositories, source, null );
277     }
278 
279     public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
280                                                          Map<String, Artifact> managedVersions,
281                                                          ArtifactRepository localRepository,
282                                                          List<ArtifactRepository> remoteRepositories,
283                                                          ArtifactMetadataSource source, ArtifactFilter filter )
284                                                              throws ArtifactResolutionException,
285                                                              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,
296                                                              ArtifactNotFoundException
297     {
298         return resolveTransitively( artifacts, originatingArtifact, localRepository, remoteRepositories, source, null );
299     }
300 
301     public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
302                                                          List<ArtifactRepository> remoteRepositories,
303                                                          ArtifactRepository localRepository,
304                                                          ArtifactMetadataSource source,
305                                                          List<ResolutionListener> listeners )
306                                                              throws ArtifactResolutionException,
307                                                              ArtifactNotFoundException
308     {
309         return resolveTransitively(
310                 artifacts, originatingArtifact, Collections.<String, Artifact>emptyMap(), localRepository,
311                                     remoteRepositories, source, null, listeners );
312     }
313 
314     @SuppressWarnings( "checkstyle:parameternumber" )
315     public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
316                                                          Map<String, Artifact> managedVersions,
317                                                          ArtifactRepository localRepository,
318                                                          List<ArtifactRepository> remoteRepositories,
319                                                          ArtifactMetadataSource source, ArtifactFilter filter,
320                                                          List<ResolutionListener> listeners )
321                                                              throws ArtifactResolutionException,
322                                                              ArtifactNotFoundException
323     {
324         return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
325                                     remoteRepositories, source, filter, listeners, null );
326     }
327 
328     @SuppressWarnings( "checkstyle:parameternumber" )
329     public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
330                                                          Map<String, Artifact> managedVersions,
331                                                          ArtifactRepository localRepository,
332                                                          List<ArtifactRepository> remoteRepositories,
333                                                          ArtifactMetadataSource source, ArtifactFilter filter,
334                                                          List<ResolutionListener> listeners,
335                                                          List<ConflictResolver> conflictResolvers )
336                                                              throws ArtifactResolutionException,
337                                                              ArtifactNotFoundException
338     {
339         ArtifactResolutionRequest request = new ArtifactResolutionRequest().
340             setArtifact( originatingArtifact ).
341             setResolveRoot( false ).
342             // This is required by the surefire plugin
343             setArtifactDependencies( artifacts ).
344             setManagedVersionMap( managedVersions ).
345             setLocalRepository( localRepository ).
346             setRemoteRepositories( remoteRepositories ).
347             setCollectionFilter( filter ).
348             setListeners( listeners );
349 
350         injectSession2( request, legacySupport.getSession() );
351 
352         return resolveWithExceptions( request );
353     }
354 
355     public ArtifactResolutionResult resolveWithExceptions( ArtifactResolutionRequest request )
356         throws ArtifactResolutionException, ArtifactNotFoundException
357     {
358         ArtifactResolutionResult result = resolve( request );
359 
360         // We have collected all the problems so let's mimic the way the old code worked and just blow up right here.
361         // That's right lets just let it rip right here and send a big incomprehensible blob of text at unsuspecting
362         // users. Bad dog!
363 
364         resolutionErrorHandler.throwErrors( request, result );
365 
366         return result;
367     }
368 
369     // ------------------------------------------------------------------------
370     //
371     // ------------------------------------------------------------------------
372 
373     @SuppressWarnings( "checkstyle:methodlength" )
374     public ArtifactResolutionResult resolve( ArtifactResolutionRequest request )
375     {
376         Artifact rootArtifact = request.getArtifact();
377         Set<Artifact> artifacts = request.getArtifactDependencies();
378         Map<String, Artifact> managedVersions = request.getManagedVersionMap();
379         List<ResolutionListener> listeners = request.getListeners();
380         ArtifactFilter collectionFilter = request.getCollectionFilter();
381         ArtifactFilter resolutionFilter = request.getResolutionFilter();
382         RepositorySystemSession session = getSession( request.getLocalRepository() );
383 
384         // TODO: hack because metadata isn't generated in m2e correctly and i want to run the maven i have in the
385         // workspace
386         if ( source == null )
387         {
388             try
389             {
390                 source = container.lookup( ArtifactMetadataSource.class );
391             }
392             catch ( ComponentLookupException e )
393             {
394                 // won't happen
395             }
396         }
397 
398         if ( listeners == null )
399         {
400             listeners = new ArrayList<>();
401 
402             if ( logger.isDebugEnabled() )
403             {
404                 listeners.add( new DebugResolutionListener( logger ) );
405             }
406 
407             listeners.add( new WarningResolutionListener( logger ) );
408         }
409 
410         ArtifactResolutionResult result = new ArtifactResolutionResult();
411 
412         // The root artifact may, or may not be resolved so we need to check before we attempt to resolve.
413         // This is often an artifact like a POM that is taken from disk and we already have hold of the
414         // file reference. But this may be a Maven Plugin that we need to resolve from a remote repository
415         // as well as its dependencies.
416 
417         if ( request.isResolveRoot() /* && rootArtifact.getFile() == null */ )
418         {
419             try
420             {
421                 resolve( rootArtifact, request.getRemoteRepositories(), session );
422             }
423             catch ( ArtifactResolutionException e )
424             {
425                 result.addErrorArtifactException( e );
426                 return result;
427             }
428             catch ( ArtifactNotFoundException e )
429             {
430                 result.addMissingArtifact( request.getArtifact() );
431                 return result;
432             }
433         }
434 
435         ArtifactResolutionRequest collectionRequest = request;
436 
437         if ( request.isResolveTransitively() )
438         {
439             MetadataResolutionRequest metadataRequest = new DefaultMetadataResolutionRequest( request );
440 
441             metadataRequest.setArtifact( rootArtifact );
442             metadataRequest.setResolveManagedVersions( managedVersions == null );
443 
444             try
445             {
446                 ResolutionGroup resolutionGroup = source.retrieve( metadataRequest );
447 
448                 if ( managedVersions == null )
449                 {
450                     managedVersions = resolutionGroup.getManagedVersions();
451                 }
452 
453                 Set<Artifact> directArtifacts = resolutionGroup.getArtifacts();
454 
455                 if ( artifacts == null || artifacts.isEmpty() )
456                 {
457                     artifacts = directArtifacts;
458                 }
459                 else
460                 {
461                     List<Artifact> allArtifacts = new ArrayList<>();
462                     allArtifacts.addAll( artifacts );
463                     allArtifacts.addAll( directArtifacts );
464 
465                     Map<String, Artifact> mergedArtifacts = new LinkedHashMap<>();
466                     for ( Artifact artifact : allArtifacts )
467                     {
468                         String conflictId = artifact.getDependencyConflictId();
469                         if ( !mergedArtifacts.containsKey( conflictId ) )
470                         {
471                             mergedArtifacts.put( conflictId, artifact );
472                         }
473                     }
474 
475                     artifacts = new LinkedHashSet<>( mergedArtifacts.values() );
476                 }
477 
478                 collectionRequest = new ArtifactResolutionRequest( request );
479                 collectionRequest.setServers( request.getServers() );
480                 collectionRequest.setMirrors( request.getMirrors() );
481                 collectionRequest.setProxies( request.getProxies() );
482                 collectionRequest.setRemoteRepositories( resolutionGroup.getResolutionRepositories() );
483             }
484             catch ( ArtifactMetadataRetrievalException e )
485             {
486                 ArtifactResolutionException are =
487                     new ArtifactResolutionException( "Unable to get dependency information for " + rootArtifact.getId()
488                         + ": " + e.getMessage(), rootArtifact, metadataRequest.getRemoteRepositories(), e );
489                 result.addMetadataResolutionException( are );
490                 return result;
491             }
492         }
493 
494         if ( artifacts == null || artifacts.isEmpty() )
495         {
496             if ( request.isResolveRoot() )
497             {
498                 result.addArtifact( rootArtifact );
499             }
500             return result;
501         }
502 
503         // After the collection we will have the artifact object in the result but they will not be resolved yet.
504         result = artifactCollector.collect( artifacts, rootArtifact, managedVersions, collectionRequest, source,
505                                             collectionFilter, listeners, null );
506 
507         // We have metadata retrieval problems, or there are cycles that have been detected
508         // so we give this back to the calling code and let them deal with this information
509         // appropriately.
510 
511         if ( result.hasMetadataResolutionExceptions() || result.hasVersionRangeViolations()
512             || result.hasCircularDependencyExceptions() )
513         {
514             logger.info( "Failure detected." );
515             return result;
516         }
517 
518         if ( result.getArtifactResolutionNodes() != null )
519         {
520             ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
521 
522             CountDownLatch latch = new CountDownLatch( result.getArtifactResolutionNodes().size() );
523 
524             for ( ResolutionNode node : result.getArtifactResolutionNodes() )
525             {
526                 Artifact artifact = node.getArtifact();
527 
528                 if ( resolutionFilter == null || resolutionFilter.include( artifact ) )
529                 {
530                     executor.execute( new ResolveTask( classLoader, latch, artifact, session,
531                                                        node.getRemoteRepositories(), result ) );
532                 }
533                 else
534                 {
535                     latch.countDown();
536                 }
537             }
538             try
539             {
540                 latch.await();
541             }
542             catch ( InterruptedException e )
543             {
544                 result.addErrorArtifactException( new ArtifactResolutionException( "Resolution interrupted",
545                                                                                    rootArtifact, e ) );
546             }
547         }
548 
549         // We want to send the root artifact back in the result but we need to do this after the other dependencies
550         // have been resolved.
551         if ( request.isResolveRoot() )
552         {
553             // Add the root artifact (as the first artifact to retain logical order of class path!)
554             Set<Artifact> allArtifacts = new LinkedHashSet<>();
555             allArtifacts.add( rootArtifact );
556             allArtifacts.addAll( result.getArtifacts() );
557             result.setArtifacts( allArtifacts );
558         }
559 
560         return result;
561     }
562 
563     public void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories,
564                          ArtifactRepository localRepository )
565                              throws ArtifactResolutionException, ArtifactNotFoundException
566     {
567         resolve( artifact, remoteRepositories, localRepository, null );
568     }
569 
570     /**
571      * ThreadCreator for creating daemon threads with fixed ThreadGroup-name.
572      */
573     static final class DaemonThreadCreator
574         implements ThreadFactory
575     {
576         static final String THREADGROUP_NAME = "org.apache.maven.artifact.resolver.DefaultArtifactResolver";
577 
578         static final ThreadGroup GROUP = new ThreadGroup( THREADGROUP_NAME );
579 
580         static final AtomicInteger THREAD_NUMBER = new AtomicInteger( 1 );
581 
582         public Thread newThread( Runnable r )
583         {
584             Thread newThread = new Thread( GROUP, r, "resolver-" + THREAD_NUMBER.getAndIncrement() );
585             newThread.setDaemon( true );
586             newThread.setContextClassLoader( null );
587             return newThread;
588         }
589     }
590 
591     private class ResolveTask
592         implements Runnable
593     {
594 
595         private final ClassLoader classLoader;
596 
597         private final CountDownLatch latch;
598 
599         private final Artifact artifact;
600 
601         private final RepositorySystemSession session;
602 
603         private final List<ArtifactRepository> remoteRepositories;
604 
605         private final ArtifactResolutionResult result;
606 
607         ResolveTask( ClassLoader classLoader, CountDownLatch latch, Artifact artifact,
608                             RepositorySystemSession session, List<ArtifactRepository> remoteRepositories,
609                             ArtifactResolutionResult result )
610         {
611             this.classLoader = classLoader;
612             this.latch = latch;
613             this.artifact = artifact;
614             this.session = session;
615             this.remoteRepositories = remoteRepositories;
616             this.result = result;
617         }
618 
619         public void run()
620         {
621             ClassLoader old = Thread.currentThread().getContextClassLoader();
622             try
623             {
624                 Thread.currentThread().setContextClassLoader( classLoader );
625                 resolve( artifact, remoteRepositories, session );
626             }
627             catch ( ArtifactNotFoundException anfe )
628             {
629                 // These are cases where the artifact just isn't present in any of the remote repositories
630                 // because it wasn't deployed, or it was deployed in the wrong place.
631 
632                 synchronized ( result )
633                 {
634                     result.addMissingArtifact( artifact );
635                 }
636             }
637             catch ( ArtifactResolutionException e )
638             {
639                 // This is really a wagon TransferFailedException so something went wrong after we successfully
640                 // retrieved the metadata.
641 
642                 synchronized ( result )
643                 {
644                     result.addErrorArtifactException( e );
645                 }
646             }
647             finally
648             {
649                 latch.countDown();
650                 Thread.currentThread().setContextClassLoader( old );
651 
652             }
653         }
654 
655     }
656 
657     @Override
658     public void dispose()
659     {
660         if ( executor instanceof ExecutorService )
661         {
662             ( (ExecutorService) executor ).shutdownNow();
663         }
664     }
665 
666 }