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