001package org.apache.maven; 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.io.IOException; 024import java.io.InputStream; 025import java.text.SimpleDateFormat; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Collection; 029import java.util.Collections; 030import java.util.Date; 031import java.util.HashMap; 032import java.util.HashSet; 033import java.util.LinkedHashMap; 034import java.util.LinkedHashSet; 035import java.util.List; 036import java.util.Map; 037import java.util.Properties; 038 039import org.apache.maven.artifact.ArtifactUtils; 040import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; 041import org.apache.maven.eventspy.internal.EventSpyDispatcher; 042import org.apache.maven.execution.DefaultMavenExecutionResult; 043import org.apache.maven.execution.ExecutionEvent; 044import org.apache.maven.execution.MavenExecutionRequest; 045import org.apache.maven.execution.MavenExecutionRequestPopulationException; 046import org.apache.maven.execution.MavenExecutionRequestPopulator; 047import org.apache.maven.execution.MavenExecutionResult; 048import org.apache.maven.execution.MavenSession; 049import org.apache.maven.execution.ProjectDependencyGraph; 050import org.apache.maven.lifecycle.internal.ExecutionEventCatapult; 051import org.apache.maven.lifecycle.internal.LifecycleStarter; 052import org.apache.maven.model.Plugin; 053import org.apache.maven.model.building.ModelProblem; 054import org.apache.maven.model.building.ModelProblemUtils; 055import org.apache.maven.model.building.ModelSource; 056import org.apache.maven.model.building.UrlModelSource; 057import org.apache.maven.plugin.LegacySupport; 058import org.apache.maven.project.MavenProject; 059import org.apache.maven.project.ProjectBuilder; 060import org.apache.maven.project.ProjectBuildingException; 061import org.apache.maven.project.ProjectBuildingRequest; 062import org.apache.maven.project.ProjectBuildingResult; 063import org.apache.maven.repository.LocalRepositoryNotAccessibleException; 064import org.apache.maven.repository.internal.MavenRepositorySystemUtils; 065import org.apache.maven.settings.Mirror; 066import org.apache.maven.settings.Proxy; 067import org.apache.maven.settings.Server; 068import org.apache.maven.settings.building.SettingsProblem; 069import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; 070import org.apache.maven.settings.crypto.SettingsDecrypter; 071import org.apache.maven.settings.crypto.SettingsDecryptionResult; 072import org.codehaus.plexus.PlexusContainer; 073import org.codehaus.plexus.component.annotations.Component; 074import org.codehaus.plexus.component.annotations.Requirement; 075import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 076import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; 077import org.codehaus.plexus.logging.Logger; 078import org.codehaus.plexus.util.IOUtil; 079import org.codehaus.plexus.util.StringUtils; 080import org.codehaus.plexus.util.dag.CycleDetectedException; 081import org.codehaus.plexus.util.xml.Xpp3Dom; 082import org.eclipse.aether.ConfigurationProperties; 083import org.eclipse.aether.DefaultRepositorySystemSession; 084import org.eclipse.aether.RepositorySystem; 085import org.eclipse.aether.RepositorySystemSession; 086import org.eclipse.aether.repository.LocalRepository; 087import org.eclipse.aether.repository.NoLocalRepositoryManagerException; 088import org.eclipse.aether.repository.RepositoryPolicy; 089import org.eclipse.aether.repository.WorkspaceReader; 090import org.eclipse.aether.resolution.ResolutionErrorPolicy; 091import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; 092import org.eclipse.aether.util.repository.AuthenticationBuilder; 093import org.eclipse.aether.util.repository.ChainedWorkspaceReader; 094import org.eclipse.aether.util.repository.DefaultAuthenticationSelector; 095import org.eclipse.aether.util.repository.DefaultMirrorSelector; 096import org.eclipse.aether.util.repository.DefaultProxySelector; 097import org.eclipse.aether.util.repository.SimpleResolutionErrorPolicy; 098 099/** 100 * @author Jason van Zyl 101 */ 102@Component( role = Maven.class ) 103public class DefaultMaven 104 implements Maven 105{ 106 107 @Requirement 108 private Logger logger; 109 110 @Requirement 111 protected ProjectBuilder projectBuilder; 112 113 @Requirement 114 private LifecycleStarter lifecycleStarter; 115 116 @Requirement 117 protected PlexusContainer container; 118 119 @Requirement 120 MavenExecutionRequestPopulator populator; 121 122 @Requirement 123 private ExecutionEventCatapult eventCatapult; 124 125 @Requirement 126 private ArtifactHandlerManager artifactHandlerManager; 127 128 @Requirement( optional = true, hint = "ide" ) 129 private WorkspaceReader workspaceRepository; 130 131 @Requirement 132 private RepositorySystem repoSystem; 133 134 @Requirement( optional = true, hint = "simple" ) 135 private LocalRepositoryManagerFactory simpleLocalRepositoryManagerFactory; 136 137 @Requirement 138 private SettingsDecrypter settingsDecrypter; 139 140 @Requirement 141 private LegacySupport legacySupport; 142 143 @Requirement 144 private EventSpyDispatcher eventSpyDispatcher; 145 146 @Requirement 147 private SessionScope sessionScope; 148 149 public MavenExecutionResult execute( MavenExecutionRequest request ) 150 { 151 MavenExecutionResult result; 152 153 try 154 { 155 result = doExecute( populator.populateDefaults( request ) ); 156 } 157 catch ( OutOfMemoryError e ) 158 { 159 result = addExceptionToResult( new DefaultMavenExecutionResult(), e ); 160 } 161 catch ( MavenExecutionRequestPopulationException e ) 162 { 163 result = addExceptionToResult( new DefaultMavenExecutionResult(), e ); 164 } 165 catch ( RuntimeException e ) 166 { 167 result = 168 addExceptionToResult( new DefaultMavenExecutionResult(), new InternalErrorException( "Internal error: " 169 + e, e ) ); 170 } 171 finally 172 { 173 legacySupport.setSession( null ); 174 } 175 176 return result; 177 } 178 179 // 180 // 1) Setup initial properties. 181 // 182 // 2) Validate local repository directory is accessible. 183 // 184 // 3) Create RepositorySystemSession. 185 // 186 // 4) Create MavenSession. 187 // 188 // 5) Execute AbstractLifecycleParticipant.afterSessionStart(session) 189 // 190 // 6) Get reactor projects looking for general POM errors 191 // 192 // 7) Create ProjectDependencyGraph using trimming which takes into account --projects and reactor mode. This ensures 193 // that the projects passed into the ReactorReader are only those specified. 194 // 195 // 8) Create ReactorReader with the getProjectMap( projects ). NOTE that getProjectMap(projects) is the code that 196 // checks for duplicate projects definitions in the build. Ideally this type of duplicate checking should be part of 197 // getting the reactor projects in 6). The duplicate checking is conflated with getProjectMap(projects). 198 // 199 // 9) Execute AbstractLifecycleParticipant.afterProjectsRead(session) 200 // 201 // 10) Create ProjectDependencyGraph without trimming (as trimming was done in 7). A new topological sort is required after 202 // the execution of 9) as the AbstractLifecycleParticipants are free to mutate the MavenProject instances, which may change 203 // dependencies which can, in turn, affect the build order. 204 // 205 // 11) Execute LifecycleStarter.start() 206 // 207 private MavenExecutionResult doExecute( MavenExecutionRequest request ) 208 { 209 if ( request.getStartTime() != null ) 210 { 211 request.getSystemProperties().put( "${build.timestamp}", 212 new SimpleDateFormat( "yyyyMMdd-hhmm" ).format( request.getStartTime() ) ); 213 } 214 215 request.setStartTime( new Date() ); 216 217 MavenExecutionResult result = new DefaultMavenExecutionResult(); 218 219 try 220 { 221 validateLocalRepository( request ); 222 } 223 catch ( LocalRepositoryNotAccessibleException e ) 224 { 225 return addExceptionToResult( result, e ); 226 } 227 228 DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) newRepositorySession( request ); 229 230 MavenSession session = new MavenSession( container, repoSession, request, result ); 231 legacySupport.setSession( session ); 232 233 try 234 { 235 for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( Collections.<MavenProject> emptyList() ) ) 236 { 237 listener.afterSessionStart( session ); 238 } 239 } 240 catch ( MavenExecutionException e ) 241 { 242 return addExceptionToResult( result, e ); 243 } 244 245 eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null ); 246 247 List<MavenProject> projects; 248 try 249 { 250 projects = getProjectsForMavenReactor( session ); 251 // 252 // Capture the full set of projects before any potential constraining is performed by --projects 253 // 254 session.setAllProjects( projects ); 255 } 256 catch ( ProjectBuildingException e ) 257 { 258 return addExceptionToResult( result, e ); 259 } 260 261 validateProjects( projects ); 262 263 // 264 // This creates the graph and trims the projects down based on the user request using something like: 265 // 266 // -pl project0,project2 eclipse:eclipse 267 // 268 ProjectDependencyGraph projectDependencyGraph = createProjectDependencyGraph( projects, request, result, true ); 269 270 session.setProjects( projectDependencyGraph.getSortedProjects() ); 271 272 if ( result.hasExceptions() ) 273 { 274 return result; 275 } 276 277 try 278 { 279 session.setProjectMap( getProjectMap( session.getProjects() ) ); 280 } 281 catch ( DuplicateProjectException e ) 282 { 283 return addExceptionToResult( result, e ); 284 } 285 286 WorkspaceReader reactorWorkspace; 287 sessionScope.enter(); 288 sessionScope.seed( MavenSession.class, session ); 289 try 290 { 291 reactorWorkspace = container.lookup( WorkspaceReader.class ); 292 } 293 catch ( ComponentLookupException e ) 294 { 295 return addExceptionToResult( result, e ); 296 } 297 298 // 299 // Desired order of precedence for local artifact repositories 300 // 301 // Reactor 302 // Workspace 303 // User Local Repository 304 // 305 repoSession.setWorkspaceReader( ChainedWorkspaceReader.newInstance( reactorWorkspace, 306 repoSession.getWorkspaceReader() ) ); 307 308 repoSession.setReadOnly(); 309 310 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); 311 try 312 { 313 for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) ) 314 { 315 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() ); 316 317 listener.afterProjectsRead( session ); 318 } 319 } 320 catch ( MavenExecutionException e ) 321 { 322 return addExceptionToResult( result, e ); 323 } 324 finally 325 { 326 Thread.currentThread().setContextClassLoader( originalClassLoader ); 327 } 328 329 // 330 // The projects need to be topologically after the participants have run their afterProjectsRead(session) 331 // because the participant is free to change the dependencies of a project which can potentially change the 332 // topological order of the projects, and therefore can potentially change the build order. 333 // 334 // Note that participants may affect the topological order of the projects but it is 335 // not expected that a participant will add or remove projects from the session. 336 // 337 projectDependencyGraph = createProjectDependencyGraph( session.getProjects(), request, result, false ); 338 339 if ( result.hasExceptions() ) 340 { 341 try 342 { 343 afterSessionEnd( projects, session ); 344 } 345 catch ( MavenExecutionException e ) 346 { 347 return addExceptionToResult( result, e ); 348 } 349 350 return result; 351 } 352 353 session.setProjects( projectDependencyGraph.getSortedProjects() ); 354 355 session.setProjectDependencyGraph( projectDependencyGraph ); 356 357 result.setTopologicallySortedProjects( session.getProjects() ); 358 359 result.setProject( session.getTopLevelProject() ); 360 361 lifecycleStarter.execute( session ); 362 363 validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() ); 364 365 if ( session.getResult().hasExceptions() ) 366 { 367 return addExceptionToResult( result, session.getResult().getExceptions().get( 0 ) ); 368 } 369 370 try 371 { 372 afterSessionEnd( projects, session ); 373 } 374 catch ( MavenExecutionException e ) 375 { 376 return addExceptionToResult( result, e ); 377 } 378 379 sessionScope.exit(); 380 381 return result; 382 } 383 384 private void afterSessionEnd( Collection<MavenProject> projects, MavenSession session ) 385 throws MavenExecutionException 386 { 387 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); 388 try 389 { 390 for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) ) 391 { 392 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() ); 393 394 listener.afterSessionEnd( session ); 395 } 396 } 397 finally 398 { 399 Thread.currentThread().setContextClassLoader( originalClassLoader ); 400 } 401 } 402 403 public RepositorySystemSession newRepositorySession( MavenExecutionRequest request ) 404 { 405 DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); 406 407 session.setCache( request.getRepositoryCache() ); 408 409 Map<Object, Object> configProps = new LinkedHashMap<Object, Object>(); 410 configProps.put( ConfigurationProperties.USER_AGENT, getUserAgent() ); 411 configProps.put( ConfigurationProperties.INTERACTIVE, request.isInteractiveMode() ); 412 configProps.putAll( request.getSystemProperties() ); 413 configProps.putAll( request.getUserProperties() ); 414 415 session.setOffline( request.isOffline() ); 416 session.setChecksumPolicy( request.getGlobalChecksumPolicy() ); 417 if ( request.isNoSnapshotUpdates() ) 418 { 419 session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_NEVER ); 420 } 421 else if ( request.isUpdateSnapshots() ) 422 { 423 session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_ALWAYS ); 424 } 425 else 426 { 427 session.setUpdatePolicy( null ); 428 } 429 430 int errorPolicy = 0; 431 errorPolicy |= request.isCacheNotFound() ? ResolutionErrorPolicy.CACHE_NOT_FOUND : 0; 432 errorPolicy |= request.isCacheTransferError() ? ResolutionErrorPolicy.CACHE_TRANSFER_ERROR : 0; 433 session.setResolutionErrorPolicy( new SimpleResolutionErrorPolicy( errorPolicy, errorPolicy 434 | ResolutionErrorPolicy.CACHE_NOT_FOUND ) ); 435 436 session.setArtifactTypeRegistry( RepositoryUtils.newArtifactTypeRegistry( artifactHandlerManager ) ); 437 438 LocalRepository localRepo = new LocalRepository( request.getLocalRepository().getBasedir() ); 439 440 if ( request.isUseLegacyLocalRepository() ) 441 { 442 logger.warn( "Disabling enhanced local repository: using legacy is strongly discouraged to ensure build reproducibility." ); 443 try 444 { 445 session.setLocalRepositoryManager( simpleLocalRepositoryManagerFactory.newInstance( session, localRepo ) ); 446 } 447 catch ( NoLocalRepositoryManagerException e ) 448 { 449 450 logger.warn( "Failed to configure legacy local repository: back to default" ); 451 session.setLocalRepositoryManager( repoSystem.newLocalRepositoryManager( session, localRepo ) ); 452 } 453 } 454 else 455 { 456 session.setLocalRepositoryManager( repoSystem.newLocalRepositoryManager( session, localRepo ) ); 457 } 458 459 if ( request.getWorkspaceReader() != null ) 460 { 461 session.setWorkspaceReader( request.getWorkspaceReader() ); 462 } 463 else 464 { 465 session.setWorkspaceReader( workspaceRepository ); 466 } 467 468 DefaultSettingsDecryptionRequest decrypt = new DefaultSettingsDecryptionRequest(); 469 decrypt.setProxies( request.getProxies() ); 470 decrypt.setServers( request.getServers() ); 471 SettingsDecryptionResult decrypted = settingsDecrypter.decrypt( decrypt ); 472 473 if ( logger.isDebugEnabled() ) 474 { 475 for ( SettingsProblem problem : decrypted.getProblems() ) 476 { 477 logger.debug( problem.getMessage(), problem.getException() ); 478 } 479 } 480 481 DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector(); 482 for ( Mirror mirror : request.getMirrors() ) 483 { 484 mirrorSelector.add( mirror.getId(), mirror.getUrl(), mirror.getLayout(), false, mirror.getMirrorOf(), 485 mirror.getMirrorOfLayouts() ); 486 } 487 session.setMirrorSelector( mirrorSelector ); 488 489 DefaultProxySelector proxySelector = new DefaultProxySelector(); 490 for ( Proxy proxy : decrypted.getProxies() ) 491 { 492 AuthenticationBuilder authBuilder = new AuthenticationBuilder(); 493 authBuilder.addUsername( proxy.getUsername() ).addPassword( proxy.getPassword() ); 494 proxySelector.add( new org.eclipse.aether.repository.Proxy( proxy.getProtocol(), proxy.getHost(), 495 proxy.getPort(), authBuilder.build() ), 496 proxy.getNonProxyHosts() ); 497 } 498 session.setProxySelector( proxySelector ); 499 500 DefaultAuthenticationSelector authSelector = new DefaultAuthenticationSelector(); 501 for ( Server server : decrypted.getServers() ) 502 { 503 AuthenticationBuilder authBuilder = new AuthenticationBuilder(); 504 authBuilder.addUsername( server.getUsername() ).addPassword( server.getPassword() ); 505 authBuilder.addPrivateKey( server.getPrivateKey(), server.getPassphrase() ); 506 authSelector.add( server.getId(), authBuilder.build() ); 507 508 if ( server.getConfiguration() != null ) 509 { 510 Xpp3Dom dom = (Xpp3Dom) server.getConfiguration(); 511 for ( int i = dom.getChildCount() - 1; i >= 0; i-- ) 512 { 513 Xpp3Dom child = dom.getChild( i ); 514 if ( "wagonProvider".equals( child.getName() ) ) 515 { 516 dom.removeChild( i ); 517 } 518 } 519 520 XmlPlexusConfiguration config = new XmlPlexusConfiguration( dom ); 521 configProps.put( "aether.connector.wagon.config." + server.getId(), config ); 522 } 523 524 configProps.put( "aether.connector.perms.fileMode." + server.getId(), server.getFilePermissions() ); 525 configProps.put( "aether.connector.perms.dirMode." + server.getId(), server.getDirectoryPermissions() ); 526 } 527 session.setAuthenticationSelector( authSelector ); 528 529 session.setTransferListener( request.getTransferListener() ); 530 531 session.setRepositoryListener( eventSpyDispatcher.chainListener( new LoggingRepositoryListener( logger ) ) ); 532 533 session.setUserProperties( request.getUserProperties() ); 534 session.setSystemProperties( request.getSystemProperties() ); 535 session.setConfigProperties( configProps ); 536 537 return session; 538 } 539 540 private String getUserAgent() 541 { 542 return "Apache-Maven/" + getMavenVersion() + " (Java " + System.getProperty( "java.version" ) + "; " 543 + System.getProperty( "os.name" ) + " " + System.getProperty( "os.version" ) + ")"; 544 } 545 546 private String getMavenVersion() 547 { 548 Properties props = new Properties(); 549 550 InputStream is = getClass().getResourceAsStream( "/META-INF/maven/org.apache.maven/maven-core/pom.properties" ); 551 if ( is != null ) 552 { 553 try 554 { 555 props.load( is ); 556 } 557 catch ( IOException e ) 558 { 559 logger.debug( "Failed to read Maven version", e ); 560 } 561 IOUtil.close( is ); 562 } 563 564 return props.getProperty( "version", "unknown-version" ); 565 } 566 567 private void validateLocalRepository( MavenExecutionRequest request ) 568 throws LocalRepositoryNotAccessibleException 569 { 570 File localRepoDir = request.getLocalRepositoryPath(); 571 572 logger.debug( "Using local repository at " + localRepoDir ); 573 574 localRepoDir.mkdirs(); 575 576 if ( !localRepoDir.isDirectory() ) 577 { 578 throw new LocalRepositoryNotAccessibleException( "Could not create local repository at " + localRepoDir ); 579 } 580 } 581 582 private Collection<AbstractMavenLifecycleParticipant> getLifecycleParticipants( Collection<MavenProject> projects ) 583 { 584 Collection<AbstractMavenLifecycleParticipant> lifecycleListeners = 585 new LinkedHashSet<AbstractMavenLifecycleParticipant>(); 586 587 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); 588 try 589 { 590 try 591 { 592 lifecycleListeners.addAll( container.lookupList( AbstractMavenLifecycleParticipant.class ) ); 593 } 594 catch ( ComponentLookupException e ) 595 { 596 // this is just silly, lookupList should return an empty list! 597 logger.warn( "Failed to lookup lifecycle participants: " + e.getMessage() ); 598 } 599 600 Collection<ClassLoader> scannedRealms = new HashSet<ClassLoader>(); 601 602 for ( MavenProject project : projects ) 603 { 604 ClassLoader projectRealm = project.getClassRealm(); 605 606 if ( projectRealm != null && scannedRealms.add( projectRealm ) ) 607 { 608 Thread.currentThread().setContextClassLoader( projectRealm ); 609 610 try 611 { 612 lifecycleListeners.addAll( container.lookupList( AbstractMavenLifecycleParticipant.class ) ); 613 } 614 catch ( ComponentLookupException e ) 615 { 616 // this is just silly, lookupList should return an empty list! 617 logger.warn( "Failed to lookup lifecycle participants: " + e.getMessage() ); 618 } 619 } 620 } 621 } 622 finally 623 { 624 Thread.currentThread().setContextClassLoader( originalClassLoader ); 625 } 626 627 return lifecycleListeners; 628 } 629 630 private MavenExecutionResult addExceptionToResult( MavenExecutionResult result, Throwable e ) 631 { 632 if ( !result.getExceptions().contains( e ) ) 633 { 634 result.addException( e ); 635 } 636 637 return result; 638 } 639 640 private List<MavenProject> getProjectsForMavenReactor( MavenSession session ) 641 throws ProjectBuildingException 642 { 643 MavenExecutionRequest request = session.getRequest(); 644 645 request.getProjectBuildingRequest().setRepositorySession( session.getRepositorySession() ); 646 647 List<MavenProject> projects = new ArrayList<MavenProject>(); 648 649 // We have no POM file. 650 // 651 if ( request.getPom() == null ) 652 { 653 ModelSource modelSource = new UrlModelSource( DefaultMaven.class.getResource( "project/standalone.xml" ) ); 654 MavenProject project = 655 projectBuilder.build( modelSource, request.getProjectBuildingRequest() ).getProject(); 656 project.setExecutionRoot( true ); 657 projects.add( project ); 658 request.setProjectPresent( false ); 659 return projects; 660 } 661 662 List<File> files = Arrays.asList( request.getPom().getAbsoluteFile() ); 663 collectProjects( projects, files, request ); 664 return projects; 665 } 666 667 private void collectProjects( List<MavenProject> projects, List<File> files, MavenExecutionRequest request ) 668 throws ProjectBuildingException 669 { 670 ProjectBuildingRequest projectBuildingRequest = request.getProjectBuildingRequest(); 671 672 List<ProjectBuildingResult> results = 673 projectBuilder.build( files, request.isRecursive(), projectBuildingRequest ); 674 675 boolean problems = false; 676 677 for ( ProjectBuildingResult result : results ) 678 { 679 projects.add( result.getProject() ); 680 681 if ( !result.getProblems().isEmpty() && logger.isWarnEnabled() ) 682 { 683 logger.warn( "" ); 684 logger.warn( "Some problems were encountered while building the effective model for " 685 + result.getProject().getId() ); 686 687 for ( ModelProblem problem : result.getProblems() ) 688 { 689 String location = ModelProblemUtils.formatLocation( problem, result.getProjectId() ); 690 logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( location ) ? " @ " + location : "" ) ); 691 } 692 693 problems = true; 694 } 695 } 696 697 if ( problems ) 698 { 699 logger.warn( "" ); 700 logger.warn( "It is highly recommended to fix these problems" 701 + " because they threaten the stability of your build." ); 702 logger.warn( "" ); 703 logger.warn( "For this reason, future Maven versions might no" 704 + " longer support building such malformed projects." ); 705 logger.warn( "" ); 706 } 707 } 708 709 private Map<String, MavenProject> getProjectMap( Collection<MavenProject> projects ) 710 throws DuplicateProjectException 711 { 712 Map<String, MavenProject> index = new LinkedHashMap<String, MavenProject>(); 713 Map<String, List<File>> collisions = new LinkedHashMap<String, List<File>>(); 714 715 for ( MavenProject project : projects ) 716 { 717 String projectId = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() ); 718 719 MavenProject collision = index.get( projectId ); 720 721 if ( collision == null ) 722 { 723 index.put( projectId, project ); 724 } 725 else 726 { 727 List<File> pomFiles = collisions.get( projectId ); 728 729 if ( pomFiles == null ) 730 { 731 pomFiles = new ArrayList<File>( Arrays.asList( collision.getFile(), project.getFile() ) ); 732 collisions.put( projectId, pomFiles ); 733 } 734 else 735 { 736 pomFiles.add( project.getFile() ); 737 } 738 } 739 } 740 741 if ( !collisions.isEmpty() ) 742 { 743 throw new DuplicateProjectException( "Two or more projects in the reactor" 744 + " have the same identifier, please make sure that <groupId>:<artifactId>:<version>" 745 + " is unique for each project: " + collisions, collisions ); 746 } 747 748 return index; 749 } 750 751 private void validateProjects( List<MavenProject> projects ) 752 { 753 Map<String, MavenProject> projectsMap = new HashMap<String, MavenProject>(); 754 755 for ( MavenProject project : projects ) 756 { 757 String projectKey = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() ); 758 759 projectsMap.put( projectKey, project ); 760 } 761 762 for ( MavenProject project : projects ) 763 { 764 // MNG-1911 / MNG-5572: Building plugins with extensions cannot be part of reactor 765 for ( Plugin plugin : project.getBuildPlugins() ) 766 { 767 if ( plugin.isExtensions() ) 768 { 769 String pluginKey = 770 ArtifactUtils.key( plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion() ); 771 772 if ( projectsMap.containsKey( pluginKey ) ) 773 { 774 logger.warn( project.getName() + " uses " + plugin.getKey() 775 + " as extensions, which is not possible within the same reactor build. This plugin was pulled from the local repository!" ); 776 } 777 } 778 } 779 } 780 } 781 782 private void validateActivatedProfiles( List<MavenProject> projects, List<String> activeProfileIds ) 783 { 784 Collection<String> notActivatedProfileIds = new LinkedHashSet<String>( activeProfileIds ); 785 786 for ( MavenProject project : projects ) 787 { 788 for ( List<String> profileIds : project.getInjectedProfileIds().values() ) 789 { 790 notActivatedProfileIds.removeAll( profileIds ); 791 } 792 } 793 794 for ( String notActivatedProfileId : notActivatedProfileIds ) 795 { 796 logger.warn( "The requested profile \"" + notActivatedProfileId 797 + "\" could not be activated because it does not exist." ); 798 } 799 } 800 801 @Deprecated // 5 January 2014 802 protected Logger getLogger() 803 { 804 return logger; 805 } 806 807 private ProjectDependencyGraph createProjectDependencyGraph( Collection<MavenProject> projects, MavenExecutionRequest request, 808 MavenExecutionResult result, boolean trimming ) 809 { 810 ProjectDependencyGraph projectDependencyGraph = null; 811 812 try 813 { 814 projectDependencyGraph = new DefaultProjectDependencyGraph( projects ); 815 816 if ( trimming ) 817 { 818 List<MavenProject> activeProjects = projectDependencyGraph.getSortedProjects(); 819 820 activeProjects = trimSelectedProjects( activeProjects, projectDependencyGraph, request ); 821 activeProjects = trimExcludedProjects( activeProjects, request ); 822 activeProjects = trimResumedProjects( activeProjects, request ); 823 824 if ( activeProjects.size() != projectDependencyGraph.getSortedProjects().size() ) 825 { 826 projectDependencyGraph = 827 new FilteredProjectDependencyGraph( projectDependencyGraph, activeProjects ); 828 } 829 } 830 } 831 catch ( CycleDetectedException e ) 832 { 833 String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage(); 834 835 ProjectCycleException error = new ProjectCycleException( message, e ); 836 837 addExceptionToResult( result, error ); 838 } 839 catch ( org.apache.maven.project.DuplicateProjectException e ) 840 { 841 addExceptionToResult( result, e ); 842 } 843 catch ( MavenExecutionException e ) 844 { 845 addExceptionToResult( result, e ); 846 } 847 848 return projectDependencyGraph; 849 } 850 851 private List<MavenProject> trimSelectedProjects( List<MavenProject> projects, ProjectDependencyGraph graph, 852 MavenExecutionRequest request ) 853 throws MavenExecutionException 854 { 855 List<MavenProject> result = projects; 856 857 if ( !request.getSelectedProjects().isEmpty() ) 858 { 859 File reactorDirectory = null; 860 if ( request.getBaseDirectory() != null ) 861 { 862 reactorDirectory = new File( request.getBaseDirectory() ); 863 } 864 865 Collection<MavenProject> selectedProjects = new LinkedHashSet<MavenProject>( projects.size() ); 866 867 for ( String selector : request.getSelectedProjects() ) 868 { 869 MavenProject selectedProject = null; 870 871 for ( MavenProject project : projects ) 872 { 873 if ( isMatchingProject( project, selector, reactorDirectory ) ) 874 { 875 selectedProject = project; 876 break; 877 } 878 } 879 880 if ( selectedProject != null ) 881 { 882 selectedProjects.add( selectedProject ); 883 } 884 else 885 { 886 throw new MavenExecutionException( "Could not find the selected project in the reactor: " 887 + selector, request.getPom() ); 888 } 889 } 890 891 boolean makeUpstream = false; 892 boolean makeDownstream = false; 893 894 if ( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM.equals( request.getMakeBehavior() ) ) 895 { 896 makeUpstream = true; 897 } 898 else if ( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM.equals( request.getMakeBehavior() ) ) 899 { 900 makeDownstream = true; 901 } 902 else if ( MavenExecutionRequest.REACTOR_MAKE_BOTH.equals( request.getMakeBehavior() ) ) 903 { 904 makeUpstream = true; 905 makeDownstream = true; 906 } 907 else if ( StringUtils.isNotEmpty( request.getMakeBehavior() ) ) 908 { 909 throw new MavenExecutionException( "Invalid reactor make behavior: " + request.getMakeBehavior(), 910 request.getPom() ); 911 } 912 913 if ( makeUpstream || makeDownstream ) 914 { 915 for ( MavenProject selectedProject : new ArrayList<MavenProject>( selectedProjects ) ) 916 { 917 if ( makeUpstream ) 918 { 919 selectedProjects.addAll( graph.getUpstreamProjects( selectedProject, true ) ); 920 } 921 if ( makeDownstream ) 922 { 923 selectedProjects.addAll( graph.getDownstreamProjects( selectedProject, true ) ); 924 } 925 } 926 } 927 928 result = new ArrayList<MavenProject>( selectedProjects.size() ); 929 930 for ( MavenProject project : projects ) 931 { 932 if ( selectedProjects.contains( project ) ) 933 { 934 result.add( project ); 935 } 936 } 937 } 938 939 return result; 940 } 941 942 private List<MavenProject> trimExcludedProjects( List<MavenProject> projects, MavenExecutionRequest request ) 943 throws MavenExecutionException 944 { 945 List<MavenProject> result = projects; 946 947 if ( !request.getExcludedProjects().isEmpty() ) 948 { 949 File reactorDirectory = null; 950 951 if ( request.getBaseDirectory() != null ) 952 { 953 reactorDirectory = new File( request.getBaseDirectory() ); 954 } 955 956 Collection<MavenProject> excludedProjects = new LinkedHashSet<MavenProject>( projects.size() ); 957 958 for ( String selector : request.getExcludedProjects() ) 959 { 960 MavenProject excludedProject = null; 961 962 for ( MavenProject project : projects ) 963 { 964 if ( isMatchingProject( project, selector, reactorDirectory ) ) 965 { 966 excludedProject = project; 967 break; 968 } 969 } 970 971 if ( excludedProject != null ) 972 { 973 excludedProjects.add( excludedProject ); 974 } 975 else 976 { 977 throw new MavenExecutionException( "Could not find the selected project in the reactor: " 978 + selector, request.getPom() ); 979 } 980 } 981 982 result = new ArrayList<MavenProject>( projects.size() ); 983 for ( MavenProject project : projects ) 984 { 985 if ( !excludedProjects.contains( project ) ) 986 { 987 result.add( project ); 988 } 989 } 990 } 991 992 return result; 993 } 994 995 private List<MavenProject> trimResumedProjects( List<MavenProject> projects, MavenExecutionRequest request ) 996 throws MavenExecutionException 997 { 998 List<MavenProject> result = projects; 999 1000 if ( StringUtils.isNotEmpty( request.getResumeFrom() ) ) 1001 { 1002 File reactorDirectory = null; 1003 if ( request.getBaseDirectory() != null ) 1004 { 1005 reactorDirectory = new File( request.getBaseDirectory() ); 1006 } 1007 1008 String selector = request.getResumeFrom(); 1009 1010 result = new ArrayList<MavenProject>( projects.size() ); 1011 1012 boolean resumed = false; 1013 1014 for ( MavenProject project : projects ) 1015 { 1016 if ( !resumed && isMatchingProject( project, selector, reactorDirectory ) ) 1017 { 1018 resumed = true; 1019 } 1020 1021 if ( resumed ) 1022 { 1023 result.add( project ); 1024 } 1025 } 1026 1027 if ( !resumed ) 1028 { 1029 throw new MavenExecutionException( "Could not find project to resume reactor build from: " + selector 1030 + " vs " + projects, request.getPom() ); 1031 } 1032 } 1033 1034 return result; 1035 } 1036 1037 private boolean isMatchingProject( MavenProject project, String selector, File reactorDirectory ) 1038 { 1039 // [groupId]:artifactId 1040 if ( selector.indexOf( ':' ) >= 0 ) 1041 { 1042 String id = ':' + project.getArtifactId(); 1043 1044 if ( id.equals( selector ) ) 1045 { 1046 return true; 1047 } 1048 1049 id = project.getGroupId() + id; 1050 1051 if ( id.equals( selector ) ) 1052 { 1053 return true; 1054 } 1055 } 1056 1057 // relative path, e.g. "sub", "../sub" or "." 1058 else if ( reactorDirectory != null ) 1059 { 1060 File selectedProject = new File( new File( reactorDirectory, selector ).toURI().normalize() ); 1061 1062 if ( selectedProject.isFile() ) 1063 { 1064 return selectedProject.equals( project.getFile() ); 1065 } 1066 else if ( selectedProject.isDirectory() ) 1067 { 1068 return selectedProject.equals( project.getBasedir() ); 1069 } 1070 } 1071 1072 return false; 1073 } 1074 1075}