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, null ) ); 103 } 104 105 public ProjectBuildingResult build( ModelSource modelSource, ProjectBuildingRequest request ) 106 throws ProjectBuildingException 107 { 108 return build( null, modelSource, new InternalConfig( request, null, 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( config.modelCache ); 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, 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 ReactorModelCache modelCache = new ReactorModelCache(); 338 339 InternalConfig config = new InternalConfig( request, modelPool, modelCache ); 340 341 Map<String, MavenProject> projectIndex = new HashMap<String, MavenProject>( 256 ); 342 343 boolean noErrors = 344 build( results, interimResults, projectIndex, pomFiles, new LinkedHashSet<File>(), true, recursive, config ); 345 346 populateReactorModelPool( modelPool, interimResults ); 347 348 ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); 349 350 try 351 { 352 noErrors = 353 build( results, new ArrayList<MavenProject>(), projectIndex, interimResults, request, 354 new HashMap<File, Boolean>() ) && noErrors; 355 } 356 finally 357 { 358 Thread.currentThread().setContextClassLoader( oldContextClassLoader ); 359 } 360 361 if ( !noErrors ) 362 { 363 throw new ProjectBuildingException( results ); 364 } 365 366 return results; 367 } 368 369 private boolean build( List<ProjectBuildingResult> results, List<InterimResult> interimResults, 370 Map<String, MavenProject> projectIndex, List<File> pomFiles, Set<File> aggregatorFiles, 371 boolean isRoot, boolean recursive, InternalConfig config ) 372 { 373 boolean noErrors = true; 374 375 for ( File pomFile : pomFiles ) 376 { 377 aggregatorFiles.add( pomFile ); 378 379 if ( !build( results, interimResults, projectIndex, pomFile, aggregatorFiles, isRoot, recursive, config ) ) 380 { 381 noErrors = false; 382 } 383 384 aggregatorFiles.remove( pomFile ); 385 } 386 387 return noErrors; 388 } 389 390 private boolean build( List<ProjectBuildingResult> results, List<InterimResult> interimResults, 391 Map<String, MavenProject> projectIndex, File pomFile, Set<File> aggregatorFiles, 392 boolean isRoot, boolean recursive, InternalConfig config ) 393 { 394 boolean noErrors = true; 395 396 ModelBuildingRequest request = getModelBuildingRequest( config ); 397 398 MavenProject project = new MavenProject( repositorySystem, this, config.request, logger ); 399 400 request.setPomFile( pomFile ); 401 request.setTwoPhaseBuilding( true ); 402 request.setLocationTracking( true ); 403 404 DefaultModelBuildingListener listener = 405 new DefaultModelBuildingListener( project, projectBuildingHelper, config.request ); 406 request.setModelBuildingListener( listener ); 407 408 try 409 { 410 ModelBuildingResult result = modelBuilder.build( request ); 411 412 Model model = result.getEffectiveModel(); 413 414 projectIndex.put( result.getModelIds().get( 0 ), project ); 415 416 InterimResult interimResult = new InterimResult( pomFile, request, result, listener, isRoot ); 417 interimResults.add( interimResult ); 418 419 if ( recursive && !model.getModules().isEmpty() ) 420 { 421 File basedir = pomFile.getParentFile(); 422 423 List<File> moduleFiles = new ArrayList<File>(); 424 425 for ( String module : model.getModules() ) 426 { 427 if ( StringUtils.isEmpty( module ) ) 428 { 429 continue; 430 } 431 432 module = module.replace( '\\', File.separatorChar ).replace( '/', File.separatorChar ); 433 434 File moduleFile = new File( basedir, module ); 435 436 if ( moduleFile.isDirectory() ) 437 { 438 moduleFile = modelProcessor.locatePom( moduleFile ); 439 } 440 441 if ( !moduleFile.isFile() ) 442 { 443 ModelProblem problem = 444 new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile 445 + " does not exist", ModelProblem.Severity.ERROR, model, -1, -1, null ); 446 result.getProblems().add( problem ); 447 448 noErrors = false; 449 450 continue; 451 } 452 453 if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) 454 { 455 // we don't canonicalize on unix to avoid interfering with symlinks 456 try 457 { 458 moduleFile = moduleFile.getCanonicalFile(); 459 } 460 catch ( IOException e ) 461 { 462 moduleFile = moduleFile.getAbsoluteFile(); 463 } 464 } 465 else 466 { 467 moduleFile = new File( moduleFile.toURI().normalize() ); 468 } 469 470 if ( aggregatorFiles.contains( moduleFile ) ) 471 { 472 StringBuilder buffer = new StringBuilder( 256 ); 473 for ( File aggregatorFile : aggregatorFiles ) 474 { 475 buffer.append( aggregatorFile ).append( " -> " ); 476 } 477 buffer.append( moduleFile ); 478 479 ModelProblem problem = 480 new DefaultModelProblem( "Child module " + moduleFile + " of " + pomFile 481 + " forms aggregation cycle " + buffer, ModelProblem.Severity.ERROR, model, -1, -1, 482 null ); 483 result.getProblems().add( problem ); 484 485 noErrors = false; 486 487 continue; 488 } 489 490 moduleFiles.add( moduleFile ); 491 } 492 493 interimResult.modules = new ArrayList<InterimResult>(); 494 495 if ( !build( results, interimResult.modules, projectIndex, moduleFiles, aggregatorFiles, false, 496 recursive, config ) ) 497 { 498 noErrors = false; 499 } 500 } 501 } 502 catch ( ModelBuildingException e ) 503 { 504 results.add( new DefaultProjectBuildingResult( e.getModelId(), pomFile, e.getProblems() ) ); 505 506 noErrors = false; 507 } 508 509 return noErrors; 510 } 511 512 static class InterimResult 513 { 514 515 File pomFile; 516 517 ModelBuildingRequest request; 518 519 ModelBuildingResult result; 520 521 DefaultModelBuildingListener listener; 522 523 boolean root; 524 525 List<InterimResult> modules = Collections.emptyList(); 526 527 InterimResult( File pomFile, ModelBuildingRequest request, ModelBuildingResult result, 528 DefaultModelBuildingListener listener, boolean root ) 529 { 530 this.pomFile = pomFile; 531 this.request = request; 532 this.result = result; 533 this.listener = listener; 534 this.root = root; 535 } 536 537 } 538 539 private void populateReactorModelPool( ReactorModelPool reactorModelPool, List<InterimResult> interimResults ) 540 { 541 for ( InterimResult interimResult : interimResults ) 542 { 543 Model model = interimResult.result.getEffectiveModel(); 544 reactorModelPool.put( model.getGroupId(), model.getArtifactId(), model.getVersion(), model.getPomFile() ); 545 546 populateReactorModelPool( reactorModelPool, interimResult.modules ); 547 } 548 } 549 550 private boolean build( List<ProjectBuildingResult> results, List<MavenProject> projects, 551 Map<String, MavenProject> projectIndex, List<InterimResult> interimResults, 552 ProjectBuildingRequest request, Map<File, Boolean> profilesXmls ) 553 { 554 boolean noErrors = true; 555 556 for ( InterimResult interimResult : interimResults ) 557 { 558 try 559 { 560 ModelBuildingResult result = modelBuilder.build( interimResult.request, interimResult.result ); 561 562 MavenProject project = interimResult.listener.getProject(); 563 initProject( project, projectIndex, result, profilesXmls ); 564 565 List<MavenProject> modules = new ArrayList<MavenProject>(); 566 noErrors = 567 build( results, modules, projectIndex, interimResult.modules, request, profilesXmls ) && noErrors; 568 569 projects.addAll( modules ); 570 projects.add( project ); 571 572 project.setExecutionRoot( interimResult.root ); 573 project.setCollectedProjects( modules ); 574 575 results.add( new DefaultProjectBuildingResult( project, result.getProblems(), null ) ); 576 } 577 catch ( ModelBuildingException e ) 578 { 579 results.add( new DefaultProjectBuildingResult( e.getModelId(), interimResult.pomFile, e.getProblems() ) ); 580 581 noErrors = false; 582 } 583 } 584 585 return noErrors; 586 } 587 588 private void initProject( MavenProject project, Map<String, MavenProject> projects, ModelBuildingResult result, 589 Map<File, Boolean> profilesXmls ) 590 { 591 Model model = result.getEffectiveModel(); 592 593 project.setModel( model ); 594 project.setOriginalModel( result.getRawModel() ); 595 596 project.setFile( model.getPomFile() ); 597 598 File parentPomFile = result.getRawModel( result.getModelIds().get( 1 ) ).getPomFile(); 599 project.setParentFile( parentPomFile ); 600 601 project.setParent( projects.get( result.getModelIds().get( 1 ) ) ); 602 603 Artifact projectArtifact = 604 repositorySystem.createArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(), null, 605 project.getPackaging() ); 606 project.setArtifact( projectArtifact ); 607 608 if ( project.getFile() != null ) 609 { 610 Build build = project.getBuild(); 611 project.addScriptSourceRoot( build.getScriptSourceDirectory() ); 612 project.addCompileSourceRoot( build.getSourceDirectory() ); 613 project.addTestCompileSourceRoot( build.getTestSourceDirectory() ); 614 } 615 616 List<Profile> activeProfiles = new ArrayList<Profile>(); 617 activeProfiles.addAll( result.getActivePomProfiles( result.getModelIds().get( 0 ) ) ); 618 activeProfiles.addAll( result.getActiveExternalProfiles() ); 619 project.setActiveProfiles( activeProfiles ); 620 621 project.setInjectedProfileIds( "external", getProfileIds( result.getActiveExternalProfiles() ) ); 622 for ( String modelId : result.getModelIds() ) 623 { 624 project.setInjectedProfileIds( modelId, getProfileIds( result.getActivePomProfiles( modelId ) ) ); 625 } 626 627 String modelId = findProfilesXml( result, profilesXmls ); 628 if ( modelId != null ) 629 { 630 ModelProblem problem = 631 new DefaultModelProblem( "Detected profiles.xml alongside " + modelId 632 + ", this file is no longer supported and was ignored" + ", please use the settings.xml instead", 633 ModelProblem.Severity.WARNING, model, -1, -1, null ); 634 result.getProblems().add( problem ); 635 } 636 } 637 638 private String findProfilesXml( ModelBuildingResult result, Map<File, Boolean> profilesXmls ) 639 { 640 for ( String modelId : result.getModelIds() ) 641 { 642 Model model = result.getRawModel( modelId ); 643 644 File basedir = model.getProjectDirectory(); 645 if ( basedir == null ) 646 { 647 break; 648 } 649 650 Boolean profilesXml = profilesXmls.get( basedir ); 651 if ( profilesXml == null ) 652 { 653 profilesXml = Boolean.valueOf( new File( basedir, "profiles.xml" ).exists() ); 654 profilesXmls.put( basedir, profilesXml ); 655 } 656 if ( profilesXml.booleanValue() ) 657 { 658 return modelId; 659 } 660 } 661 662 return null; 663 } 664 665 class InternalConfig 666 { 667 668 public final ProjectBuildingRequest request; 669 670 public final RepositorySystemSession session; 671 672 public final List<RemoteRepository> repositories; 673 674 public final ReactorModelPool modelPool; 675 676 public final ReactorModelCache modelCache; 677 678 public InternalConfig( ProjectBuildingRequest request, ReactorModelPool modelPool, ReactorModelCache modelCache ) 679 { 680 this.request = request; 681 this.modelPool = modelPool; 682 this.modelCache = modelCache; 683 session = 684 LegacyLocalRepositoryManager.overlay( request.getLocalRepository(), request.getRepositorySession(), 685 repoSystem ); 686 repositories = RepositoryUtils.toRepos( request.getRemoteRepositories() ); 687 } 688 689 } 690 691 }