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.io.IOException;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.Date;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import org.apache.maven.artifact.Artifact;
34  import org.apache.maven.artifact.factory.ArtifactFactory;
35  import org.apache.maven.artifact.manager.WagonManager;
36  import org.apache.maven.artifact.metadata.ArtifactMetadata;
37  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
38  import org.apache.maven.artifact.repository.ArtifactRepository;
39  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
40  import org.apache.maven.artifact.repository.metadata.Metadata;
41  import org.apache.maven.artifact.repository.metadata.Snapshot;
42  import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata;
43  import org.apache.maven.artifact.repository.metadata.Versioning;
44  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
45  import org.apache.maven.artifact.transform.ArtifactTransformationManager;
46  import org.apache.maven.wagon.ResourceDoesNotExistException;
47  import org.apache.maven.wagon.TransferFailedException;
48  import org.codehaus.plexus.logging.AbstractLogEnabled;
49  import org.codehaus.plexus.util.FileUtils;
50  
51  import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch;
52  import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue;
53  import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor;
54  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
55  
56  public class DefaultArtifactResolver
57      extends AbstractLogEnabled
58      implements ArtifactResolver
59  {
60      // ----------------------------------------------------------------------
61      // Components
62      // ----------------------------------------------------------------------
63  
64      private static final int DEFAULT_POOL_SIZE = 5;
65  
66      private WagonManager wagonManager;
67  
68      private ArtifactTransformationManager transformationManager;
69  
70      protected ArtifactFactory artifactFactory;
71  
72      private ArtifactCollector artifactCollector;
73      private final ThreadPoolExecutor resolveArtifactPool;
74  
75      public DefaultArtifactResolver()
76      {
77          super();
78          resolveArtifactPool = 
79              new ThreadPoolExecutor( DEFAULT_POOL_SIZE, DEFAULT_POOL_SIZE, 3, TimeUnit.SECONDS,
80                                      new LinkedBlockingQueue() );
81      }
82  
83      // ----------------------------------------------------------------------
84      // Implementation
85      // ----------------------------------------------------------------------
86  
87      public void resolve( Artifact artifact, List remoteRepositories, ArtifactRepository localRepository )
88          throws ArtifactResolutionException, ArtifactNotFoundException
89      {
90          resolve( artifact, remoteRepositories, localRepository, false );
91      }
92  
93      public void resolveAlways( Artifact artifact, List remoteRepositories, ArtifactRepository localRepository )
94          throws ArtifactResolutionException, ArtifactNotFoundException
95      {
96          resolve( artifact, remoteRepositories, localRepository, true );
97      }
98  
99      private void resolve( Artifact artifact, List remoteRepositories, ArtifactRepository localRepository,
100                           boolean force )
101         throws ArtifactResolutionException, ArtifactNotFoundException
102     {
103         if ( artifact == null )
104         {
105             return;
106         }
107 
108         if ( Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) )
109         {
110             File systemFile = artifact.getFile();
111 
112             if ( systemFile == null )
113             {
114                 throw new ArtifactNotFoundException(
115                     "System artifact: " + artifact + " has no file attached", artifact );
116             }
117 
118             if ( !systemFile.isFile() )
119             {
120                 throw new ArtifactNotFoundException( "System artifact: " + artifact
121                     + " is not a file: " + systemFile, artifact );
122             }
123 
124             if ( !systemFile.exists() )
125             {
126                 throw new ArtifactNotFoundException(
127                     "System artifact: " + artifact + " not found in path: " + systemFile,
128                     artifact );
129             }
130 
131             artifact.setResolved( true );
132         }
133         else if ( !artifact.isResolved() )
134         {
135             // ----------------------------------------------------------------------
136             // Check for the existence of the artifact in the specified local
137             // ArtifactRepository. If it is present then simply return as the
138             // request for resolution has been satisfied.
139             // ----------------------------------------------------------------------
140 
141             String localPath = localRepository.pathOf( artifact );
142 
143             artifact.setFile( new File( localRepository.getBasedir(), localPath ) );
144 
145             transformationManager.transformForResolve( artifact, remoteRepositories, localRepository );
146 
147             boolean localCopy = false;
148             for ( Iterator i = artifact.getMetadataList().iterator(); i.hasNext(); )
149             {
150                 ArtifactMetadata m = (ArtifactMetadata) i.next();
151                 if ( m instanceof SnapshotArtifactRepositoryMetadata )
152                 {
153                     SnapshotArtifactRepositoryMetadata snapshotMetadata = (SnapshotArtifactRepositoryMetadata) m;
154 
155                     Metadata metadata = snapshotMetadata.getMetadata();
156                     if ( metadata != null )
157                     {
158                         Versioning versioning = metadata.getVersioning();
159                         if ( versioning != null )
160                         {
161                             Snapshot snapshot = versioning.getSnapshot();
162                             if ( snapshot != null )
163                             {
164                                 localCopy = snapshot.isLocalCopy();
165                             }
166                         }
167                     }
168                 }
169             }
170 
171             File destination = artifact.getFile();
172             List repositories = remoteRepositories;
173 
174             // TODO: would prefer the snapshot transformation took care of this. Maybe we need a "shouldresolve" flag.
175             if ( artifact.isSnapshot() && artifact.getBaseVersion().equals( artifact.getVersion() ) &&
176                 destination.exists() && !localCopy && wagonManager.isOnline() )
177             {
178                 Date comparisonDate = new Date( destination.lastModified() );
179 
180                 // cull to list of repositories that would like an update
181                 repositories = new ArrayList( remoteRepositories );
182                 for ( Iterator i = repositories.iterator(); i.hasNext(); )
183                 {
184                     ArtifactRepository repository = (ArtifactRepository) i.next();
185                     ArtifactRepositoryPolicy policy = repository.getSnapshots();
186                     if ( !policy.isEnabled() || !policy.checkOutOfDate( comparisonDate ) )
187                     {
188                         i.remove();
189                     }
190                 }
191 
192                 if ( !repositories.isEmpty() )
193                 {
194                     // someone wants to check for updates
195                     force = true;
196                 }
197             }
198 
199             if ( !destination.exists() || force )
200             {
201                 if ( !wagonManager.isOnline() )
202                 {
203                     throw new ArtifactNotFoundException( "System is offline.", artifact );
204                 }
205 
206                 try
207                 {
208                     // TODO: force should be passed to the wagon manager
209                     if ( artifact.getRepository() != null )
210                     {
211                         // the transformations discovered the artifact - so use it exclusively
212                         wagonManager.getArtifact( artifact, artifact.getRepository() );
213                     }
214                     else
215                     {
216                         wagonManager.getArtifact( artifact, repositories );
217                     }
218 
219                     if ( !artifact.isResolved() && !destination.exists() )
220                     {
221                         throw new ArtifactResolutionException(
222                             "Failed to resolve artifact, possibly due to a repository list that is not appropriately equipped for this artifact's metadata.",
223                             artifact, getMirroredRepositories( remoteRepositories ) );
224                     }
225                 }
226                 catch ( ResourceDoesNotExistException e )
227                 {
228                     throw new ArtifactNotFoundException( e.getMessage(), artifact,
229                                                          getMirroredRepositories( remoteRepositories ), e );
230                 }
231                 catch ( TransferFailedException e )
232                 {
233                     throw new ArtifactResolutionException( e.getMessage(), artifact,
234                                                            getMirroredRepositories( remoteRepositories ), e );
235                 }
236             }
237             else if ( destination.exists() )
238             {
239                 // locally resolved...no need to hit the remote repo.
240                 artifact.setResolved( true );
241             }
242 
243             if ( artifact.isSnapshot() && !artifact.getBaseVersion().equals( artifact.getVersion() ) )
244             {
245                 String version = artifact.getVersion();
246                 artifact.selectVersion( artifact.getBaseVersion() );
247                 File copy = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) );
248                 if ( !copy.exists() || copy.lastModified() != destination.lastModified()
249                     || copy.length() != destination.length() )
250                 {
251                     // recopy file if it was reresolved, or doesn't exist.
252                     try
253                     {
254                         FileUtils.copyFile( destination, copy );
255                         copy.setLastModified( destination.lastModified() );
256                     }
257                     catch ( IOException e )
258                     {
259                         throw new ArtifactResolutionException(
260                             "Unable to copy resolved artifact for local use: " + e.getMessage(), artifact,
261                             getMirroredRepositories( remoteRepositories ), e );
262                     }
263                 }
264                 artifact.setFile( copy );
265                 artifact.selectVersion( version );
266             }
267         }
268     }
269 
270     public ArtifactResolutionResult resolveTransitively( Set artifacts, Artifact originatingArtifact,
271                                                          ArtifactRepository localRepository, List remoteRepositories,
272                                                          ArtifactMetadataSource source, ArtifactFilter filter )
273         throws ArtifactResolutionException, ArtifactNotFoundException
274     {
275         return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository,
276                                     remoteRepositories, source, filter );
277 
278     }
279 
280     public ArtifactResolutionResult resolveTransitively( Set artifacts, Artifact originatingArtifact,
281                                                          Map managedVersions, ArtifactRepository localRepository,
282                                                          List remoteRepositories, ArtifactMetadataSource source )
283         throws ArtifactResolutionException, ArtifactNotFoundException
284     {
285         return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
286                                     remoteRepositories, source, null );
287     }
288 
289     public ArtifactResolutionResult resolveTransitively( Set artifacts, Artifact originatingArtifact,
290                                                          Map managedVersions, ArtifactRepository localRepository,
291                                                          List remoteRepositories, ArtifactMetadataSource source,
292                                                          ArtifactFilter filter )
293         throws ArtifactResolutionException, ArtifactNotFoundException
294     {
295         // TODO: this is simplistic
296         List listeners = new ArrayList();
297         if ( getLogger().isDebugEnabled() )
298         {
299             listeners.add( new DebugResolutionListener( getLogger() ) );
300         }
301 
302         listeners.add( new WarningResolutionListener( getLogger() ) );
303 
304         return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
305                                     remoteRepositories, source, filter, listeners );
306 
307     }
308 
309     public ArtifactResolutionResult resolveTransitively( Set artifacts, Artifact originatingArtifact,
310                                                          Map managedVersions, ArtifactRepository localRepository,
311                                                          List remoteRepositories, ArtifactMetadataSource source,
312                                                          ArtifactFilter filter, List listeners )
313         throws ArtifactResolutionException, ArtifactNotFoundException
314     {
315         ArtifactResolutionResult artifactResolutionResult;
316         artifactResolutionResult =
317             artifactCollector.collect( artifacts, originatingArtifact, managedVersions, localRepository,
318                                        remoteRepositories, source, filter, listeners );
319 
320         List resolvedArtifacts = Collections.synchronizedList( new ArrayList() );
321         List missingArtifacts = Collections.synchronizedList( new ArrayList() );
322         CountDownLatch latch = new CountDownLatch( artifactResolutionResult.getArtifactResolutionNodes().size() );
323         Map nodesByGroupId = new HashMap();
324         for ( Iterator i = artifactResolutionResult.getArtifactResolutionNodes().iterator(); i.hasNext(); )
325         {
326             ResolutionNode node = (ResolutionNode) i.next();
327             List nodes = (List) nodesByGroupId.get( node.getArtifact().getGroupId() );
328             if ( nodes == null )
329             {
330                 nodes = new ArrayList();
331                 nodesByGroupId.put( node.getArtifact().getGroupId(), nodes );
332             }
333             nodes.add( node );
334         }
335 
336         List resolutionExceptions = Collections.synchronizedList( new ArrayList() );
337         try
338         {
339             for ( Iterator i = nodesByGroupId.values().iterator(); i.hasNext(); )
340             {
341                 List nodes = (List) i.next();
342                 resolveArtifactPool.execute( new ResolveArtifactTask( resolveArtifactPool, latch, nodes,
343                                                                       localRepository, resolvedArtifacts,
344                                                                       missingArtifacts, resolutionExceptions ) );
345             }
346             latch.await();
347         }
348         catch ( InterruptedException e )
349         {
350             throw new ArtifactResolutionException( "Resolution interrupted", originatingArtifact, e );
351         }
352 
353         if ( !resolutionExceptions.isEmpty() )
354         {
355             throw (ArtifactResolutionException) resolutionExceptions.get( 0 );
356         }
357         
358         if ( missingArtifacts.size() > 0 )
359         {
360             throw new MultipleArtifactsNotFoundException( originatingArtifact, resolvedArtifacts, missingArtifacts,
361                                                           getMirroredRepositories( remoteRepositories ) );
362         }
363 
364         return artifactResolutionResult;
365     }
366 
367     private List getMirroredRepositories( List remoteRepositories )
368     {
369         Map repos = new HashMap();
370         for ( Iterator i = remoteRepositories.iterator(); i.hasNext(); )
371         {
372             ArtifactRepository repository = (ArtifactRepository) i.next();
373             ArtifactRepository repo = wagonManager.getMirrorRepository( repository );
374             repos.put( repo.getId(), repo );
375         }
376         return new ArrayList( repos.values() );
377     }
378 
379     public ArtifactResolutionResult resolveTransitively( Set artifacts, Artifact originatingArtifact,
380                                                          List remoteRepositories, ArtifactRepository localRepository,
381                                                          ArtifactMetadataSource source )
382         throws ArtifactResolutionException, ArtifactNotFoundException
383     {
384         return resolveTransitively( artifacts, originatingArtifact, localRepository, remoteRepositories, source, null );
385     }
386 
387     public ArtifactResolutionResult resolveTransitively( Set artifacts, Artifact originatingArtifact,
388                                                          List remoteRepositories, ArtifactRepository localRepository,
389                                                          ArtifactMetadataSource source, List listeners )
390         throws ArtifactResolutionException, ArtifactNotFoundException
391     {
392         return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository,
393                                     remoteRepositories, source, null, listeners );
394     }
395 
396     private class ResolveArtifactTask
397         implements Runnable
398     {
399         private List nodes;
400 
401         private ArtifactRepository localRepository;
402 
403         private List resolvedArtifacts;
404 
405         private List missingArtifacts;
406 
407         private CountDownLatch latch;
408 
409         private ThreadPoolExecutor pool;
410 
411         private List resolutionExceptions;
412 
413         public ResolveArtifactTask( ThreadPoolExecutor pool, CountDownLatch latch, List nodes,
414                                     ArtifactRepository localRepository, List resolvedArtifacts, List missingArtifacts,
415                                     List resolutionExceptions )
416         {
417             this.nodes = nodes;
418             this.localRepository = localRepository;
419             this.resolvedArtifacts = resolvedArtifacts;
420             this.missingArtifacts = missingArtifacts;
421             this.latch = latch;
422             this.pool = pool;
423             this.resolutionExceptions = resolutionExceptions;
424         }
425 
426         public void run()
427         {
428             Iterator i = nodes.iterator();
429             ResolutionNode node = (ResolutionNode) i.next();
430             i.remove();
431             try
432             {
433                 resolveArtifact( node );
434             }
435             catch ( ArtifactResolutionException e )
436             {
437                 resolutionExceptions.add( e );
438             }
439             finally 
440             {
441                 latch.countDown();
442 
443                 if ( i.hasNext() )
444                 {
445                     pool.execute( new ResolveArtifactTask( pool, latch, nodes, localRepository, resolvedArtifacts,
446                                                            missingArtifacts, resolutionExceptions ) );
447                 }
448             }
449         }
450 
451         private void resolveArtifact( ResolutionNode node )
452             throws ArtifactResolutionException
453         {
454             try
455             {
456                 resolve( node.getArtifact(), node.getRemoteRepositories(), localRepository );
457                 resolvedArtifacts.add( node.getArtifact() );
458             }
459             catch ( ArtifactNotFoundException anfe )
460             {
461                 getLogger().debug( anfe.getMessage(), anfe );
462 
463                 missingArtifacts.add( node.getArtifact() );
464             }
465         }
466     }
467 
468     public synchronized void configureNumberOfThreads( int threads )
469     {
470         resolveArtifactPool.setCorePoolSize( threads );
471         resolveArtifactPool.setMaximumPoolSize( threads );
472     }
473 
474     void setWagonManager( WagonManager wagonManager )
475     {
476         this.wagonManager = wagonManager;
477     }
478 }