001 package org.apache.maven.artifact.resolver; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license 005 * agreements. See the NOTICE file distributed with this work for additional information regarding 006 * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance with the License. You may obtain a 008 * copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed under the License 013 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 014 * or implied. See the License for the specific language governing permissions and limitations under 015 * the License. 016 */ 017 018 import java.io.File; 019 import java.util.ArrayList; 020 import java.util.Collections; 021 import java.util.LinkedHashMap; 022 import java.util.LinkedHashSet; 023 import java.util.List; 024 import java.util.Map; 025 import java.util.Set; 026 import java.util.concurrent.CountDownLatch; 027 import java.util.concurrent.Executor; 028 import java.util.concurrent.ExecutorService; 029 import java.util.concurrent.LinkedBlockingQueue; 030 import java.util.concurrent.ThreadFactory; 031 import java.util.concurrent.ThreadPoolExecutor; 032 import java.util.concurrent.TimeUnit; 033 import java.util.concurrent.atomic.AtomicInteger; 034 import java.util.regex.Matcher; 035 036 import org.apache.maven.RepositoryUtils; 037 import org.apache.maven.artifact.Artifact; 038 import org.apache.maven.artifact.factory.ArtifactFactory; 039 import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException; 040 import org.apache.maven.artifact.metadata.ArtifactMetadataSource; 041 import org.apache.maven.artifact.metadata.ResolutionGroup; 042 import org.apache.maven.artifact.repository.ArtifactRepository; 043 import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager; 044 import org.apache.maven.artifact.repository.RepositoryRequest; 045 import org.apache.maven.artifact.repository.metadata.Snapshot; 046 import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata; 047 import org.apache.maven.artifact.resolver.filter.ArtifactFilter; 048 import org.apache.maven.execution.MavenSession; 049 import org.apache.maven.plugin.LegacySupport; 050 import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest; 051 import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; 052 import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver; 053 import org.apache.maven.wagon.events.TransferListener; 054 import org.codehaus.plexus.PlexusContainer; 055 import org.codehaus.plexus.component.annotations.Component; 056 import org.codehaus.plexus.component.annotations.Requirement; 057 import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 058 import org.codehaus.plexus.logging.Logger; 059 import org.sonatype.aether.RepositorySystem; 060 import org.sonatype.aether.RepositorySystemSession; 061 import org.sonatype.aether.repository.LocalRepositoryManager; 062 import org.sonatype.aether.resolution.ArtifactRequest; 063 import org.sonatype.aether.resolution.ArtifactResult; 064 065 /** 066 * @author Jason van Zyl 067 */ 068 @Component(role = ArtifactResolver.class) 069 public class DefaultArtifactResolver 070 implements ArtifactResolver 071 { 072 @Requirement 073 private Logger logger; 074 075 @Requirement 076 protected ArtifactFactory artifactFactory; 077 078 @Requirement 079 private ArtifactCollector artifactCollector; 080 081 @Requirement 082 private ResolutionErrorHandler resolutionErrorHandler; 083 084 @Requirement 085 private ArtifactMetadataSource source; 086 087 @Requirement 088 private PlexusContainer container; 089 090 @Requirement 091 private LegacySupport legacySupport; 092 093 @Requirement 094 private RepositorySystem repoSystem; 095 096 private final Executor executor; 097 098 public DefaultArtifactResolver() 099 { 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 }