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