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