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