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, 584 boolean replaceRepositories ) 585 { 586 if ( modelResolver == null ) 587 { 588 return; 589 } 590 591 problems.setSource( model ); 592 593 List<Repository> repositories = model.getRepositories(); 594 595 for ( Repository repository : repositories ) 596 { 597 try 598 { 599 modelResolver.addRepository( repository, replaceRepositories ); 600 } 601 catch ( InvalidRepositoryException e ) 602 { 603 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 604 .setMessage( "Invalid repository " + repository.getId() + ": " + e.getMessage() ) 605 .setLocation( repository.getLocation( "" ) ) 606 .setException( e ) ); 607 } 608 } 609 } 610 611 private void checkPluginVersions( List<ModelData> lineage, ModelBuildingRequest request, 612 ModelProblemCollector problems ) 613 { 614 if ( request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 ) 615 { 616 return; 617 } 618 619 Map<String, Plugin> plugins = new HashMap<String, Plugin>(); 620 Map<String, String> versions = new HashMap<String, String>(); 621 Map<String, String> managedVersions = new HashMap<String, String>(); 622 623 for ( int i = lineage.size() - 1; i >= 0; i-- ) 624 { 625 Model model = lineage.get( i ).getModel(); 626 Build build = model.getBuild(); 627 if ( build != null ) 628 { 629 for ( Plugin plugin : build.getPlugins() ) 630 { 631 String key = plugin.getKey(); 632 if ( versions.get( key ) == null ) 633 { 634 versions.put( key, plugin.getVersion() ); 635 plugins.put( key, plugin ); 636 } 637 } 638 PluginManagement mngt = build.getPluginManagement(); 639 if ( mngt != null ) 640 { 641 for ( Plugin plugin : mngt.getPlugins() ) 642 { 643 String key = plugin.getKey(); 644 if ( managedVersions.get( key ) == null ) 645 { 646 managedVersions.put( key, plugin.getVersion() ); 647 } 648 } 649 } 650 } 651 } 652 653 for ( String key : versions.keySet() ) 654 { 655 if ( versions.get( key ) == null && managedVersions.get( key ) == null ) 656 { 657 InputLocation location = plugins.get( key ).getLocation( "" ); 658 problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.V20 ) 659 .setMessage( "'build.plugins.plugin.version' for " + key + " is missing." ) 660 .setLocation( location ) ); 661 } 662 } 663 } 664 665 private void assembleInheritance( List<ModelData> lineage, ModelBuildingRequest request, 666 ModelProblemCollector problems ) 667 { 668 for ( int i = lineage.size() - 2; i >= 0; i-- ) 669 { 670 Model parent = lineage.get( i + 1 ).getModel(); 671 Model child = lineage.get( i ).getModel(); 672 inheritanceAssembler.assembleModelInheritance( child, parent, request, problems ); 673 } 674 } 675 676 private Map<String, Activation> getProfileActivations( Model model, boolean clone ) 677 { 678 Map<String, Activation> activations = new HashMap<String, Activation>(); 679 for ( Profile profile : model.getProfiles() ) 680 { 681 Activation activation = profile.getActivation(); 682 683 if ( activation == null ) 684 { 685 continue; 686 } 687 688 if ( clone ) 689 { 690 activation = activation.clone(); 691 } 692 693 activations.put( profile.getId(), activation ); 694 } 695 696 return activations; 697 } 698 699 private void injectProfileActivations( Model model, Map<String, Activation> activations ) 700 { 701 for ( Profile profile : model.getProfiles() ) 702 { 703 Activation activation = profile.getActivation(); 704 705 if ( activation == null ) 706 { 707 continue; 708 } 709 710 // restore activation 711 profile.setActivation( activations.get( profile.getId() ) ); 712 } 713 } 714 715 private Model interpolateModel( Model model, ModelBuildingRequest request, ModelProblemCollector problems ) 716 { 717 // save profile activations before interpolation, since they are evaluated with limited scope 718 Map<String, Activation> originalActivations = getProfileActivations( model, true ); 719 720 Model result = modelInterpolator.interpolateModel( model, model.getProjectDirectory(), request, problems ); 721 result.setPomFile( model.getPomFile() ); 722 723 // restore profiles with file activation to their value before full interpolation 724 injectProfileActivations( model, originalActivations ); 725 726 return result; 727 } 728 729 private ModelData readParent( Model childModel, ModelSource childSource, ModelBuildingRequest request, 730 DefaultModelProblemCollector problems ) 731 throws ModelBuildingException 732 { 733 ModelData parentData; 734 735 Parent parent = childModel.getParent(); 736 737 if ( parent != null ) 738 { 739 String groupId = parent.getGroupId(); 740 String artifactId = parent.getArtifactId(); 741 String version = parent.getVersion(); 742 743 parentData = getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW ); 744 745 if ( parentData == null ) 746 { 747 parentData = readParentLocally( childModel, childSource, request, problems ); 748 749 if ( parentData == null ) 750 { 751 parentData = readParentExternally( childModel, request, problems ); 752 } 753 754 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW, parentData ); 755 } 756 else 757 { 758 /* 759 * NOTE: This is a sanity check of the cache hit. If the cached parent POM was locally resolved, the 760 * child's <relativePath> should point at that parent, too. If it doesn't, we ignore the cache and 761 * resolve externally, to mimic the behavior if the cache didn't exist in the first place. Otherwise, 762 * the cache would obscure a bad POM. 763 */ 764 765 File pomFile = parentData.getModel().getPomFile(); 766 if ( pomFile != null ) 767 { 768 ModelSource expectedParentSource = getParentPomFile( childModel, childSource ); 769 770 if ( expectedParentSource instanceof ModelSource2 771 && !pomFile.toURI().equals( ( (ModelSource2) expectedParentSource ).getLocationURI() ) ) 772 { 773 parentData = readParentExternally( childModel, request, problems ); 774 } 775 } 776 } 777 778 Model parentModel = parentData.getModel(); 779 780 if ( !"pom".equals( parentModel.getPackaging() ) ) 781 { 782 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 783 .setMessage( "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint( parentModel ) 784 + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"" ) 785 .setLocation( parentModel.getLocation( "packaging" ) ) ); 786 } 787 } 788 else 789 { 790 parentData = null; 791 } 792 793 return parentData; 794 } 795 796 private ModelData readParentLocally( Model childModel, ModelSource childSource, ModelBuildingRequest request, 797 DefaultModelProblemCollector problems ) 798 throws ModelBuildingException 799 { 800 ModelSource candidateSource = getParentPomFile( childModel, childSource ); 801 802 if ( candidateSource == null ) 803 { 804 return null; 805 } 806 807 File pomFile = null; 808 if ( candidateSource instanceof FileModelSource ) 809 { 810 pomFile = ( (FileModelSource) candidateSource ).getPomFile(); 811 } 812 813 Model candidateModel = readModel( candidateSource, pomFile, request, problems ); 814 815 String groupId = candidateModel.getGroupId(); 816 if ( groupId == null && candidateModel.getParent() != null ) 817 { 818 groupId = candidateModel.getParent().getGroupId(); 819 } 820 String artifactId = candidateModel.getArtifactId(); 821 String version = candidateModel.getVersion(); 822 if ( version == null && candidateModel.getParent() != null ) 823 { 824 version = candidateModel.getParent().getVersion(); 825 } 826 827 Parent parent = childModel.getParent(); 828 829 if ( groupId == null || !groupId.equals( parent.getGroupId() ) || artifactId == null 830 || !artifactId.equals( parent.getArtifactId() ) ) 831 { 832 StringBuilder buffer = new StringBuilder( 256 ); 833 buffer.append( "'parent.relativePath'" ); 834 if ( childModel != problems.getRootModel() ) 835 { 836 buffer.append( " of POM " ).append( ModelProblemUtils.toSourceHint( childModel ) ); 837 } 838 buffer.append( " points at " ).append( groupId ).append( ":" ).append( artifactId ); 839 buffer.append( " instead of " ).append( parent.getGroupId() ).append( ":" ); 840 buffer.append( parent.getArtifactId() ).append( ", please verify your project structure" ); 841 842 problems.setSource( childModel ); 843 problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.BASE ) 844 .setMessage( buffer.toString() ) 845 .setLocation( parent.getLocation( "" ) ) ); 846 return null; 847 } 848 if ( version == null || !version.equals( parent.getVersion() ) ) 849 { 850 return null; 851 } 852 853 ModelData parentData = new ModelData( candidateSource, candidateModel, groupId, artifactId, version ); 854 855 return parentData; 856 } 857 858 private ModelSource getParentPomFile( Model childModel, ModelSource source ) 859 { 860 if ( !( source instanceof ModelSource2 ) ) 861 { 862 return null; 863 } 864 865 String parentPath = childModel.getParent().getRelativePath(); 866 867 if ( parentPath == null || parentPath.length() <= 0 ) 868 { 869 return null; 870 } 871 872 return ( (ModelSource2) source ).getRelatedSource( parentPath ); 873 } 874 875 private ModelData readParentExternally( Model childModel, ModelBuildingRequest request, 876 DefaultModelProblemCollector problems ) 877 throws ModelBuildingException 878 { 879 problems.setSource( childModel ); 880 881 Parent parent = childModel.getParent().clone(); 882 883 String groupId = parent.getGroupId(); 884 String artifactId = parent.getArtifactId(); 885 String version = parent.getVersion(); 886 887 ModelResolver modelResolver = request.getModelResolver(); 888 889 if ( modelResolver == null ) 890 { 891 throw new IllegalArgumentException( "no model resolver provided, cannot resolve parent POM " 892 + ModelProblemUtils.toId( groupId, artifactId, version ) + " for POM " 893 + ModelProblemUtils.toSourceHint( childModel ) ); 894 } 895 896 ModelSource modelSource; 897 try 898 { 899 modelSource = modelResolver.resolveModel( parent ); 900 } 901 catch ( UnresolvableModelException e ) 902 { 903 StringBuilder buffer = new StringBuilder( 256 ); 904 buffer.append( "Non-resolvable parent POM" ); 905 if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) ) 906 { 907 buffer.append( " " ).append( ModelProblemUtils.toId( groupId, artifactId, version ) ); 908 } 909 if ( childModel != problems.getRootModel() ) 910 { 911 buffer.append( " for " ).append( ModelProblemUtils.toId( childModel ) ); 912 } 913 buffer.append( ": " ).append( e.getMessage() ); 914 if ( childModel.getProjectDirectory() != null ) 915 { 916 if ( parent.getRelativePath() == null || parent.getRelativePath().length() <= 0 ) 917 { 918 buffer.append( " and 'parent.relativePath' points at no local POM" ); 919 } 920 else 921 { 922 buffer.append( " and 'parent.relativePath' points at wrong local POM" ); 923 } 924 } 925 926 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) 927 .setMessage( buffer.toString() ) 928 .setLocation( parent.getLocation( "" ) ) 929 .setException( e ) ); 930 throw problems.newModelBuildingException(); 931 } 932 933 ModelBuildingRequest lenientRequest = request; 934 if ( request.getValidationLevel() > ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 ) 935 { 936 lenientRequest = new FilterModelBuildingRequest( request ) 937 { 938 @Override 939 public int getValidationLevel() 940 { 941 return ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0; 942 } 943 }; 944 } 945 946 Model parentModel = readModel( modelSource, null, lenientRequest, problems ); 947 948 if ( !parent.getVersion().equals( version ) ) 949 { 950 if ( childModel.getVersion() == null ) 951 { 952 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ). 953 setMessage( "Version must be a constant" ). 954 setLocation( childModel.getLocation( "" ) ) ); 955 956 } 957 else 958 { 959 if ( childModel.getVersion().indexOf( "${" ) > -1 ) 960 { 961 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ). 962 setMessage( "Version must be a constant" ). 963 setLocation( childModel.getLocation( "version" ) ) ); 964 965 } 966 } 967 968 // MNG-2199: What else to check here ? 969 } 970 971 ModelData parentData = new ModelData( modelSource, parentModel, parent.getGroupId(), parent.getArtifactId(), 972 parent.getVersion() ); 973 974 return parentData; 975 } 976 977 private Model getSuperModel() 978 { 979 return superPomProvider.getSuperModel( "4.0.0" ).clone(); 980 } 981 982 private void importDependencyManagement( Model model, ModelBuildingRequest request, 983 DefaultModelProblemCollector problems, Collection<String> importIds ) 984 { 985 DependencyManagement depMngt = model.getDependencyManagement(); 986 987 if ( depMngt == null ) 988 { 989 return; 990 } 991 992 String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion(); 993 994 importIds.add( importing ); 995 996 ModelResolver modelResolver = request.getModelResolver(); 997 998 ModelBuildingRequest importRequest = null; 999 1000 List<DependencyManagement> importMngts = null; 1001 1002 for ( Iterator<Dependency> it = depMngt.getDependencies().iterator(); it.hasNext(); ) 1003 { 1004 Dependency dependency = it.next(); 1005 1006 if ( !"pom".equals( dependency.getType() ) || !"import".equals( dependency.getScope() ) ) 1007 { 1008 continue; 1009 } 1010 1011 it.remove(); 1012 1013 String groupId = dependency.getGroupId(); 1014 String artifactId = dependency.getArtifactId(); 1015 String version = dependency.getVersion(); 1016 1017 if ( groupId == null || groupId.length() <= 0 ) 1018 { 1019 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 1020 .setMessage( "'dependencyManagement.dependencies.dependency.groupId' for " 1021 + dependency.getManagementKey() + " is missing." ) 1022 .setLocation( dependency.getLocation( "" ) ) ); 1023 continue; 1024 } 1025 if ( artifactId == null || artifactId.length() <= 0 ) 1026 { 1027 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 1028 .setMessage( "'dependencyManagement.dependencies.dependency.artifactId' for " 1029 + dependency.getManagementKey() + " is missing." ) 1030 .setLocation( dependency.getLocation( "" ) ) ); 1031 continue; 1032 } 1033 if ( version == null || version.length() <= 0 ) 1034 { 1035 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 1036 .setMessage( "'dependencyManagement.dependencies.dependency.version' for " 1037 + dependency.getManagementKey() + " is missing." ) 1038 .setLocation( dependency.getLocation( "" ) ) ); 1039 continue; 1040 } 1041 1042 String imported = groupId + ':' + artifactId + ':' + version; 1043 1044 if ( importIds.contains( imported ) ) 1045 { 1046 String message = "The dependencies of type=pom and with scope=import form a cycle: "; 1047 for ( String modelId : importIds ) 1048 { 1049 message += modelId + " -> "; 1050 } 1051 message += imported; 1052 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage( message ) ); 1053 1054 continue; 1055 } 1056 1057 DependencyManagement importMngt = 1058 getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT ); 1059 1060 if ( importMngt == null ) 1061 { 1062 if ( modelResolver == null ) 1063 { 1064 throw new IllegalArgumentException( "no model resolver provided, cannot resolve import POM " 1065 + ModelProblemUtils.toId( groupId, artifactId, version ) + " for POM " 1066 + ModelProblemUtils.toSourceHint( model ) ); 1067 } 1068 1069 ModelSource importSource; 1070 try 1071 { 1072 importSource = modelResolver.resolveModel( groupId, artifactId, version ); 1073 } 1074 catch ( UnresolvableModelException e ) 1075 { 1076 StringBuilder buffer = new StringBuilder( 256 ); 1077 buffer.append( "Non-resolvable import POM" ); 1078 if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) ) 1079 { 1080 buffer.append( " " ).append( ModelProblemUtils.toId( groupId, artifactId, version ) ); 1081 } 1082 buffer.append( ": " ).append( e.getMessage() ); 1083 1084 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 1085 .setMessage( buffer.toString() ) 1086 .setLocation( dependency.getLocation( "" ) ) 1087 .setException( e ) ); 1088 continue; 1089 } 1090 1091 if ( importRequest == null ) 1092 { 1093 importRequest = new DefaultModelBuildingRequest(); 1094 importRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL ); 1095 importRequest.setModelCache( request.getModelCache() ); 1096 importRequest.setSystemProperties( request.getSystemProperties() ); 1097 importRequest.setUserProperties( request.getUserProperties() ); 1098 importRequest.setLocationTracking( request.isLocationTracking() ); 1099 } 1100 1101 importRequest.setModelSource( importSource ); 1102 importRequest.setModelResolver( modelResolver.newCopy() ); 1103 1104 ModelBuildingResult importResult; 1105 try 1106 { 1107 importResult = build( importRequest ); 1108 } 1109 catch ( ModelBuildingException e ) 1110 { 1111 problems.addAll( e.getProblems() ); 1112 continue; 1113 } 1114 1115 problems.addAll( importResult.getProblems() ); 1116 1117 Model importModel = importResult.getEffectiveModel(); 1118 1119 importMngt = importModel.getDependencyManagement(); 1120 1121 if ( importMngt == null ) 1122 { 1123 importMngt = new DependencyManagement(); 1124 } 1125 1126 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, importMngt ); 1127 } 1128 1129 if ( importMngts == null ) 1130 { 1131 importMngts = new ArrayList<DependencyManagement>(); 1132 } 1133 1134 importMngts.add( importMngt ); 1135 } 1136 1137 importIds.remove( importing ); 1138 1139 dependencyManagementImporter.importManagement( model, importMngts, request, problems ); 1140 } 1141 1142 private <T> void putCache( ModelCache modelCache, String groupId, String artifactId, String version, 1143 ModelCacheTag<T> tag, T data ) 1144 { 1145 if ( modelCache != null ) 1146 { 1147 modelCache.put( groupId, artifactId, version, tag.getName(), tag.intoCache( data ) ); 1148 } 1149 } 1150 1151 private <T> T getCache( ModelCache modelCache, String groupId, String artifactId, String version, 1152 ModelCacheTag<T> tag ) 1153 { 1154 if ( modelCache != null ) 1155 { 1156 Object data = modelCache.get( groupId, artifactId, version, tag.getName() ); 1157 if ( data != null ) 1158 { 1159 return tag.fromCache( tag.getType().cast( data ) ); 1160 } 1161 } 1162 return null; 1163 } 1164 1165 private void fireEvent( Model model, ModelBuildingRequest request, ModelProblemCollector problems, 1166 ModelBuildingEventCatapult catapult ) 1167 throws ModelBuildingException 1168 { 1169 ModelBuildingListener listener = request.getModelBuildingListener(); 1170 1171 if ( listener != null ) 1172 { 1173 ModelBuildingEvent event = new DefaultModelBuildingEvent( model, request, problems ); 1174 1175 catapult.fire( listener, event ); 1176 } 1177 } 1178 1179 private boolean containsCoordinates( String message, String groupId, String artifactId, String version ) 1180 { 1181 return message != null && ( groupId == null || message.contains( groupId ) ) 1182 && ( artifactId == null || message.contains( artifactId ) ) 1183 && ( version == null || message.contains( version ) ); 1184 } 1185 1186 protected boolean hasModelErrors( ModelProblemCollectorExt problems ) 1187 { 1188 if ( problems instanceof DefaultModelProblemCollector ) 1189 { 1190 return ( (DefaultModelProblemCollector) problems ).hasErrors(); 1191 } 1192 else 1193 { 1194 // the default execution path only knows the DefaultModelProblemCollector, 1195 // only reason it's not in signature is because it's package private 1196 throw new IllegalStateException(); 1197 } 1198 } 1199 1200 protected boolean hasFatalErrors( ModelProblemCollectorExt problems ) 1201 { 1202 if ( problems instanceof DefaultModelProblemCollector ) 1203 { 1204 return ( (DefaultModelProblemCollector) problems ).hasFatalErrors(); 1205 } 1206 else 1207 { 1208 // the default execution path only knows the DefaultModelProblemCollector, 1209 // only reason it's not in signature is because it's package private 1210 throw new IllegalStateException(); 1211 } 1212 } 1213 1214}