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.util.ArrayList; 026import java.util.Arrays; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.Date; 030import java.util.HashMap; 031import java.util.HashSet; 032import java.util.LinkedHashMap; 033import java.util.LinkedHashSet; 034import java.util.List; 035import java.util.Map; 036import java.util.Properties; 037 038import org.apache.maven.artifact.ArtifactUtils; 039import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; 040import org.apache.maven.eventspy.internal.EventSpyDispatcher; 041import org.apache.maven.execution.DefaultMavenExecutionResult; 042import org.apache.maven.execution.ExecutionEvent; 043import org.apache.maven.execution.MavenExecutionRequest; 044import org.apache.maven.execution.MavenExecutionRequestPopulationException; 045import org.apache.maven.execution.MavenExecutionRequestPopulator; 046import org.apache.maven.execution.MavenExecutionResult; 047import org.apache.maven.execution.MavenSession; 048import org.apache.maven.execution.ProjectDependencyGraph; 049import org.apache.maven.lifecycle.internal.ExecutionEventCatapult; 050import org.apache.maven.lifecycle.internal.LifecycleStarter; 051import org.apache.maven.model.Plugin; 052import org.apache.maven.model.building.ModelProblem; 053import org.apache.maven.model.building.ModelProblemUtils; 054import org.apache.maven.model.building.ModelSource; 055import org.apache.maven.model.building.UrlModelSource; 056import org.apache.maven.plugin.LegacySupport; 057import org.apache.maven.project.MavenProject; 058import org.apache.maven.project.ProjectBuilder; 059import org.apache.maven.project.ProjectBuildingException; 060import org.apache.maven.project.ProjectBuildingRequest; 061import org.apache.maven.project.ProjectBuildingResult; 062import org.apache.maven.repository.LocalRepositoryNotAccessibleException; 063import org.apache.maven.repository.internal.MavenRepositorySystemUtils; 064import org.apache.maven.session.scope.internal.SessionScope; 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 simpleLocalRepoMgrFactory; 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. 193 // This ensures 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 197 // part of 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 202 // required after the execution of 9) as the AbstractLifecycleParticipants are free to mutate the MavenProject 203 // instances, which may change dependencies which can, in turn, affect the build order. 204 // 205 // 11) Execute LifecycleStarter.start() 206 // 207 @SuppressWarnings( "checkstyle:methodlength" ) 208 private MavenExecutionResult doExecute( MavenExecutionRequest request ) 209 { 210 request.setStartTime( new Date() ); 211 212 MavenExecutionResult result = new DefaultMavenExecutionResult(); 213 214 try 215 { 216 validateLocalRepository( request ); 217 } 218 catch ( LocalRepositoryNotAccessibleException e ) 219 { 220 return addExceptionToResult( result, e ); 221 } 222 223 DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) newRepositorySession( request ); 224 225 MavenSession session = new MavenSession( container, repoSession, request, result ); 226 227 // 228 // We enter the session scope right after the MavenSession creation and before any of the AbstractLifecycleParticipant lookups 229 // so that @SessionScoped components can be @Injected into AbstractLifecycleParticipants. 230 // 231 sessionScope.enter(); 232 sessionScope.seed( MavenSession.class, session ); 233 234 legacySupport.setSession( session ); 235 236 try 237 { 238 for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( Collections.<MavenProject>emptyList() ) ) 239 { 240 listener.afterSessionStart( session ); 241 } 242 } 243 catch ( MavenExecutionException e ) 244 { 245 return addExceptionToResult( result, e ); 246 } 247 248 eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null ); 249 250 List<MavenProject> projects; 251 try 252 { 253 projects = getProjectsForMavenReactor( session ); 254 // 255 // Capture the full set of projects before any potential constraining is performed by --projects 256 // 257 session.setAllProjects( projects ); 258 } 259 catch ( ProjectBuildingException e ) 260 { 261 return addExceptionToResult( result, e ); 262 } 263 264 validateProjects( projects ); 265 266 // 267 // This creates the graph and trims the projects down based on the user request using something like: 268 // 269 // -pl project0,project2 eclipse:eclipse 270 // 271 ProjectDependencyGraph projectDependencyGraph = createProjectDependencyGraph( projects, request, result, true ); 272 273 if ( result.hasExceptions() ) 274 { 275 return result; 276 } 277 278 session.setProjects( projectDependencyGraph.getSortedProjects() ); 279 280 try 281 { 282 session.setProjectMap( getProjectMap( session.getProjects() ) ); 283 } 284 catch ( DuplicateProjectException e ) 285 { 286 return addExceptionToResult( result, e ); 287 } 288 289 WorkspaceReader reactorWorkspace; 290 try 291 { 292 reactorWorkspace = container.lookup( WorkspaceReader.class, ReactorReader.HINT ); 293 } 294 catch ( ComponentLookupException e ) 295 { 296 return addExceptionToResult( result, e ); 297 } 298 299 // 300 // Desired order of precedence for local artifact repositories 301 // 302 // Reactor 303 // Workspace 304 // User Local Repository 305 // 306 repoSession.setWorkspaceReader( ChainedWorkspaceReader.newInstance( reactorWorkspace, 307 repoSession.getWorkspaceReader() ) ); 308 309 repoSession.setReadOnly(); 310 311 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); 312 try 313 { 314 for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) ) 315 { 316 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() ); 317 318 listener.afterProjectsRead( session ); 319 } 320 } 321 catch ( MavenExecutionException e ) 322 { 323 return addExceptionToResult( result, e ); 324 } 325 finally 326 { 327 Thread.currentThread().setContextClassLoader( originalClassLoader ); 328 } 329 330 // 331 // The projects need to be topologically after the participants have run their afterProjectsRead(session) 332 // because the participant is free to change the dependencies of a project which can potentially change the 333 // topological order of the projects, and therefore can potentially change the build order. 334 // 335 // Note that participants may affect the topological order of the projects but it is 336 // not expected that a participant will add or remove projects from the session. 337 // 338 projectDependencyGraph = createProjectDependencyGraph( session.getProjects(), request, result, false ); 339 340 try 341 { 342 if ( result.hasExceptions() ) 343 { 344 return result; 345 } 346 347 session.setProjects( projectDependencyGraph.getSortedProjects() ); 348 349 session.setProjectDependencyGraph( projectDependencyGraph ); 350 351 result.setTopologicallySortedProjects( session.getProjects() ); 352 353 result.setProject( session.getTopLevelProject() ); 354 355 lifecycleStarter.execute( session ); 356 357 validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() ); 358 359 if ( session.getResult().hasExceptions() ) 360 { 361 return addExceptionToResult( result, session.getResult().getExceptions().get( 0 ) ); 362 } 363 } 364 finally 365 { 366 try 367 { 368 afterSessionEnd( projects, session ); 369 } 370 catch ( MavenExecutionException e ) 371 { 372 return addExceptionToResult( result, e ); 373 } 374 finally 375 { 376 sessionScope.exit(); 377 } 378 } 379 380 return result; 381 } 382 383 private void afterSessionEnd( Collection<MavenProject> projects, MavenSession session ) 384 throws MavenExecutionException 385 { 386 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); 387 try 388 { 389 for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) ) 390 { 391 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() ); 392 393 listener.afterSessionEnd( session ); 394 } 395 } 396 finally 397 { 398 Thread.currentThread().setContextClassLoader( originalClassLoader ); 399 } 400 } 401 402 public RepositorySystemSession newRepositorySession( MavenExecutionRequest request ) 403 { 404 DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); 405 406 session.setCache( request.getRepositoryCache() ); 407 408 Map<Object, Object> configProps = new LinkedHashMap<Object, Object>(); 409 configProps.put( ConfigurationProperties.USER_AGENT, getUserAgent() ); 410 configProps.put( ConfigurationProperties.INTERACTIVE, request.isInteractiveMode() ); 411 configProps.putAll( request.getSystemProperties() ); 412 configProps.putAll( request.getUserProperties() ); 413 414 session.setOffline( request.isOffline() ); 415 session.setChecksumPolicy( request.getGlobalChecksumPolicy() ); 416 if ( request.isNoSnapshotUpdates() ) 417 { 418 session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_NEVER ); 419 } 420 else if ( request.isUpdateSnapshots() ) 421 { 422 session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_ALWAYS ); 423 } 424 else 425 { 426 session.setUpdatePolicy( null ); 427 } 428 429 int errorPolicy = 0; 430 errorPolicy |= request.isCacheNotFound() ? ResolutionErrorPolicy.CACHE_NOT_FOUND : 0; 431 errorPolicy |= request.isCacheTransferError() ? ResolutionErrorPolicy.CACHE_TRANSFER_ERROR : 0; 432 session.setResolutionErrorPolicy( new SimpleResolutionErrorPolicy( errorPolicy, errorPolicy 433 | ResolutionErrorPolicy.CACHE_NOT_FOUND ) ); 434 435 session.setArtifactTypeRegistry( RepositoryUtils.newArtifactTypeRegistry( artifactHandlerManager ) ); 436 437 LocalRepository localRepo = new LocalRepository( request.getLocalRepository().getBasedir() ); 438 439 if ( request.isUseLegacyLocalRepository() ) 440 { 441 logger.warn( "Disabling enhanced local repository: using legacy is strongly discouraged to ensure" 442 + " build reproducibility." ); 443 try 444 { 445 session.setLocalRepositoryManager( simpleLocalRepoMgrFactory.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 loc = ModelProblemUtils.formatLocation( problem, result.getProjectId() ); 690 logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( loc ) ? " @ " + loc : "" ) ); 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 p : projects ) 756 { 757 String projectKey = ArtifactUtils.key( p.getGroupId(), p.getArtifactId(), p.getVersion() ); 758 759 projectsMap.put( projectKey, p ); 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. " 776 + "This plugin was pulled from the local repository!" ); 777 } 778 } 779 } 780 } 781 } 782 783 private void validateActivatedProfiles( List<MavenProject> projects, List<String> activeProfileIds ) 784 { 785 Collection<String> notActivatedProfileIds = new LinkedHashSet<String>( activeProfileIds ); 786 787 for ( MavenProject project : projects ) 788 { 789 for ( List<String> profileIds : project.getInjectedProfileIds().values() ) 790 { 791 notActivatedProfileIds.removeAll( profileIds ); 792 } 793 } 794 795 for ( String notActivatedProfileId : notActivatedProfileIds ) 796 { 797 logger.warn( "The requested profile \"" + notActivatedProfileId 798 + "\" could not be activated because it does not exist." ); 799 } 800 } 801 802 @Deprecated // 5 January 2014 803 protected Logger getLogger() 804 { 805 return logger; 806 } 807 808 private ProjectDependencyGraph createProjectDependencyGraph( Collection<MavenProject> projects, 809 MavenExecutionRequest request, 810 MavenExecutionResult result, boolean trimming ) 811 { 812 ProjectDependencyGraph projectDependencyGraph = null; 813 814 try 815 { 816 projectDependencyGraph = new DefaultProjectDependencyGraph( projects ); 817 818 if ( trimming ) 819 { 820 List<MavenProject> activeProjects = projectDependencyGraph.getSortedProjects(); 821 822 activeProjects = trimSelectedProjects( activeProjects, projectDependencyGraph, request ); 823 activeProjects = trimExcludedProjects( activeProjects, request ); 824 activeProjects = trimResumedProjects( activeProjects, request ); 825 826 if ( activeProjects.size() != projectDependencyGraph.getSortedProjects().size() ) 827 { 828 projectDependencyGraph = 829 new FilteredProjectDependencyGraph( projectDependencyGraph, activeProjects ); 830 } 831 } 832 } 833 catch ( CycleDetectedException e ) 834 { 835 String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage(); 836 837 ProjectCycleException error = new ProjectCycleException( message, e ); 838 839 addExceptionToResult( result, error ); 840 } 841 catch ( org.apache.maven.project.DuplicateProjectException e ) 842 { 843 addExceptionToResult( result, e ); 844 } 845 catch ( MavenExecutionException e ) 846 { 847 addExceptionToResult( result, e ); 848 } 849 850 return projectDependencyGraph; 851 } 852 853 private List<MavenProject> trimSelectedProjects( List<MavenProject> projects, ProjectDependencyGraph graph, 854 MavenExecutionRequest request ) 855 throws MavenExecutionException 856 { 857 List<MavenProject> result = projects; 858 859 if ( !request.getSelectedProjects().isEmpty() ) 860 { 861 File reactorDirectory = null; 862 if ( request.getBaseDirectory() != null ) 863 { 864 reactorDirectory = new File( request.getBaseDirectory() ); 865 } 866 867 Collection<MavenProject> selectedProjects = new LinkedHashSet<MavenProject>( projects.size() ); 868 869 for ( String selector : request.getSelectedProjects() ) 870 { 871 MavenProject selectedProject = null; 872 873 for ( MavenProject project : projects ) 874 { 875 if ( isMatchingProject( project, selector, reactorDirectory ) ) 876 { 877 selectedProject = project; 878 break; 879 } 880 } 881 882 if ( selectedProject != null ) 883 { 884 selectedProjects.add( selectedProject ); 885 } 886 else 887 { 888 throw new MavenExecutionException( "Could not find the selected project in the reactor: " 889 + selector, request.getPom() ); 890 } 891 } 892 893 boolean makeUpstream = false; 894 boolean makeDownstream = false; 895 896 if ( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM.equals( request.getMakeBehavior() ) ) 897 { 898 makeUpstream = true; 899 } 900 else if ( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM.equals( request.getMakeBehavior() ) ) 901 { 902 makeDownstream = true; 903 } 904 else if ( MavenExecutionRequest.REACTOR_MAKE_BOTH.equals( request.getMakeBehavior() ) ) 905 { 906 makeUpstream = true; 907 makeDownstream = true; 908 } 909 else if ( StringUtils.isNotEmpty( request.getMakeBehavior() ) ) 910 { 911 throw new MavenExecutionException( "Invalid reactor make behavior: " + request.getMakeBehavior(), 912 request.getPom() ); 913 } 914 915 if ( makeUpstream || makeDownstream ) 916 { 917 for ( MavenProject selectedProject : new ArrayList<MavenProject>( selectedProjects ) ) 918 { 919 if ( makeUpstream ) 920 { 921 selectedProjects.addAll( graph.getUpstreamProjects( selectedProject, true ) ); 922 } 923 if ( makeDownstream ) 924 { 925 selectedProjects.addAll( graph.getDownstreamProjects( selectedProject, true ) ); 926 } 927 } 928 } 929 930 result = new ArrayList<MavenProject>( selectedProjects.size() ); 931 932 for ( MavenProject project : projects ) 933 { 934 if ( selectedProjects.contains( project ) ) 935 { 936 result.add( project ); 937 } 938 } 939 } 940 941 return result; 942 } 943 944 private List<MavenProject> trimExcludedProjects( List<MavenProject> projects, MavenExecutionRequest request ) 945 throws MavenExecutionException 946 { 947 List<MavenProject> result = projects; 948 949 if ( !request.getExcludedProjects().isEmpty() ) 950 { 951 File reactorDirectory = null; 952 953 if ( request.getBaseDirectory() != null ) 954 { 955 reactorDirectory = new File( request.getBaseDirectory() ); 956 } 957 958 Collection<MavenProject> excludedProjects = new LinkedHashSet<MavenProject>( projects.size() ); 959 960 for ( String selector : request.getExcludedProjects() ) 961 { 962 MavenProject excludedProject = null; 963 964 for ( MavenProject project : projects ) 965 { 966 if ( isMatchingProject( project, selector, reactorDirectory ) ) 967 { 968 excludedProject = project; 969 break; 970 } 971 } 972 973 if ( excludedProject != null ) 974 { 975 excludedProjects.add( excludedProject ); 976 } 977 else 978 { 979 throw new MavenExecutionException( "Could not find the selected project in the reactor: " 980 + selector, request.getPom() ); 981 } 982 } 983 984 result = new ArrayList<MavenProject>( projects.size() ); 985 for ( MavenProject project : projects ) 986 { 987 if ( !excludedProjects.contains( project ) ) 988 { 989 result.add( project ); 990 } 991 } 992 } 993 994 return result; 995 } 996 997 private List<MavenProject> trimResumedProjects( List<MavenProject> projects, MavenExecutionRequest request ) 998 throws MavenExecutionException 999 { 1000 List<MavenProject> result = projects; 1001 1002 if ( StringUtils.isNotEmpty( request.getResumeFrom() ) ) 1003 { 1004 File reactorDirectory = null; 1005 if ( request.getBaseDirectory() != null ) 1006 { 1007 reactorDirectory = new File( request.getBaseDirectory() ); 1008 } 1009 1010 String selector = request.getResumeFrom(); 1011 1012 result = new ArrayList<MavenProject>( projects.size() ); 1013 1014 boolean resumed = false; 1015 1016 for ( MavenProject project : projects ) 1017 { 1018 if ( !resumed && isMatchingProject( project, selector, reactorDirectory ) ) 1019 { 1020 resumed = true; 1021 } 1022 1023 if ( resumed ) 1024 { 1025 result.add( project ); 1026 } 1027 } 1028 1029 if ( !resumed ) 1030 { 1031 throw new MavenExecutionException( "Could not find project to resume reactor build from: " + selector 1032 + " vs " + projects, request.getPom() ); 1033 } 1034 } 1035 1036 return result; 1037 } 1038 1039 private boolean isMatchingProject( MavenProject project, String selector, File reactorDirectory ) 1040 { 1041 // [groupId]:artifactId 1042 if ( selector.indexOf( ':' ) >= 0 ) 1043 { 1044 String id = ':' + project.getArtifactId(); 1045 1046 if ( id.equals( selector ) ) 1047 { 1048 return true; 1049 } 1050 1051 id = project.getGroupId() + id; 1052 1053 if ( id.equals( selector ) ) 1054 { 1055 return true; 1056 } 1057 } 1058 1059 // relative path, e.g. "sub", "../sub" or "." 1060 else if ( reactorDirectory != null ) 1061 { 1062 File selectedProject = new File( new File( reactorDirectory, selector ).toURI().normalize() ); 1063 1064 if ( selectedProject.isFile() ) 1065 { 1066 return selectedProject.equals( project.getFile() ); 1067 } 1068 else if ( selectedProject.isDirectory() ) 1069 { 1070 return selectedProject.equals( project.getBasedir() ); 1071 } 1072 } 1073 1074 return false; 1075 } 1076 1077}