001package org.apache.maven.model.building; 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.util.ArrayList; 025import java.util.Collection; 026import java.util.HashMap; 027import java.util.Iterator; 028import java.util.LinkedHashSet; 029import java.util.List; 030import java.util.Map; 031import java.util.Properties; 032 033import org.apache.maven.model.Activation; 034import org.apache.maven.model.Build; 035import org.apache.maven.model.Dependency; 036import org.apache.maven.model.DependencyManagement; 037import org.apache.maven.model.InputLocation; 038import org.apache.maven.model.InputSource; 039import org.apache.maven.model.Model; 040import org.apache.maven.model.Parent; 041import org.apache.maven.model.Plugin; 042import org.apache.maven.model.PluginManagement; 043import org.apache.maven.model.Profile; 044import org.apache.maven.model.Repository; 045import org.apache.maven.model.building.ModelProblem.Severity; 046import org.apache.maven.model.building.ModelProblem.Version; 047import org.apache.maven.model.composition.DependencyManagementImporter; 048import org.apache.maven.model.inheritance.InheritanceAssembler; 049import org.apache.maven.model.interpolation.ModelInterpolator; 050import org.apache.maven.model.io.ModelParseException; 051import org.apache.maven.model.management.DependencyManagementInjector; 052import org.apache.maven.model.management.PluginManagementInjector; 053import org.apache.maven.model.normalization.ModelNormalizer; 054import org.apache.maven.model.path.ModelPathTranslator; 055import org.apache.maven.model.path.ModelUrlNormalizer; 056import org.apache.maven.model.plugin.LifecycleBindingsInjector; 057import org.apache.maven.model.plugin.PluginConfigurationExpander; 058import org.apache.maven.model.plugin.ReportConfigurationExpander; 059import org.apache.maven.model.plugin.ReportingConverter; 060import org.apache.maven.model.profile.DefaultProfileActivationContext; 061import org.apache.maven.model.profile.ProfileInjector; 062import org.apache.maven.model.profile.ProfileSelector; 063import org.apache.maven.model.resolution.InvalidRepositoryException; 064import org.apache.maven.model.resolution.ModelResolver; 065import org.apache.maven.model.resolution.UnresolvableModelException; 066import org.apache.maven.model.superpom.SuperPomProvider; 067import org.apache.maven.model.validation.ModelValidator; 068import org.codehaus.plexus.component.annotations.Component; 069import org.codehaus.plexus.component.annotations.Requirement; 070 071/** 072 * @author Benjamin Bentmann 073 */ 074@Component( role = ModelBuilder.class ) 075public class DefaultModelBuilder 076 implements ModelBuilder 077{ 078 @Requirement 079 private ModelProcessor modelProcessor; 080 081 @Requirement 082 private ModelValidator modelValidator; 083 084 @Requirement 085 private ModelNormalizer modelNormalizer; 086 087 @Requirement 088 private ModelInterpolator modelInterpolator; 089 090 @Requirement 091 private ModelPathTranslator modelPathTranslator; 092 093 @Requirement 094 private ModelUrlNormalizer modelUrlNormalizer; 095 096 @Requirement 097 private SuperPomProvider superPomProvider; 098 099 @Requirement 100 private InheritanceAssembler inheritanceAssembler; 101 102 @Requirement 103 private ProfileSelector profileSelector; 104 105 @Requirement 106 private ProfileInjector profileInjector; 107 108 @Requirement 109 private PluginManagementInjector pluginManagementInjector; 110 111 @Requirement 112 private DependencyManagementInjector dependencyManagementInjector; 113 114 @Requirement 115 private DependencyManagementImporter dependencyManagementImporter; 116 117 @Requirement( optional = true ) 118 private LifecycleBindingsInjector lifecycleBindingsInjector; 119 120 @Requirement 121 private PluginConfigurationExpander pluginConfigurationExpander; 122 123 @Requirement 124 private ReportConfigurationExpander reportConfigurationExpander; 125 126 @Requirement 127 private ReportingConverter reportingConverter; 128 129 public DefaultModelBuilder setModelProcessor( ModelProcessor modelProcessor ) 130 { 131 this.modelProcessor = modelProcessor; 132 return this; 133 } 134 135 public DefaultModelBuilder setModelValidator( ModelValidator modelValidator ) 136 { 137 this.modelValidator = modelValidator; 138 return this; 139 } 140 141 public DefaultModelBuilder setModelNormalizer( ModelNormalizer modelNormalizer ) 142 { 143 this.modelNormalizer = modelNormalizer; 144 return this; 145 } 146 147 public DefaultModelBuilder setModelInterpolator( ModelInterpolator modelInterpolator ) 148 { 149 this.modelInterpolator = modelInterpolator; 150 return this; 151 } 152 153 public DefaultModelBuilder setModelPathTranslator( ModelPathTranslator modelPathTranslator ) 154 { 155 this.modelPathTranslator = modelPathTranslator; 156 return this; 157 } 158 159 public DefaultModelBuilder setModelUrlNormalizer( ModelUrlNormalizer modelUrlNormalizer ) 160 { 161 this.modelUrlNormalizer = modelUrlNormalizer; 162 return this; 163 } 164 165 public DefaultModelBuilder setSuperPomProvider( SuperPomProvider superPomProvider ) 166 { 167 this.superPomProvider = superPomProvider; 168 return this; 169 } 170 171 public DefaultModelBuilder setProfileSelector( ProfileSelector profileSelector ) 172 { 173 this.profileSelector = profileSelector; 174 return this; 175 } 176 177 public DefaultModelBuilder setProfileInjector( ProfileInjector profileInjector ) 178 { 179 this.profileInjector = profileInjector; 180 return this; 181 } 182 183 public DefaultModelBuilder setInheritanceAssembler( InheritanceAssembler inheritanceAssembler ) 184 { 185 this.inheritanceAssembler = inheritanceAssembler; 186 return this; 187 } 188 189 public DefaultModelBuilder setDependencyManagementImporter( DependencyManagementImporter depMngmntImporter ) 190 { 191 this.dependencyManagementImporter = depMngmntImporter; 192 return this; 193 } 194 195 public DefaultModelBuilder setDependencyManagementInjector( DependencyManagementInjector depMngmntInjector ) 196 { 197 this.dependencyManagementInjector = depMngmntInjector; 198 return this; 199 } 200 201 public DefaultModelBuilder setLifecycleBindingsInjector( LifecycleBindingsInjector lifecycleBindingsInjector ) 202 { 203 this.lifecycleBindingsInjector = lifecycleBindingsInjector; 204 return this; 205 } 206 207 public DefaultModelBuilder setPluginConfigurationExpander( PluginConfigurationExpander pluginConfigurationExpander ) 208 { 209 this.pluginConfigurationExpander = pluginConfigurationExpander; 210 return this; 211 } 212 213 public DefaultModelBuilder setPluginManagementInjector( PluginManagementInjector pluginManagementInjector ) 214 { 215 this.pluginManagementInjector = pluginManagementInjector; 216 return this; 217 } 218 219 public DefaultModelBuilder setReportConfigurationExpander( ReportConfigurationExpander reportConfigurationExpander ) 220 { 221 this.reportConfigurationExpander = reportConfigurationExpander; 222 return this; 223 } 224 225 public DefaultModelBuilder setReportingConverter( ReportingConverter reportingConverter ) 226 { 227 this.reportingConverter = reportingConverter; 228 return this; 229 } 230 231 public ModelBuildingResult build( ModelBuildingRequest request ) 232 throws ModelBuildingException 233 { 234 DefaultModelBuildingResult result = new DefaultModelBuildingResult(); 235 236 DefaultModelProblemCollector problems = new DefaultModelProblemCollector( result ); 237 238 DefaultProfileActivationContext profileActivationContext = getProfileActivationContext( request ); 239 240 problems.setSource( "(external profiles)" ); 241 List<Profile> activeExternalProfiles = 242 profileSelector.getActiveProfiles( request.getProfiles(), profileActivationContext, problems ); 243 244 result.setActiveExternalProfiles( activeExternalProfiles ); 245 246 if ( !activeExternalProfiles.isEmpty() ) 247 { 248 Properties profileProps = new Properties(); 249 for ( Profile profile : activeExternalProfiles ) 250 { 251 profileProps.putAll( profile.getProperties() ); 252 } 253 profileProps.putAll( profileActivationContext.getUserProperties() ); 254 profileActivationContext.setUserProperties( profileProps ); 255 } 256 257 Model inputModel = readModel( request.getModelSource(), request.getPomFile(), request, problems ); 258 259 problems.setRootModel( inputModel ); 260 261 ModelData resultData = new ModelData( request.getModelSource(), inputModel ); 262 ModelData superData = new ModelData( null, getSuperModel() ); 263 264 Collection<String> parentIds = new LinkedHashSet<String>(); 265 List<ModelData> lineage = new ArrayList<ModelData>(); 266 267 for ( ModelData currentData = resultData; currentData != null; ) 268 { 269 lineage.add( currentData ); 270 271 Model tmpModel = currentData.getModel(); 272 273 Model rawModel = tmpModel.clone(); 274 currentData.setRawModel( rawModel ); 275 276 problems.setSource( tmpModel ); 277 278 modelNormalizer.mergeDuplicates( tmpModel, request, problems ); 279 280 profileActivationContext.setProjectProperties( tmpModel.getProperties() ); 281 282 List<Profile> activePomProfiles = 283 profileSelector.getActiveProfiles( rawModel.getProfiles(), profileActivationContext, problems ); 284 currentData.setActiveProfiles( activePomProfiles ); 285 286 Map<String, Activation> interpolatedActivations = getProfileActivations( rawModel, false ); 287 injectProfileActivations( tmpModel, interpolatedActivations ); 288 289 for ( Profile activeProfile : activePomProfiles ) 290 { 291 profileInjector.injectProfile( tmpModel, activeProfile, request, problems ); 292 } 293 294 if ( currentData == resultData ) 295 { 296 for ( Profile activeProfile : activeExternalProfiles ) 297 { 298 profileInjector.injectProfile( tmpModel, activeProfile, request, problems ); 299 } 300 } 301 302 if ( currentData == superData ) 303 { 304 break; 305 } 306 307 configureResolver( request.getModelResolver(), tmpModel, problems ); 308 309 ModelData parentData = readParent( tmpModel, currentData.getSource(), request, problems ); 310 311 if ( parentData == null ) 312 { 313 currentData = superData; 314 } 315 else if ( currentData == resultData ) 316 { // First iteration - add initial parent id after version resolution. 317 currentData.setGroupId( currentData.getRawModel().getGroupId() == null 318 ? parentData.getGroupId() 319 : currentData.getRawModel().getGroupId() ); 320 321 currentData.setVersion( currentData.getRawModel().getVersion() == null 322 ? parentData.getVersion() 323 : currentData.getRawModel().getVersion() ); 324 325 currentData.setArtifactId( currentData.getRawModel().getArtifactId() ); 326 parentIds.add( currentData.getId() ); 327 // Reset - only needed for 'getId'. 328 currentData.setGroupId( null ); 329 currentData.setArtifactId( null ); 330 currentData.setVersion( null ); 331 currentData = parentData; 332 } 333 else if ( !parentIds.add( parentData.getId() ) ) 334 { 335 String message = "The parents form a cycle: "; 336 for ( String modelId : parentIds ) 337 { 338 message += modelId + " -> "; 339 } 340 message += parentData.getId(); 341 342 problems.add( 343 new ModelProblemCollectorRequest( ModelProblem.Severity.FATAL, ModelProblem.Version.BASE ). 344 setMessage( message ) ); 345 346 throw problems.newModelBuildingException(); 347 } 348 else 349 { 350 currentData = parentData; 351 } 352 } 353 354 problems.setSource( inputModel ); 355 checkPluginVersions( lineage, request, problems ); 356 357 assembleInheritance( lineage, request, problems ); 358 359 Model resultModel = resultData.getModel(); 360 361 problems.setSource( resultModel ); 362 problems.setRootModel( resultModel ); 363 364 resultModel = interpolateModel( resultModel, request, problems ); 365 resultData.setModel( resultModel ); 366 367 modelUrlNormalizer.normalize( resultModel, request ); 368 369 //Now the fully interpolated model is available reconfigure the resolver 370 configureResolver( request.getModelResolver(), resultModel, problems , true ); 371 372 resultData.setGroupId( resultModel.getGroupId() ); 373 resultData.setArtifactId( resultModel.getArtifactId() ); 374 resultData.setVersion( resultModel.getVersion() ); 375 376 result.setEffectiveModel( resultModel ); 377 378 for ( ModelData currentData : lineage ) 379 { 380 String modelId = ( currentData != superData ) ? currentData.getId() : ""; 381 382 result.addModelId( modelId ); 383 result.setActivePomProfiles( modelId, currentData.getActiveProfiles() ); 384 result.setRawModel( modelId, currentData.getRawModel() ); 385 } 386 387 if ( !request.isTwoPhaseBuilding() ) 388 { 389 build( request, result ); 390 } 391 392 return result; 393 } 394 395 public ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result ) 396 throws ModelBuildingException 397 { 398 return build( request, result, new LinkedHashSet<String>() ); 399 } 400 401 private ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result, 402 Collection<String> imports ) 403 throws ModelBuildingException 404 { 405 Model resultModel = result.getEffectiveModel(); 406 407 DefaultModelProblemCollector problems = new DefaultModelProblemCollector( result ); 408 problems.setSource( resultModel ); 409 problems.setRootModel( resultModel ); 410 411 modelPathTranslator.alignToBaseDirectory( resultModel, resultModel.getProjectDirectory(), request ); 412 413 pluginManagementInjector.injectManagement( resultModel, request, problems ); 414 415 fireEvent( resultModel, request, problems, ModelBuildingEventCatapult.BUILD_EXTENSIONS_ASSEMBLED ); 416 417 if ( request.isProcessPlugins() ) 418 { 419 if ( lifecycleBindingsInjector == null ) 420 { 421 throw new IllegalStateException( "lifecycle bindings injector is missing" ); 422 } 423 424 lifecycleBindingsInjector.injectLifecycleBindings( resultModel, request, problems ); 425 } 426 427 importDependencyManagement( resultModel, request, problems, imports ); 428 429 dependencyManagementInjector.injectManagement( resultModel, request, problems ); 430 431 modelNormalizer.injectDefaultValues( resultModel, request, problems ); 432 433 if ( request.isProcessPlugins() ) 434 { 435 reportConfigurationExpander.expandPluginConfiguration( resultModel, request, problems ); 436 437 reportingConverter.convertReporting( resultModel, request, problems ); 438 439 pluginConfigurationExpander.expandPluginConfiguration( resultModel, request, problems ); 440 } 441 442 modelValidator.validateEffectiveModel( resultModel, request, problems ); 443 444 if ( hasModelErrors( problems ) ) 445 { 446 throw problems.newModelBuildingException(); 447 } 448 449 return result; 450 } 451 452 private Model readModel( ModelSource modelSource, File pomFile, ModelBuildingRequest request, 453 DefaultModelProblemCollector problems ) 454 throws ModelBuildingException 455 { 456 Model model; 457 458 if ( modelSource == null ) 459 { 460 if ( pomFile != null ) 461 { 462 modelSource = new FileModelSource( pomFile ); 463 } 464 else 465 { 466 throw new IllegalArgumentException( "neither model source nor input file are specified" ); 467 } 468 } 469 470 problems.setSource( modelSource.getLocation() ); 471 try 472 { 473 boolean strict = request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0; 474 InputSource source = request.isLocationTracking() ? new InputSource() : null; 475 476 Map<String, Object> options = new HashMap<String, Object>(); 477 options.put( ModelProcessor.IS_STRICT, strict ); 478 options.put( ModelProcessor.INPUT_SOURCE, source ); 479 options.put( ModelProcessor.SOURCE, modelSource ); 480 481 try 482 { 483 model = modelProcessor.read( modelSource.getInputStream(), options ); 484 } 485 catch ( ModelParseException e ) 486 { 487 if ( !strict ) 488 { 489 throw e; 490 } 491 492 options.put( ModelProcessor.IS_STRICT, Boolean.FALSE ); 493 494 try 495 { 496 model = modelProcessor.read( modelSource.getInputStream(), options ); 497 } 498 catch ( ModelParseException ne ) 499 { 500 // still unreadable even in non-strict mode, rethrow original error 501 throw e; 502 } 503 504 if ( pomFile != null ) 505 { 506 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.V20 ) 507 .setMessage( "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage() ) 508 .setException( e ) ); 509 } 510 else 511 { 512 problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.V20 ) 513 .setMessage( "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage() ) 514 .setException( e ) ); 515 } 516 } 517 518 if ( source != null ) 519 { 520 source.setModelId( ModelProblemUtils.toId( model ) ); 521 source.setLocation( modelSource.getLocation() ); 522 } 523 } 524 catch ( ModelParseException e ) 525 { 526 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) 527 .setMessage( "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage() ) 528 .setException( e ) ); 529 throw problems.newModelBuildingException(); 530 } 531 catch ( IOException e ) 532 { 533 String msg = e.getMessage(); 534 if ( msg == null || msg.length() <= 0 ) 535 { 536 // NOTE: There's java.nio.charset.MalformedInputException and sun.io.MalformedInputException 537 if ( e.getClass().getName().endsWith( "MalformedInputException" ) ) 538 { 539 msg = "Some input bytes do not match the file encoding."; 540 } 541 else 542 { 543 msg = e.getClass().getSimpleName(); 544 } 545 } 546 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) 547 .setMessage( "Non-readable POM " + modelSource.getLocation() + ": " + msg ) 548 .setException( e ) ); 549 throw problems.newModelBuildingException(); 550 } 551 552 model.setPomFile( pomFile ); 553 554 problems.setSource( model ); 555 modelValidator.validateRawModel( model, request, problems ); 556 557 if ( hasFatalErrors( problems ) ) 558 { 559 throw problems.newModelBuildingException(); 560 } 561 562 return model; 563 } 564 565 private DefaultProfileActivationContext getProfileActivationContext( ModelBuildingRequest request ) 566 { 567 DefaultProfileActivationContext context = new DefaultProfileActivationContext(); 568 569 context.setActiveProfileIds( request.getActiveProfileIds() ); 570 context.setInactiveProfileIds( request.getInactiveProfileIds() ); 571 context.setSystemProperties( request.getSystemProperties() ); 572 context.setUserProperties( request.getUserProperties() ); 573 context.setProjectDirectory( ( request.getPomFile() != null ) ? request.getPomFile().getParentFile() : null ); 574 575 return context; 576 } 577 578 private void configureResolver( ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems ) 579 { 580 configureResolver( modelResolver, model, problems, false ); 581 } 582 583 private void configureResolver( ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems, boolean resetRepositories ) 584 { 585 if ( modelResolver == null ) 586 { 587 return; 588 } 589 590 problems.setSource( model ); 591 592 List<Repository> repositories = model.getRepositories(); 593 594 if ( resetRepositories ) 595 { 596 modelResolver.resetRepositories(); 597 } 598 599 for ( Repository repository : repositories ) 600 { 601 try 602 { 603 modelResolver.addRepository( repository ); 604 } 605 catch ( InvalidRepositoryException e ) 606 { 607 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 608 .setMessage( "Invalid repository " + repository.getId() + ": " + e.getMessage() ) 609 .setLocation( repository.getLocation( "" ) ) 610 .setException( e ) ); 611 } 612 } 613 } 614 615 private void checkPluginVersions( List<ModelData> lineage, ModelBuildingRequest request, 616 ModelProblemCollector problems ) 617 { 618 if ( request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 ) 619 { 620 return; 621 } 622 623 Map<String, Plugin> plugins = new HashMap<String, Plugin>(); 624 Map<String, String> versions = new HashMap<String, String>(); 625 Map<String, String> managedVersions = new HashMap<String, String>(); 626 627 for ( int i = lineage.size() - 1; i >= 0; i-- ) 628 { 629 Model model = lineage.get( i ).getModel(); 630 Build build = model.getBuild(); 631 if ( build != null ) 632 { 633 for ( Plugin plugin : build.getPlugins() ) 634 { 635 String key = plugin.getKey(); 636 if ( versions.get( key ) == null ) 637 { 638 versions.put( key, plugin.getVersion() ); 639 plugins.put( key, plugin ); 640 } 641 } 642 PluginManagement mngt = build.getPluginManagement(); 643 if ( mngt != null ) 644 { 645 for ( Plugin plugin : mngt.getPlugins() ) 646 { 647 String key = plugin.getKey(); 648 if ( managedVersions.get( key ) == null ) 649 { 650 managedVersions.put( key, plugin.getVersion() ); 651 } 652 } 653 } 654 } 655 } 656 657 for ( String key : versions.keySet() ) 658 { 659 if ( versions.get( key ) == null && managedVersions.get( key ) == null ) 660 { 661 InputLocation location = plugins.get( key ).getLocation( "" ); 662 problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.V20 ) 663 .setMessage( "'build.plugins.plugin.version' for " + key + " is missing." ) 664 .setLocation( location ) ); 665 } 666 } 667 } 668 669 private void assembleInheritance( List<ModelData> lineage, ModelBuildingRequest request, 670 ModelProblemCollector problems ) 671 { 672 for ( int i = lineage.size() - 2; i >= 0; i-- ) 673 { 674 Model parent = lineage.get( i + 1 ).getModel(); 675 Model child = lineage.get( i ).getModel(); 676 inheritanceAssembler.assembleModelInheritance( child, parent, request, problems ); 677 } 678 } 679 680 private Map<String, Activation> getProfileActivations( Model model, boolean clone ) 681 { 682 Map<String, Activation> activations = new HashMap<String, Activation>(); 683 for ( Profile profile : model.getProfiles() ) 684 { 685 Activation activation = profile.getActivation(); 686 687 if ( activation == null ) 688 { 689 continue; 690 } 691 692 if ( clone ) 693 { 694 activation = activation.clone(); 695 } 696 697 activations.put( profile.getId(), activation ); 698 } 699 700 return activations; 701 } 702 703 private void injectProfileActivations( Model model, Map<String, Activation> activations ) 704 { 705 for ( Profile profile : model.getProfiles() ) 706 { 707 Activation activation = profile.getActivation(); 708 709 if ( activation == null ) 710 { 711 continue; 712 } 713 714 // restore activation 715 profile.setActivation( activations.get( profile.getId() ) ); 716 } 717 } 718 719 private Model interpolateModel( Model model, ModelBuildingRequest request, ModelProblemCollector problems ) 720 { 721 // save profile activations before interpolation, since they are evaluated with limited scope 722 Map<String, Activation> originalActivations = getProfileActivations( model, true ); 723 724 Model result = modelInterpolator.interpolateModel( model, model.getProjectDirectory(), request, problems ); 725 result.setPomFile( model.getPomFile() ); 726 727 // restore profiles with file activation to their value before full interpolation 728 injectProfileActivations( model, originalActivations ); 729 730 return result; 731 } 732 733 private ModelData readParent( Model childModel, ModelSource childSource, ModelBuildingRequest request, 734 DefaultModelProblemCollector problems ) 735 throws ModelBuildingException 736 { 737 ModelData parentData; 738 739 Parent parent = childModel.getParent(); 740 741 if ( parent != null ) 742 { 743 String groupId = parent.getGroupId(); 744 String artifactId = parent.getArtifactId(); 745 String version = parent.getVersion(); 746 747 parentData = getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW ); 748 749 if ( parentData == null ) 750 { 751 parentData = readParentLocally( childModel, childSource, request, problems ); 752 753 if ( parentData == null ) 754 { 755 parentData = readParentExternally( childModel, request, problems ); 756 } 757 758 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW, parentData ); 759 } 760 else 761 { 762 /* 763 * NOTE: This is a sanity check of the cache hit. If the cached parent POM was locally resolved, the 764 * child's <relativePath> should point at that parent, too. If it doesn't, we ignore the cache and 765 * resolve externally, to mimic the behavior if the cache didn't exist in the first place. Otherwise, 766 * the cache would obscure a bad POM. 767 */ 768 769 File pomFile = parentData.getModel().getPomFile(); 770 if ( pomFile != null ) 771 { 772 ModelSource expectedParentSource = getParentPomFile( childModel, childSource ); 773 774 if ( expectedParentSource instanceof ModelSource2 775 && !pomFile.toURI().equals( ( (ModelSource2) expectedParentSource ).getLocationURI() ) ) 776 { 777 parentData = readParentExternally( childModel, request, problems ); 778 } 779 } 780 } 781 782 Model parentModel = parentData.getModel(); 783 784 if ( !"pom".equals( parentModel.getPackaging() ) ) 785 { 786 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 787 .setMessage( "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint( parentModel ) 788 + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"" ) 789 .setLocation( parentModel.getLocation( "packaging" ) ) ); 790 } 791 } 792 else 793 { 794 parentData = null; 795 } 796 797 return parentData; 798 } 799 800 private ModelData readParentLocally( Model childModel, ModelSource childSource, ModelBuildingRequest request, 801 DefaultModelProblemCollector problems ) 802 throws ModelBuildingException 803 { 804 ModelSource candidateSource = getParentPomFile( childModel, childSource ); 805 806 if ( candidateSource == null ) 807 { 808 return null; 809 } 810 811 File pomFile = null; 812 if ( candidateSource instanceof FileModelSource ) 813 { 814 pomFile = ( (FileModelSource) candidateSource ).getPomFile(); 815 } 816 817 Model candidateModel = readModel( candidateSource, pomFile, request, problems ); 818 819 String groupId = candidateModel.getGroupId(); 820 if ( groupId == null && candidateModel.getParent() != null ) 821 { 822 groupId = candidateModel.getParent().getGroupId(); 823 } 824 String artifactId = candidateModel.getArtifactId(); 825 String version = candidateModel.getVersion(); 826 if ( version == null && candidateModel.getParent() != null ) 827 { 828 version = candidateModel.getParent().getVersion(); 829 } 830 831 Parent parent = childModel.getParent(); 832 833 if ( groupId == null || !groupId.equals( parent.getGroupId() ) || artifactId == null 834 || !artifactId.equals( parent.getArtifactId() ) ) 835 { 836 StringBuilder buffer = new StringBuilder( 256 ); 837 buffer.append( "'parent.relativePath'" ); 838 if ( childModel != problems.getRootModel() ) 839 { 840 buffer.append( " of POM " ).append( ModelProblemUtils.toSourceHint( childModel ) ); 841 } 842 buffer.append( " points at " ).append( groupId ).append( ":" ).append( artifactId ); 843 buffer.append( " instead of " ).append( parent.getGroupId() ).append( ":" ).append( parent.getArtifactId() ); 844 buffer.append( ", please verify your project structure" ); 845 846 problems.setSource( childModel ); 847 problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.BASE ) 848 .setMessage( buffer.toString() ) 849 .setLocation( parent.getLocation( "" ) ) ); 850 return null; 851 } 852 if ( version == null || !version.equals( parent.getVersion() ) ) 853 { 854 return null; 855 } 856 857 ModelData parentData = new ModelData( candidateSource, candidateModel, groupId, artifactId, version ); 858 859 return parentData; 860 } 861 862 private ModelSource getParentPomFile( Model childModel, ModelSource source ) 863 { 864 if ( !( source instanceof ModelSource2 ) ) 865 { 866 return null; 867 } 868 869 String parentPath = childModel.getParent().getRelativePath(); 870 871 if ( parentPath == null || parentPath.length() <= 0 ) 872 { 873 return null; 874 } 875 876 return ( (ModelSource2) source ).getRelatedSource( parentPath ); 877 } 878 879 private ModelData readParentExternally( Model childModel, ModelBuildingRequest request, 880 DefaultModelProblemCollector problems ) 881 throws ModelBuildingException 882 { 883 problems.setSource( childModel ); 884 885 Parent parent = childModel.getParent().clone(); 886 887 String groupId = parent.getGroupId(); 888 String artifactId = parent.getArtifactId(); 889 String version = parent.getVersion(); 890 891 ModelResolver modelResolver = request.getModelResolver(); 892 893 if ( modelResolver == null ) 894 { 895 throw new IllegalArgumentException( "no model resolver provided, cannot resolve parent POM " 896 + ModelProblemUtils.toId( groupId, artifactId, version ) + " for POM " 897 + ModelProblemUtils.toSourceHint( childModel ) ); 898 } 899 900 ModelSource modelSource; 901 try 902 { 903 modelSource = modelResolver.resolveModel( parent ); 904 } 905 catch ( UnresolvableModelException e ) 906 { 907 StringBuilder buffer = new StringBuilder( 256 ); 908 buffer.append( "Non-resolvable parent POM" ); 909 if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) ) 910 { 911 buffer.append( " " ).append( ModelProblemUtils.toId( groupId, artifactId, version ) ); 912 } 913 if ( childModel != problems.getRootModel() ) 914 { 915 buffer.append( " for " ).append( ModelProblemUtils.toId( childModel ) ); 916 } 917 buffer.append( ": " ).append( e.getMessage() ); 918 if ( childModel.getProjectDirectory() != null ) 919 { 920 if ( parent.getRelativePath() == null || parent.getRelativePath().length() <= 0 ) 921 { 922 buffer.append( " and 'parent.relativePath' points at no local POM" ); 923 } 924 else 925 { 926 buffer.append( " and 'parent.relativePath' points at wrong local POM" ); 927 } 928 } 929 930 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) 931 .setMessage( buffer.toString() ) 932 .setLocation( parent.getLocation( "" ) ) 933 .setException( e ) ); 934 throw problems.newModelBuildingException(); 935 } 936 937 ModelBuildingRequest lenientRequest = request; 938 if ( request.getValidationLevel() > ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 ) 939 { 940 lenientRequest = new FilterModelBuildingRequest( request ) 941 { 942 @Override 943 public int getValidationLevel() 944 { 945 return ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0; 946 } 947 }; 948 } 949 950 Model parentModel = readModel( modelSource, null, lenientRequest, problems ); 951 952 if ( !parent.getVersion().equals( version ) ) 953 { 954 if ( childModel.getVersion() == null ) 955 { 956 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ). 957 setMessage( "Version must be a constant" ). 958 setLocation( childModel.getLocation( "" ) ) ); 959 960 } 961 else 962 { 963 if ( childModel.getVersion().indexOf( "${" ) > -1 ) 964 { 965 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ). 966 setMessage( "Version must be a constant" ). 967 setLocation( childModel.getLocation( "version" ) ) ); 968 969 } 970 } 971 972 // MNG-2199: What else to check here ? 973 } 974 975 ModelData parentData = new ModelData( modelSource, parentModel, parent.getGroupId(), parent.getArtifactId(), 976 parent.getVersion() ); 977 978 return parentData; 979 } 980 981 private Model getSuperModel() 982 { 983 return superPomProvider.getSuperModel( "4.0.0" ).clone(); 984 } 985 986 private void importDependencyManagement( Model model, ModelBuildingRequest request, 987 DefaultModelProblemCollector problems, Collection<String> importIds ) 988 { 989 DependencyManagement depMngt = model.getDependencyManagement(); 990 991 if ( depMngt == null ) 992 { 993 return; 994 } 995 996 String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion(); 997 998 importIds.add( importing ); 999 1000 ModelResolver modelResolver = request.getModelResolver(); 1001 1002 ModelBuildingRequest importRequest = null; 1003 1004 List<DependencyManagement> importMngts = null; 1005 1006 for ( Iterator<Dependency> it = depMngt.getDependencies().iterator(); it.hasNext(); ) 1007 { 1008 Dependency dependency = it.next(); 1009 1010 if ( !"pom".equals( dependency.getType() ) || !"import".equals( dependency.getScope() ) ) 1011 { 1012 continue; 1013 } 1014 1015 it.remove(); 1016 1017 String groupId = dependency.getGroupId(); 1018 String artifactId = dependency.getArtifactId(); 1019 String version = dependency.getVersion(); 1020 1021 if ( groupId == null || groupId.length() <= 0 ) 1022 { 1023 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 1024 .setMessage( "'dependencyManagement.dependencies.dependency.groupId' for " 1025 + dependency.getManagementKey() + " is missing." ) 1026 .setLocation( dependency.getLocation( "" ) ) ); 1027 continue; 1028 } 1029 if ( artifactId == null || artifactId.length() <= 0 ) 1030 { 1031 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 1032 .setMessage( "'dependencyManagement.dependencies.dependency.artifactId' for " 1033 + dependency.getManagementKey() + " is missing." ) 1034 .setLocation( dependency.getLocation( "" ) ) ); 1035 continue; 1036 } 1037 if ( version == null || version.length() <= 0 ) 1038 { 1039 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 1040 .setMessage( "'dependencyManagement.dependencies.dependency.version' for " 1041 + dependency.getManagementKey() + " is missing." ) 1042 .setLocation( dependency.getLocation( "" ) ) ); 1043 continue; 1044 } 1045 1046 String imported = groupId + ':' + artifactId + ':' + version; 1047 1048 if ( importIds.contains( imported ) ) 1049 { 1050 String message = "The dependencies of type=pom and with scope=import form a cycle: "; 1051 for ( String modelId : importIds ) 1052 { 1053 message += modelId + " -> "; 1054 } 1055 message += imported; 1056 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage( message ) ); 1057 1058 continue; 1059 } 1060 1061 DependencyManagement importMngt = 1062 getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT ); 1063 1064 if ( importMngt == null ) 1065 { 1066 if ( modelResolver == null ) 1067 { 1068 throw new IllegalArgumentException( "no model resolver provided, cannot resolve import POM " 1069 + ModelProblemUtils.toId( groupId, artifactId, version ) + " for POM " 1070 + ModelProblemUtils.toSourceHint( model ) ); 1071 } 1072 1073 ModelSource importSource; 1074 try 1075 { 1076 importSource = modelResolver.resolveModel( groupId, artifactId, version ); 1077 } 1078 catch ( UnresolvableModelException e ) 1079 { 1080 StringBuilder buffer = new StringBuilder( 256 ); 1081 buffer.append( "Non-resolvable import POM" ); 1082 if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) ) 1083 { 1084 buffer.append( " " ).append( ModelProblemUtils.toId( groupId, artifactId, version ) ); 1085 } 1086 buffer.append( ": " ).append( e.getMessage() ); 1087 1088 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 1089 .setMessage( buffer.toString() ) 1090 .setLocation( dependency.getLocation( "" ) ) 1091 .setException( e ) ); 1092 continue; 1093 } 1094 1095 if ( importRequest == null ) 1096 { 1097 importRequest = new DefaultModelBuildingRequest(); 1098 importRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL ); 1099 importRequest.setModelCache( request.getModelCache() ); 1100 importRequest.setSystemProperties( request.getSystemProperties() ); 1101 importRequest.setUserProperties( request.getUserProperties() ); 1102 importRequest.setLocationTracking( request.isLocationTracking() ); 1103 } 1104 1105 importRequest.setModelSource( importSource ); 1106 importRequest.setModelResolver( modelResolver.newCopy() ); 1107 1108 ModelBuildingResult importResult; 1109 try 1110 { 1111 importResult = build( importRequest ); 1112 } 1113 catch ( ModelBuildingException e ) 1114 { 1115 problems.addAll( e.getProblems() ); 1116 continue; 1117 } 1118 1119 problems.addAll( importResult.getProblems() ); 1120 1121 Model importModel = importResult.getEffectiveModel(); 1122 1123 importMngt = importModel.getDependencyManagement(); 1124 1125 if ( importMngt == null ) 1126 { 1127 importMngt = new DependencyManagement(); 1128 } 1129 1130 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, importMngt ); 1131 } 1132 1133 if ( importMngts == null ) 1134 { 1135 importMngts = new ArrayList<DependencyManagement>(); 1136 } 1137 1138 importMngts.add( importMngt ); 1139 } 1140 1141 importIds.remove( importing ); 1142 1143 dependencyManagementImporter.importManagement( model, importMngts, request, problems ); 1144 } 1145 1146 private <T> void putCache( ModelCache modelCache, String groupId, String artifactId, String version, 1147 ModelCacheTag<T> tag, T data ) 1148 { 1149 if ( modelCache != null ) 1150 { 1151 modelCache.put( groupId, artifactId, version, tag.getName(), tag.intoCache( data ) ); 1152 } 1153 } 1154 1155 private <T> T getCache( ModelCache modelCache, String groupId, String artifactId, String version, 1156 ModelCacheTag<T> tag ) 1157 { 1158 if ( modelCache != null ) 1159 { 1160 Object data = modelCache.get( groupId, artifactId, version, tag.getName() ); 1161 if ( data != null ) 1162 { 1163 return tag.fromCache( tag.getType().cast( data ) ); 1164 } 1165 } 1166 return null; 1167 } 1168 1169 private void fireEvent( Model model, ModelBuildingRequest request, ModelProblemCollector problems, 1170 ModelBuildingEventCatapult catapult ) 1171 throws ModelBuildingException 1172 { 1173 ModelBuildingListener listener = request.getModelBuildingListener(); 1174 1175 if ( listener != null ) 1176 { 1177 ModelBuildingEvent event = new DefaultModelBuildingEvent( model, request, problems ); 1178 1179 catapult.fire( listener, event ); 1180 } 1181 } 1182 1183 private boolean containsCoordinates( String message, String groupId, String artifactId, String version ) 1184 { 1185 return message != null && ( groupId == null || message.contains( groupId ) ) 1186 && ( artifactId == null || message.contains( artifactId ) ) 1187 && ( version == null || message.contains( version ) ); 1188 } 1189 1190 protected boolean hasModelErrors( ModelProblemCollectorExt problems ) 1191 { 1192 if ( problems instanceof DefaultModelProblemCollector ) 1193 { 1194 return ( (DefaultModelProblemCollector) problems ).hasErrors(); 1195 } 1196 else 1197 { 1198 // the default execution path only knows the DefaultModelProblemCollector, 1199 // only reason it's not in signature is because it's package private 1200 throw new IllegalStateException(); 1201 } 1202 } 1203 1204 protected boolean hasFatalErrors( ModelProblemCollectorExt problems ) 1205 { 1206 if ( problems instanceof DefaultModelProblemCollector ) 1207 { 1208 return ( (DefaultModelProblemCollector) problems ).hasFatalErrors(); 1209 } 1210 else 1211 { 1212 // the default execution path only knows the DefaultModelProblemCollector, 1213 // only reason it's not in signature is because it's package private 1214 throw new IllegalStateException(); 1215 } 1216 } 1217 1218}