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