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