001 package org.apache.maven.artifact.resolver;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.io.File;
023 import java.util.ArrayList;
024 import java.util.Collections;
025 import java.util.LinkedHashMap;
026 import java.util.LinkedHashSet;
027 import java.util.List;
028 import java.util.Map;
029 import java.util.Set;
030 import java.util.concurrent.CountDownLatch;
031 import java.util.concurrent.Executor;
032 import java.util.concurrent.ExecutorService;
033 import java.util.concurrent.LinkedBlockingQueue;
034 import java.util.concurrent.ThreadFactory;
035 import java.util.concurrent.ThreadPoolExecutor;
036 import java.util.concurrent.TimeUnit;
037 import java.util.concurrent.atomic.AtomicInteger;
038 import java.util.regex.Matcher;
039
040 import org.apache.maven.RepositoryUtils;
041 import org.apache.maven.artifact.Artifact;
042 import org.apache.maven.artifact.factory.ArtifactFactory;
043 import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
044 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
045 import org.apache.maven.artifact.metadata.ResolutionGroup;
046 import org.apache.maven.artifact.repository.ArtifactRepository;
047 import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager;
048 import org.apache.maven.artifact.repository.RepositoryRequest;
049 import org.apache.maven.artifact.repository.metadata.Snapshot;
050 import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata;
051 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
052 import org.apache.maven.execution.MavenSession;
053 import org.apache.maven.plugin.LegacySupport;
054 import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest;
055 import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest;
056 import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver;
057 import org.apache.maven.wagon.events.TransferListener;
058 import org.codehaus.plexus.PlexusContainer;
059 import org.codehaus.plexus.component.annotations.Component;
060 import org.codehaus.plexus.component.annotations.Requirement;
061 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
062 import org.codehaus.plexus.logging.Logger;
063 import org.eclipse.aether.RepositorySystem;
064 import org.eclipse.aether.RepositorySystemSession;
065 import org.eclipse.aether.repository.LocalRepositoryManager;
066 import org.eclipse.aether.resolution.ArtifactRequest;
067 import org.eclipse.aether.resolution.ArtifactResult;
068
069 /**
070 * @author Jason van Zyl
071 */
072 @Component( role = ArtifactResolver.class )
073 public class DefaultArtifactResolver
074 implements ArtifactResolver
075 {
076 @Requirement
077 private Logger logger;
078
079 @Requirement
080 protected ArtifactFactory artifactFactory;
081
082 @Requirement
083 private ArtifactCollector artifactCollector;
084
085 @Requirement
086 private ResolutionErrorHandler resolutionErrorHandler;
087
088 @Requirement
089 private ArtifactMetadataSource source;
090
091 @Requirement
092 private PlexusContainer container;
093
094 @Requirement
095 private LegacySupport legacySupport;
096
097 @Requirement
098 private RepositorySystem repoSystem;
099
100 private final Executor executor;
101
102 public DefaultArtifactResolver()
103 {
104 int threads = Integer.getInteger( "maven.artifact.threads", 5 );
105 if ( threads <= 1 )
106 {
107 executor = new Executor()
108 {
109 public void execute( Runnable command )
110 {
111 command.run();
112 }
113 };
114 }
115 else
116 {
117 executor =
118 new ThreadPoolExecutor( threads, threads, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
119 new DaemonThreadCreator() );
120 }
121 }
122
123 @Override
124 protected void finalize()
125 throws Throwable
126 {
127 if ( executor instanceof ExecutorService )
128 {
129 ( (ExecutorService) executor ).shutdown();
130 }
131 }
132
133 private RepositorySystemSession getSession( ArtifactRepository localRepository )
134 {
135 return LegacyLocalRepositoryManager.overlay( localRepository, legacySupport.getRepositorySession(), repoSystem );
136 }
137
138 private void injectSession1( RepositoryRequest request, MavenSession session )
139 {
140 if ( session != null )
141 {
142 request.setOffline( session.isOffline() );
143 request.setForceUpdate( session.getRequest().isUpdateSnapshots() );
144 }
145 }
146
147 private void injectSession2( ArtifactResolutionRequest request, MavenSession session )
148 {
149 injectSession1( request, session );
150
151 if ( session != null )
152 {
153 request.setServers( session.getRequest().getServers() );
154 request.setMirrors( session.getRequest().getMirrors() );
155 request.setProxies( session.getRequest().getProxies() );
156 }
157 }
158
159 public void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories,
160 ArtifactRepository localRepository, TransferListener resolutionListener )
161 throws ArtifactResolutionException, ArtifactNotFoundException
162 {
163 resolve( artifact, remoteRepositories, getSession( localRepository ) );
164 }
165
166 public void resolveAlways( Artifact artifact, List<ArtifactRepository> remoteRepositories,
167 ArtifactRepository localRepository )
168 throws ArtifactResolutionException, ArtifactNotFoundException
169 {
170 resolve( artifact, remoteRepositories, getSession( localRepository ) );
171 }
172
173 private void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories,
174 RepositorySystemSession session )
175 throws ArtifactResolutionException, ArtifactNotFoundException
176 {
177 if ( artifact == null )
178 {
179 return;
180 }
181
182 if ( Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) )
183 {
184 File systemFile = artifact.getFile();
185
186 if ( systemFile == null )
187 {
188 throw new ArtifactNotFoundException( "System artifact: " + artifact + " has no file attached", artifact );
189 }
190
191 if ( !systemFile.exists() )
192 {
193 throw new ArtifactNotFoundException( "System artifact: " + artifact + " not found in path: " + systemFile, artifact );
194 }
195
196 if ( !systemFile.isFile() )
197 {
198 throw new ArtifactNotFoundException( "System artifact: " + artifact + " is not a file: " + systemFile, artifact );
199 }
200
201 artifact.setResolved( true );
202
203 return;
204 }
205
206 if ( !artifact.isResolved() )
207 {
208 ArtifactResult result;
209
210 try
211 {
212 ArtifactRequest artifactRequest = new ArtifactRequest();
213 artifactRequest.setArtifact( RepositoryUtils.toArtifact( artifact ) );
214 artifactRequest.setRepositories( RepositoryUtils.toRepos( remoteRepositories ) );
215
216 // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
217 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
218 String path = lrm.getPathForLocalArtifact( artifactRequest.getArtifact() );
219 artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) );
220
221 result = repoSystem.resolveArtifact( session, artifactRequest );
222 }
223 catch ( org.eclipse.aether.resolution.ArtifactResolutionException e )
224 {
225 if ( e.getCause() instanceof org.eclipse.aether.transfer.ArtifactNotFoundException )
226 {
227 throw new ArtifactNotFoundException( e.getMessage(), artifact, remoteRepositories, e );
228 }
229 else
230 {
231 throw new ArtifactResolutionException( e.getMessage(), artifact, remoteRepositories, e );
232 }
233 }
234
235 artifact.selectVersion( result.getArtifact().getVersion() );
236 artifact.setFile( result.getArtifact().getFile() );
237 artifact.setResolved( true );
238
239 if ( artifact.isSnapshot() )
240 {
241 Matcher matcher = Artifact.VERSION_FILE_PATTERN.matcher( artifact.getVersion() );
242 if ( matcher.matches() )
243 {
244 Snapshot snapshot = new Snapshot();
245 snapshot.setTimestamp( matcher.group( 2 ) );
246 try
247 {
248 snapshot.setBuildNumber( Integer.parseInt( matcher.group( 3 ) ) );
249 artifact.addMetadata( new SnapshotArtifactRepositoryMetadata( artifact, snapshot ) );
250 }
251 catch ( NumberFormatException e )
252 {
253 logger.warn( "Invalid artifact version " + artifact.getVersion() + ": " + e.getMessage() );
254 }
255 }
256 }
257 }
258 }
259
260 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
261 ArtifactRepository localRepository,
262 List<ArtifactRepository> remoteRepositories,
263 ArtifactMetadataSource source, ArtifactFilter filter )
264 throws ArtifactResolutionException, ArtifactNotFoundException
265 {
266 return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository,
267 remoteRepositories, source, filter );
268
269 }
270
271 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
272 Map managedVersions, ArtifactRepository localRepository,
273 List<ArtifactRepository> remoteRepositories,
274 ArtifactMetadataSource source )
275 throws ArtifactResolutionException, ArtifactNotFoundException
276 {
277 return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
278 remoteRepositories, source, null );
279 }
280
281 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
282 Map managedVersions, ArtifactRepository localRepository,
283 List<ArtifactRepository> remoteRepositories,
284 ArtifactMetadataSource source, ArtifactFilter filter )
285 throws ArtifactResolutionException, ArtifactNotFoundException
286 {
287 return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
288 remoteRepositories, source, filter, null );
289 }
290
291 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
292 List<ArtifactRepository> remoteRepositories,
293 ArtifactRepository localRepository,
294 ArtifactMetadataSource source )
295 throws ArtifactResolutionException, 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, ArtifactNotFoundException
306 {
307 return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository,
308 remoteRepositories, source, null, listeners );
309 }
310
311 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
312 Map managedVersions, ArtifactRepository localRepository,
313 List<ArtifactRepository> remoteRepositories,
314 ArtifactMetadataSource source, ArtifactFilter filter,
315 List<ResolutionListener> listeners )
316 throws ArtifactResolutionException, ArtifactNotFoundException
317 {
318 return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository,
319 remoteRepositories, source, filter, listeners, null );
320 }
321
322 public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact,
323 Map managedVersions, ArtifactRepository localRepository,
324 List<ArtifactRepository> remoteRepositories,
325 ArtifactMetadataSource source, ArtifactFilter filter,
326 List<ResolutionListener> listeners,
327 List<ConflictResolver> conflictResolvers )
328 throws ArtifactResolutionException, ArtifactNotFoundException
329 {
330 ArtifactResolutionRequest request = new ArtifactResolutionRequest()
331 .setArtifact( originatingArtifact )
332 .setResolveRoot( false )
333 // This is required by the surefire plugin
334 .setArtifactDependencies( artifacts )
335 .setManagedVersionMap( managedVersions )
336 .setLocalRepository( localRepository )
337 .setRemoteRepositories( remoteRepositories )
338 .setCollectionFilter( filter )
339 .setListeners( listeners );
340
341 injectSession2( request, legacySupport.getSession() );
342
343 return resolveWithExceptions( request );
344 }
345
346 public ArtifactResolutionResult resolveWithExceptions( ArtifactResolutionRequest request )
347 throws ArtifactResolutionException, ArtifactNotFoundException
348 {
349 ArtifactResolutionResult result = resolve( request );
350
351 // We have collected all the problems so let's mimic the way the old code worked and just blow up right here.
352 // That's right lets just let it rip right here and send a big incomprehensible blob of text at unsuspecting
353 // users. Bad dog!
354
355 resolutionErrorHandler.throwErrors( request, result );
356
357 return result;
358 }
359
360 // ------------------------------------------------------------------------
361 //
362 // ------------------------------------------------------------------------
363
364 public ArtifactResolutionResult resolve( ArtifactResolutionRequest request )
365 {
366 Artifact rootArtifact = request.getArtifact();
367 Set<Artifact> artifacts = request.getArtifactDependencies();
368 Map<String, Artifact> managedVersions = request.getManagedVersionMap();
369 List<ResolutionListener> listeners = request.getListeners();
370 ArtifactFilter collectionFilter = request.getCollectionFilter();
371 ArtifactFilter resolutionFilter = request.getResolutionFilter();
372 RepositorySystemSession session = getSession( request.getLocalRepository() );
373
374 //TODO: hack because metadata isn't generated in m2e correctly and i want to run the maven i have in the 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<ResolutionListener>();
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<Artifact>();
451 allArtifacts.addAll( artifacts );
452 allArtifacts.addAll( directArtifacts );
453
454 Map<String, Artifact> mergedArtifacts = new LinkedHashMap<String, Artifact>();
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<Artifact>( 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 =
494 artifactCollector.collect( artifacts, rootArtifact, managedVersions, collectionRequest, source,
495 collectionFilter, listeners, null );
496
497 // We have metadata retrieval problems, or there are cycles that have been detected
498 // so we give this back to the calling code and let them deal with this information
499 // appropriately.
500
501 if ( result.hasMetadataResolutionExceptions() || result.hasVersionRangeViolations() || 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<Artifact>();
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, ArtifactRepository localRepository )
552 throws ArtifactResolutionException, ArtifactNotFoundException
553 {
554 resolve( artifact, remoteRepositories, localRepository, null );
555 }
556
557 /**
558 * ThreadCreator for creating daemon threads with fixed ThreadGroup-name.
559 */
560 static final class DaemonThreadCreator
561 implements ThreadFactory
562 {
563 static final String THREADGROUP_NAME = "org.apache.maven.artifact.resolver.DefaultArtifactResolver";
564
565 static final ThreadGroup GROUP = new ThreadGroup( THREADGROUP_NAME );
566
567 static final AtomicInteger THREAD_NUMBER = new AtomicInteger( 1 );
568
569 public Thread newThread( Runnable r )
570 {
571 Thread newThread = new Thread( GROUP, r, "resolver-" + THREAD_NUMBER.getAndIncrement() );
572 newThread.setDaemon( true );
573 return newThread;
574 }
575 }
576
577 private class ResolveTask
578 implements Runnable
579 {
580
581 private final ClassLoader classLoader;
582
583 private final CountDownLatch latch;
584
585 private final Artifact artifact;
586
587 private final RepositorySystemSession session;
588
589 private final List<ArtifactRepository> remoteRepositories;
590
591 private final ArtifactResolutionResult result;
592
593 public ResolveTask( ClassLoader classLoader, CountDownLatch latch, Artifact artifact, RepositorySystemSession session,
594 List<ArtifactRepository> remoteRepositories, ArtifactResolutionResult result )
595 {
596 this.classLoader = classLoader;
597 this.latch = latch;
598 this.artifact = artifact;
599 this.session = session;
600 this.remoteRepositories = remoteRepositories;
601 this.result = result;
602 }
603
604 public void run()
605 {
606 ClassLoader old = Thread.currentThread().getContextClassLoader();
607 try
608 {
609 Thread.currentThread().setContextClassLoader( classLoader );
610 resolve( artifact, remoteRepositories, session );
611 }
612 catch ( ArtifactNotFoundException anfe )
613 {
614 // These are cases where the artifact just isn't present in any of the remote repositories
615 // because it wasn't deployed, or it was deployed in the wrong place.
616
617 synchronized ( result )
618 {
619 result.addMissingArtifact( artifact );
620 }
621 }
622 catch ( ArtifactResolutionException e )
623 {
624 // This is really a wagon TransferFailedException so something went wrong after we successfully
625 // retrieved the metadata.
626
627 synchronized ( result )
628 {
629 result.addErrorArtifactException( e );
630 }
631 }
632 finally
633 {
634 latch.countDown();
635 Thread.currentThread().setContextClassLoader( old );
636
637 }
638 }
639
640 }
641
642 }