001 package org.apache.maven.project; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license 005 * agreements. See the NOTICE file distributed with this work for additional information regarding 006 * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance with the License. You may obtain a 008 * copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed under the License 013 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 014 * or implied. See the License for the specific language governing permissions and limitations under 015 * the License. 016 */ 017 018 import java.io.File; 019 import java.io.IOException; 020 import java.util.ArrayList; 021 import java.util.Arrays; 022 import java.util.Collections; 023 import java.util.HashMap; 024 import java.util.LinkedHashSet; 025 import java.util.List; 026 import java.util.Map; 027 import java.util.Set; 028 029 import org.apache.maven.RepositoryUtils; 030 import org.apache.maven.artifact.Artifact; 031 import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager; 032 import org.apache.maven.model.Build; 033 import org.apache.maven.model.Model; 034 import org.apache.maven.model.Profile; 035 import org.apache.maven.model.building.DefaultModelBuildingRequest; 036 import org.apache.maven.model.building.DefaultModelProblem; 037 import org.apache.maven.model.building.FileModelSource; 038 import org.apache.maven.model.building.ModelBuilder; 039 import org.apache.maven.model.building.ModelBuildingException; 040 import org.apache.maven.model.building.ModelBuildingRequest; 041 import org.apache.maven.model.building.ModelBuildingResult; 042 import org.apache.maven.model.building.ModelProblem; 043 import org.apache.maven.model.building.ModelProcessor; 044 import org.apache.maven.model.building.ModelSource; 045 import org.apache.maven.model.building.StringModelSource; 046 import org.apache.maven.model.resolution.ModelResolver; 047 import org.apache.maven.repository.RepositorySystem; 048 import org.apache.maven.repository.internal.ArtifactDescriptorUtils; 049 import org.codehaus.plexus.component.annotations.Component; 050 import org.codehaus.plexus.component.annotations.Requirement; 051 import org.codehaus.plexus.logging.Logger; 052 import org.codehaus.plexus.util.Os; 053 import org.codehaus.plexus.util.StringUtils; 054 import org.sonatype.aether.RepositorySystemSession; 055 import org.sonatype.aether.RequestTrace; 056 import org.sonatype.aether.impl.RemoteRepositoryManager; 057 import org.sonatype.aether.repository.LocalRepositoryManager; 058 import org.sonatype.aether.repository.RemoteRepository; 059 import org.sonatype.aether.repository.WorkspaceRepository; 060 import org.sonatype.aether.resolution.ArtifactRequest; 061 import org.sonatype.aether.resolution.ArtifactResult; 062 import org.sonatype.aether.util.DefaultRequestTrace; 063 064 /** 065 */ 066 @Component( role = ProjectBuilder.class ) 067 public class DefaultProjectBuilder 068 implements ProjectBuilder 069 { 070 071 @Requirement 072 private Logger logger; 073 074 @Requirement 075 private ModelBuilder modelBuilder; 076 077 @Requirement 078 private ModelProcessor modelProcessor; 079 080 @Requirement 081 private ProjectBuildingHelper projectBuildingHelper; 082 083 @Requirement 084 private RepositorySystem repositorySystem; 085 086 @Requirement 087 private org.sonatype.aether.RepositorySystem repoSystem; 088 089 @Requirement 090 private RemoteRepositoryManager repositoryManager; 091 092 @Requirement 093 private ProjectDependenciesResolver dependencyResolver; 094 095 // ---------------------------------------------------------------------- 096 // MavenProjectBuilder Implementation 097 // ---------------------------------------------------------------------- 098 099 public ProjectBuildingResult build( File pomFile, ProjectBuildingRequest request ) 100 throws ProjectBuildingException 101 { 102 return build( pomFile, new FileModelSource( pomFile ), new InternalConfig( request, null ) ); 103 } 104 105 public ProjectBuildingResult build( ModelSource modelSource, ProjectBuildingRequest request ) 106 throws ProjectBuildingException 107 { 108 return build( null, modelSource, new InternalConfig( request, null ) ); 109 } 110 111 private ProjectBuildingResult build( File pomFile, ModelSource modelSource, InternalConfig config ) 112 throws ProjectBuildingException 113 { 114 ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); 115 116 try 117 { 118 ProjectBuildingRequest configuration = config.request; 119 120 MavenProject project = configuration.getProject(); 121 122 List<ModelProblem> modelProblems = null; 123 Throwable error = null; 124 125 if ( project == null ) 126 { 127 ModelBuildingRequest request = getModelBuildingRequest( config ); 128 129 project = new MavenProject( repositorySystem, this, configuration, logger ); 130 131 DefaultModelBuildingListener listener = 132 new DefaultModelBuildingListener( project, projectBuildingHelper, configuration ); 133 request.setModelBuildingListener( listener ); 134 135 request.setPomFile( pomFile ); 136 request.setModelSource( modelSource ); 137 request.setLocationTracking( true ); 138 139 ModelBuildingResult result; 140 try 141 { 142 result = modelBuilder.build( request ); 143 } 144 catch ( ModelBuildingException e ) 145 { 146 result = e.getResult(); 147 if ( result == null || result.getEffectiveModel() == null ) 148 { 149 throw new ProjectBuildingException( e.getModelId(), e.getMessage(), pomFile, e ); 150 } 151 // validation error, continue project building and delay failing to help IDEs 152 error = e; 153 } 154 155 modelProblems = result.getProblems(); 156 157 initProject( project, Collections.<String, MavenProject> emptyMap(), result, 158 new HashMap<File, Boolean>() ); 159 } 160 else if ( configuration.isResolveDependencies() ) 161 { 162 projectBuildingHelper.selectProjectRealm( project ); 163 } 164 165 DependencyResolutionResult resolutionResult = null; 166 167 if ( configuration.isResolveDependencies() ) 168 { 169 resolutionResult = resolveDependencies( project, config.session ); 170 } 171 172 ProjectBuildingResult result = new DefaultProjectBuildingResult( project, modelProblems, resolutionResult ); 173 174 if ( error != null ) 175 { 176 ProjectBuildingException e = new ProjectBuildingException( Arrays.asList( result ) ); 177 e.initCause( error ); 178 throw e; 179 } 180 181 return result; 182 } 183 finally 184 { 185 Thread.currentThread().setContextClassLoader( oldContextClassLoader ); 186 } 187 } 188 189 private DependencyResolutionResult resolveDependencies( MavenProject project, RepositorySystemSession session ) 190 { 191 DependencyResolutionResult resolutionResult = null; 192 193 try 194 { 195 DefaultDependencyResolutionRequest resolution = new DefaultDependencyResolutionRequest( project, session ); 196 resolutionResult = dependencyResolver.resolve( resolution ); 197 } 198 catch ( DependencyResolutionException e ) 199 { 200 resolutionResult = e.getResult(); 201 } 202 203 Set<Artifact> artifacts = new LinkedHashSet<Artifact>(); 204 if ( resolutionResult.getDependencyGraph() != null ) 205 { 206 RepositoryUtils.toArtifacts( artifacts, resolutionResult.getDependencyGraph().getChildren(), 207 Collections.singletonList( project.getArtifact().getId() ), null ); 208 209 // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not 210 LocalRepositoryManager lrm = session.getLocalRepositoryManager(); 211 for ( Artifact artifact : artifacts ) 212 { 213 if ( !artifact.isResolved() ) 214 { 215 String path = lrm.getPathForLocalArtifact( RepositoryUtils.toArtifact( artifact ) ); 216 artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) ); 217 } 218 } 219 } 220 project.setResolvedArtifacts( artifacts ); 221 project.setArtifacts( artifacts ); 222 223 return resolutionResult; 224 } 225 226 private List<String> getProfileIds( List<Profile> profiles ) 227 { 228 List<String> ids = new ArrayList<String>( profiles.size() ); 229 230 for ( Profile profile : profiles ) 231 { 232 ids.add( profile.getId() ); 233 } 234 235 return ids; 236 } 237 238 private ModelBuildingRequest getModelBuildingRequest( InternalConfig config ) 239 { 240 ProjectBuildingRequest configuration = config.request; 241 242 ModelBuildingRequest request = new DefaultModelBuildingRequest(); 243 244 RequestTrace trace = DefaultRequestTrace.newChild( null, configuration ).newChild( request ); 245 246 ModelResolver resolver = 247 new ProjectModelResolver( config.session, trace, repoSystem, repositoryManager, config.repositories, 248 configuration.getRepositoryMerging(), config.modelPool ); 249 250 request.setValidationLevel( configuration.getValidationLevel() ); 251 request.setProcessPlugins( configuration.isProcessPlugins() ); 252 request.setProfiles( configuration.getProfiles() ); 253 request.setActiveProfileIds( configuration.getActiveProfileIds() ); 254 request.setInactiveProfileIds( configuration.getInactiveProfileIds() ); 255 request.setSystemProperties( configuration.getSystemProperties() ); 256 request.setUserProperties( configuration.getUserProperties() ); 257 request.setBuildStartTime( configuration.getBuildStartTime() ); 258 request.setModelResolver( resolver ); 259 request.setModelCache( new ReactorModelCache() ); 260 261 return request; 262 } 263 264 public ProjectBuildingResult build( Artifact artifact, ProjectBuildingRequest request ) 265 throws ProjectBuildingException 266 { 267 return build( artifact, false, request ); 268 } 269 270 public ProjectBuildingResult build( Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request ) 271 throws ProjectBuildingException 272 { 273 org.sonatype.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact( artifact ); 274 pomArtifact = ArtifactDescriptorUtils.toPomArtifact( pomArtifact ); 275 276 InternalConfig config = new InternalConfig( request, null ); 277 278 boolean localProject; 279 280 try 281 { 282 ArtifactRequest pomRequest = new ArtifactRequest(); 283 pomRequest.setArtifact( pomArtifact ); 284 pomRequest.setRepositories( config.repositories ); 285 ArtifactResult pomResult = repoSystem.resolveArtifact( config.session, pomRequest ); 286 287 pomArtifact = pomResult.getArtifact(); 288 localProject = pomResult.getRepository() instanceof WorkspaceRepository; 289 } 290 catch ( org.sonatype.aether.resolution.ArtifactResolutionException e ) 291 { 292 if ( e.getResults().get( 0 ).isMissing() && allowStubModel ) 293 { 294 return build( null, createStubModelSource( artifact ), config ); 295 } 296 throw new ProjectBuildingException( artifact.getId(), 297 "Error resolving project artifact: " + e.getMessage(), e ); 298 } 299 300 File pomFile = pomArtifact.getFile(); 301 302 if ( "pom".equals( artifact.getType() ) ) 303 { 304 artifact.selectVersion( pomArtifact.getVersion() ); 305 artifact.setFile( pomFile ); 306 artifact.setResolved( true ); 307 } 308 309 return build( localProject ? pomFile : null, new FileModelSource( pomFile ), config ); 310 } 311 312 private ModelSource createStubModelSource( Artifact artifact ) 313 { 314 StringBuilder buffer = new StringBuilder( 1024 ); 315 316 buffer.append( "<?xml version='1.0'?>" ); 317 buffer.append( "<project>" ); 318 buffer.append( "<modelVersion>4.0.0</modelVersion>" ); 319 buffer.append( "<groupId>" ).append( artifact.getGroupId() ).append( "</groupId>" ); 320 buffer.append( "<artifactId>" ).append( artifact.getArtifactId() ).append( "</artifactId>" ); 321 buffer.append( "<version>" ).append( artifact.getBaseVersion() ).append( "</version>" ); 322 buffer.append( "<packaging>" ).append( artifact.getType() ).append( "</packaging>" ); 323 buffer.append( "</project>" ); 324 325 return new StringModelSource( buffer, artifact.getId() ); 326 } 327 328 public List<ProjectBuildingResult> build( List<File> pomFiles, boolean recursive, ProjectBuildingRequest request ) 329 throws ProjectBuildingException 330 { 331 List<ProjectBuildingResult> results = new ArrayList<ProjectBuildingResult>(); 332 333 List<InterimResult> interimResults = new ArrayList<InterimResult>(); 334 335 ReactorModelPool modelPool = new ReactorModelPool(); 336 337 InternalConfig config = new InternalConfig( request, modelPool ); 338 339 Map<String, MavenProject> projectIndex = new HashMap<String, MavenProject>( 256 ); 340 341 boolean noErrors = 342 build( results, interimResults, projectIndex, pomFiles, new LinkedHashSet<File>(), true, recursive, config ); 343 344 populateReactorModelPool( modelPool, interimResults ); 345 346 ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); 347 348 try 349 { 350 noErrors = 351 build( results, new ArrayList<MavenProject>(), projectIndex, interimResults, request, 352 new HashMap<File, Boolean>() ) && noErrors; 353 } 354 finally 355 { 356 Thread.currentThread().setContextClassLoader( oldContextClassLoader ); 357 } 358 359 if ( !noErrors ) 360 { 361 throw new ProjectBuildingException( results ); 362 } 363 364 return results; 365 } 366 367 private boolean build( List<ProjectBuildingResult> results, List<InterimResult> interimResults, 368 Map<String, MavenProject> projectIndex, List<File> pomFiles, Set<File> aggregatorFiles, 369 boolean isRoot, boolean recursive, InternalConfig config ) 370 { 371 boolean noErrors = true; 372 373 for ( File pomFile : pomFiles ) 374 { 375 aggregatorFiles.add( pomFile ); 376 377 if ( !build( results, interimResults, projectIndex, pomFile, aggregatorFiles, isRoot, recursive, config ) ) 378 { 379 noErrors = false; 380 } 381 382 aggregatorFiles.remove( pomFile ); 383 } 384 385 return noErrors; 386 } 387 388 private boolean build( List<ProjectBuildingResult> results, List<InterimResult> interimResults, 389 Map<String, MavenProject> projectIndex, File pomFile, Set<File> aggregatorFiles, 390 boolean isRoot, boolean recursive, InternalConfig config ) 391 { 392 boolean noErrors = true; 393 394 ModelBuildingRequest request = getModelBuildingRequest( config ); 395 396 MavenProject project = new MavenProject( repositorySystem, this, config.request, logger ); 397 398 request.setPomFile( pomFile ); 399 request.setTwoPhaseBuilding( true ); 400 request.setLocationTracking( true ); 401 402 DefaultModelBuildingListener listener = 403 new DefaultModelBuildingListener( project, projectBuildingHelper, config.request ); 404 request.setModelBuildingListener( listener ); 405 406 try 407 { 408 ModelBuildingResult result = modelBuilder.build( request ); 409 410 Model model = result.getEffectiveModel(); 411 412 projectIndex.put( result.getModelIds().get( 0 ), project ); 413 414 InterimResult interimResult = new InterimResult( pomFile, request, result, listener, isRoot ); 415 interimResults.add( interimResult ); 416 417 if ( recursive && !model.getModules().isEmpty() ) 418 { 419 File basedir = pomFile.getParentFile(); 420 421 List<File> moduleFiles = new ArrayList<File>(); 422 423 for ( String module : model.getModules() ) 424 { 425 if ( StringUtils.isEmpty( module ) ) 426 { 427 continue; 428 } 429 430 module = module.replace( '\\', File.separatorChar ).replace( '/', File.separatorChar ); 431 432 File moduleFile = new File( basedir, module ); 433 434 if ( moduleFile.isDirectory() ) 435 { 436 moduleFile = modelProcessor.locatePom( moduleFile ); 437 } 438 439 if ( !moduleFile.isFile() ) 440 { 441 ModelProblem problem = 442 new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile 443 + " does not exist", ModelProblem.Severity.ERROR, ModelProblem.Version.BASE, model, -1, -1, null ); 444 result.getProblems().add( problem ); 445 446 noErrors = false; 447 448 continue; 449 } 450 451 if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) 452 { 453 // we don't canonicalize on unix to avoid interfering with symlinks 454 try 455 { 456 moduleFile = moduleFile.getCanonicalFile(); 457 } 458 catch ( IOException e ) 459 { 460 moduleFile = moduleFile.getAbsoluteFile(); 461 } 462 } 463 else 464 { 465 moduleFile = new File( moduleFile.toURI().normalize() ); 466 } 467 468 if ( aggregatorFiles.contains( moduleFile ) ) 469 { 470 StringBuilder buffer = new StringBuilder( 256 ); 471 for ( File aggregatorFile : aggregatorFiles ) 472 { 473 buffer.append( aggregatorFile ).append( " -> " ); 474 } 475 buffer.append( moduleFile ); 476 477 ModelProblem problem = 478 new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile 479 + " forms aggregation cycle " + buffer, ModelProblem.Severity.ERROR, ModelProblem.Version.BASE, model, -1, -1, 480 null ); 481 result.getProblems().add( problem ); 482 483 noErrors = false; 484 485 continue; 486 } 487 488 moduleFiles.add( moduleFile ); 489 } 490 491 interimResult.modules = new ArrayList<InterimResult>(); 492 493 if ( !build( results, interimResult.modules, projectIndex, moduleFiles, aggregatorFiles, false, 494 recursive, config ) ) 495 { 496 noErrors = false; 497 } 498 } 499 } 500 catch ( ModelBuildingException e ) 501 { 502 results.add( new DefaultProjectBuildingResult( e.getModelId(), pomFile, e.getProblems() ) ); 503 504 noErrors = false; 505 } 506 507 return noErrors; 508 } 509 510 static class InterimResult 511 { 512 513 File pomFile; 514 515 ModelBuildingRequest request; 516 517 ModelBuildingResult result; 518 519 DefaultModelBuildingListener listener; 520 521 boolean root; 522 523 List<InterimResult> modules = Collections.emptyList(); 524 525 InterimResult( File pomFile, ModelBuildingRequest request, ModelBuildingResult result, 526 DefaultModelBuildingListener listener, boolean root ) 527 { 528 this.pomFile = pomFile; 529 this.request = request; 530 this.result = result; 531 this.listener = listener; 532 this.root = root; 533 } 534 535 } 536 537 private void populateReactorModelPool( ReactorModelPool reactorModelPool, List<InterimResult> interimResults ) 538 { 539 for ( InterimResult interimResult : interimResults ) 540 { 541 Model model = interimResult.result.getEffectiveModel(); 542 reactorModelPool.put( model.getGroupId(), model.getArtifactId(), model.getVersion(), model.getPomFile() ); 543 544 populateReactorModelPool( reactorModelPool, interimResult.modules ); 545 } 546 } 547 548 private boolean build( List<ProjectBuildingResult> results, List<MavenProject> projects, 549 Map<String, MavenProject> projectIndex, List<InterimResult> interimResults, 550 ProjectBuildingRequest request, Map<File, Boolean> profilesXmls ) 551 { 552 boolean noErrors = true; 553 554 for ( InterimResult interimResult : interimResults ) 555 { 556 try 557 { 558 ModelBuildingResult result = modelBuilder.build( interimResult.request, interimResult.result ); 559 560 MavenProject project = interimResult.listener.getProject(); 561 initProject( project, projectIndex, result, profilesXmls ); 562 563 List<MavenProject> modules = new ArrayList<MavenProject>(); 564 noErrors = 565 build( results, modules, projectIndex, interimResult.modules, request, profilesXmls ) && noErrors; 566 567 projects.addAll( modules ); 568 projects.add( project ); 569 570 project.setExecutionRoot( interimResult.root ); 571 project.setCollectedProjects( modules ); 572 573 results.add( new DefaultProjectBuildingResult( project, result.getProblems(), null ) ); 574 } 575 catch ( ModelBuildingException e ) 576 { 577 results.add( new DefaultProjectBuildingResult( e.getModelId(), interimResult.pomFile, e.getProblems() ) ); 578 579 noErrors = false; 580 } 581 } 582 583 return noErrors; 584 } 585 586 private void initProject( MavenProject project, Map<String, MavenProject> projects, ModelBuildingResult result, 587 Map<File, Boolean> profilesXmls ) 588 { 589 Model model = result.getEffectiveModel(); 590 591 project.setModel( model ); 592 project.setOriginalModel( result.getRawModel() ); 593 594 project.setFile( model.getPomFile() ); 595 596 File parentPomFile = result.getRawModel( result.getModelIds().get( 1 ) ).getPomFile(); 597 project.setParentFile( parentPomFile ); 598 599 project.setParent( projects.get( result.getModelIds().get( 1 ) ) ); 600 601 Artifact projectArtifact = 602 repositorySystem.createArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(), null, 603 project.getPackaging() ); 604 project.setArtifact( projectArtifact ); 605 606 if ( project.getFile() != null ) 607 { 608 Build build = project.getBuild(); 609 project.addScriptSourceRoot( build.getScriptSourceDirectory() ); 610 project.addCompileSourceRoot( build.getSourceDirectory() ); 611 project.addTestCompileSourceRoot( build.getTestSourceDirectory() ); 612 } 613 614 List<Profile> activeProfiles = new ArrayList<Profile>(); 615 activeProfiles.addAll( result.getActivePomProfiles( result.getModelIds().get( 0 ) ) ); 616 activeProfiles.addAll( result.getActiveExternalProfiles() ); 617 project.setActiveProfiles( activeProfiles ); 618 619 project.setInjectedProfileIds( "external", getProfileIds( result.getActiveExternalProfiles() ) ); 620 for ( String modelId : result.getModelIds() ) 621 { 622 project.setInjectedProfileIds( modelId, getProfileIds( result.getActivePomProfiles( modelId ) ) ); 623 } 624 625 String modelId = findProfilesXml( result, profilesXmls ); 626 if ( modelId != null ) 627 { 628 ModelProblem problem = 629 new DefaultModelProblem( "Detected profiles.xml alongside " + modelId 630 + ", this file is no longer supported and was ignored" + ", please use the settings.xml instead", 631 ModelProblem.Severity.WARNING, ModelProblem.Version.V30, model, -1, -1, null ); 632 result.getProblems().add( problem ); 633 } 634 } 635 636 private String findProfilesXml( ModelBuildingResult result, Map<File, Boolean> profilesXmls ) 637 { 638 for ( String modelId : result.getModelIds() ) 639 { 640 Model model = result.getRawModel( modelId ); 641 642 File basedir = model.getProjectDirectory(); 643 if ( basedir == null ) 644 { 645 break; 646 } 647 648 Boolean profilesXml = profilesXmls.get( basedir ); 649 if ( profilesXml == null ) 650 { 651 profilesXml = Boolean.valueOf( new File( basedir, "profiles.xml" ).exists() ); 652 profilesXmls.put( basedir, profilesXml ); 653 } 654 if ( profilesXml.booleanValue() ) 655 { 656 return modelId; 657 } 658 } 659 660 return null; 661 } 662 663 class InternalConfig 664 { 665 666 public final ProjectBuildingRequest request; 667 668 public final RepositorySystemSession session; 669 670 public final List<RemoteRepository> repositories; 671 672 public final ReactorModelPool modelPool; 673 674 InternalConfig( ProjectBuildingRequest request, ReactorModelPool modelPool ) 675 { 676 this.request = request; 677 this.modelPool = modelPool; 678 session = 679 LegacyLocalRepositoryManager.overlay( request.getLocalRepository(), request.getRepositorySession(), 680 repoSystem ); 681 repositories = RepositoryUtils.toRepos( request.getRemoteRepositories() ); 682 } 683 684 } 685 686 }