View Javadoc

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