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