View Javadoc
1   package org.apache.maven.model.building;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  
23  import org.apache.commons.lang3.Validate;
24  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
25  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
26  import org.apache.maven.artifact.versioning.VersionRange;
27  import org.apache.maven.model.Activation;
28  import org.apache.maven.model.Build;
29  import org.apache.maven.model.Dependency;
30  import org.apache.maven.model.DependencyManagement;
31  import org.apache.maven.model.InputLocation;
32  import org.apache.maven.model.InputSource;
33  import org.apache.maven.model.Model;
34  import org.apache.maven.model.Parent;
35  import org.apache.maven.model.Plugin;
36  import org.apache.maven.model.PluginManagement;
37  import org.apache.maven.model.Profile;
38  import org.apache.maven.model.Repository;
39  import org.apache.maven.model.building.ModelProblem.Severity;
40  import org.apache.maven.model.building.ModelProblem.Version;
41  import org.apache.maven.model.composition.DependencyManagementImporter;
42  import org.apache.maven.model.inheritance.InheritanceAssembler;
43  import org.apache.maven.model.interpolation.ModelInterpolator;
44  import org.apache.maven.model.io.ModelParseException;
45  import org.apache.maven.model.management.DependencyManagementInjector;
46  import org.apache.maven.model.management.PluginManagementInjector;
47  import org.apache.maven.model.normalization.ModelNormalizer;
48  import org.apache.maven.model.path.ModelPathTranslator;
49  import org.apache.maven.model.path.ModelUrlNormalizer;
50  import org.apache.maven.model.plugin.LifecycleBindingsInjector;
51  import org.apache.maven.model.plugin.PluginConfigurationExpander;
52  import org.apache.maven.model.plugin.ReportConfigurationExpander;
53  import org.apache.maven.model.plugin.ReportingConverter;
54  import org.apache.maven.model.profile.DefaultProfileActivationContext;
55  import org.apache.maven.model.profile.ProfileInjector;
56  import org.apache.maven.model.profile.ProfileSelector;
57  import org.apache.maven.model.resolution.InvalidRepositoryException;
58  import org.apache.maven.model.resolution.ModelResolver;
59  import org.apache.maven.model.resolution.UnresolvableModelException;
60  import org.apache.maven.model.resolution.WorkspaceModelResolver;
61  import org.apache.maven.model.superpom.SuperPomProvider;
62  import org.apache.maven.model.validation.ModelValidator;
63  import org.codehaus.plexus.component.annotations.Component;
64  import org.codehaus.plexus.component.annotations.Requirement;
65  
66  import java.io.File;
67  import java.io.IOException;
68  import java.util.ArrayList;
69  import java.util.Collection;
70  import java.util.HashMap;
71  import java.util.Iterator;
72  import java.util.LinkedHashSet;
73  import java.util.List;
74  import java.util.Map;
75  import java.util.Properties;
76  
77  import static org.apache.maven.model.building.Result.error;
78  import static org.apache.maven.model.building.Result.newResult;
79  
80  /**
81   * @author Benjamin Bentmann
82   */
83  @Component( role = ModelBuilder.class )
84  public class DefaultModelBuilder
85      implements ModelBuilder
86  {
87      @Requirement
88      private ModelProcessor modelProcessor;
89  
90      @Requirement
91      private ModelValidator modelValidator;
92  
93      @Requirement
94      private ModelNormalizer modelNormalizer;
95  
96      @Requirement
97      private ModelInterpolator modelInterpolator;
98  
99      @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 depMgmtImporter )
199     {
200         this.dependencyManagementImporter = depMgmtImporter;
201         return this;
202     }
203 
204     public DefaultModelBuilder setDependencyManagementInjector( DependencyManagementInjector depMgmtInjector )
205     {
206         this.dependencyManagementInjector = depMgmtInjector;
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 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 mgmt = build.getPluginManagement();
687                 if ( mgmt != null )
688                 {
689                     for ( Plugin plugin : mgmt.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                 // Validate versions aren't inherited when using parent ranges the same way as when read externally.
943                 if ( childModel.getVersion() == null )
944                 {
945                     // Message below is checked for in the MNG-2199 core IT.
946                     problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 )
947                         .setMessage( "Version must be a constant" ).setLocation( childModel.getLocation( "" ) ) );
948 
949                 }
950                 else
951                 {
952                     if ( childModel.getVersion().contains( "${" ) )
953                     {
954                         // Message below is checked for in the MNG-2199 core IT.
955                         problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 )
956                             .setMessage( "Version must be a constant" )
957                             .setLocation( childModel.getLocation( "version" ) ) );
958 
959                     }
960                 }
961 
962                 // MNG-2199: What else to check here ?
963             }
964             catch ( InvalidVersionSpecificationException e )
965             {
966                 // invalid version range, so drop back to resolution from the repository
967                 return null;
968             }
969         }
970 
971         //
972         // Here we just need to know that a version is fine to use but this validation we can do in our workspace
973         // resolver.
974         //
975 
976         /*
977          * if ( version == null || !version.equals( parent.getVersion() ) ) { return null; }
978          */
979 
980         ModelData parentData = new ModelData( candidateSource, candidateModel, groupId, artifactId, version );
981 
982         return parentData;
983     }
984 
985     private ModelSource getParentPomFile( Model childModel, ModelSource source )
986     {
987         if ( !( source instanceof ModelSource2 ) )
988         {
989             return null;
990         }
991 
992         String parentPath = childModel.getParent().getRelativePath();
993 
994         if ( parentPath == null || parentPath.length() <= 0 )
995         {
996             return null;
997         }
998 
999         return ( (ModelSource2) source ).getRelatedSource( parentPath );
1000     }
1001 
1002     private ModelData readParentExternally( Model childModel, ModelBuildingRequest request,
1003                                             DefaultModelProblemCollector problems )
1004         throws ModelBuildingException
1005     {
1006         problems.setSource( childModel );
1007 
1008         Parent parent = childModel.getParent().clone();
1009 
1010         String groupId = parent.getGroupId();
1011         String artifactId = parent.getArtifactId();
1012         String version = parent.getVersion();
1013 
1014         ModelResolver modelResolver = request.getModelResolver();
1015 
1016         Validate.notNull( modelResolver, "request.modelResolver cannot be null (parent POM %s and POM %s)",
1017             ModelProblemUtils.toId( groupId, artifactId, version ), ModelProblemUtils.toSourceHint( childModel ) );
1018 
1019         ModelSource modelSource;
1020         try
1021         {
1022             modelSource = modelResolver.resolveModel( parent );
1023         }
1024         catch ( UnresolvableModelException e )
1025         {
1026             // Message below is checked for in the MNG-2199 core IT.
1027             StringBuilder buffer = new StringBuilder( 256 );
1028             buffer.append( "Non-resolvable parent POM" );
1029             if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) )
1030             {
1031                 buffer.append( ' ' ).append( ModelProblemUtils.toId( groupId, artifactId, version ) );
1032             }
1033             if ( childModel != problems.getRootModel() )
1034             {
1035                 buffer.append( " for " ).append( ModelProblemUtils.toId( childModel ) );
1036             }
1037             buffer.append( ": " ).append( e.getMessage() );
1038             if ( childModel.getProjectDirectory() != null )
1039             {
1040                 if ( parent.getRelativePath() == null || parent.getRelativePath().length() <= 0 )
1041                 {
1042                     buffer.append( " and 'parent.relativePath' points at no local POM" );
1043                 }
1044                 else
1045                 {
1046                     buffer.append( " and 'parent.relativePath' points at wrong local POM" );
1047                 }
1048             }
1049 
1050             problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE )
1051                 .setMessage( buffer.toString() ).setLocation( parent.getLocation( "" ) ).setException( e ) );
1052             throw problems.newModelBuildingException();
1053         }
1054 
1055         ModelBuildingRequest lenientRequest = request;
1056         if ( request.getValidationLevel() > ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
1057         {
1058             lenientRequest = new FilterModelBuildingRequest( request )
1059             {
1060                 @Override
1061                 public int getValidationLevel()
1062                 {
1063                     return ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
1064                 }
1065             };
1066         }
1067 
1068         Model parentModel = readModel( modelSource, null, lenientRequest, problems );
1069 
1070         if ( !parent.getVersion().equals( version ) )
1071         {
1072             if ( childModel.getVersion() == null )
1073             {
1074                 // Message below is checked for in the MNG-2199 core IT.
1075                 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 )
1076                     .setMessage( "Version must be a constant" ).setLocation( childModel.getLocation( "" ) ) );
1077 
1078             }
1079             else
1080             {
1081                 if ( childModel.getVersion().contains( "${" ) )
1082                 {
1083                     // Message below is checked for in the MNG-2199 core IT.
1084                     problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 )
1085                         .setMessage( "Version must be a constant" )
1086                         .setLocation( childModel.getLocation( "version" ) ) );
1087 
1088                 }
1089             }
1090 
1091             // MNG-2199: What else to check here ?
1092         }
1093 
1094         ModelData parentData = new ModelData( modelSource, parentModel, parent.getGroupId(), parent.getArtifactId(),
1095                                               parent.getVersion() );
1096 
1097         return parentData;
1098     }
1099 
1100     private Model getSuperModel()
1101     {
1102         return superPomProvider.getSuperModel( "4.0.0" ).clone();
1103     }
1104 
1105     private void importDependencyManagement( Model model, ModelBuildingRequest request,
1106                                              DefaultModelProblemCollector problems, Collection<String> importIds )
1107     {
1108         DependencyManagement depMgmt = model.getDependencyManagement();
1109 
1110         if ( depMgmt == null )
1111         {
1112             return;
1113         }
1114 
1115         String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion();
1116 
1117         importIds.add( importing );
1118 
1119         final WorkspaceModelResolver workspaceResolver = request.getWorkspaceModelResolver();
1120         final ModelResolver modelResolver = request.getModelResolver();
1121 
1122         ModelBuildingRequest importRequest = null;
1123 
1124         List<DependencyManagement> importMgmts = null;
1125 
1126         for ( Iterator<Dependency> it = depMgmt.getDependencies().iterator(); it.hasNext(); )
1127         {
1128             Dependency dependency = it.next();
1129 
1130             if ( !"pom".equals( dependency.getType() ) || !"import".equals( dependency.getScope() ) )
1131             {
1132                 continue;
1133             }
1134 
1135             it.remove();
1136 
1137             String groupId = dependency.getGroupId();
1138             String artifactId = dependency.getArtifactId();
1139             String version = dependency.getVersion();
1140 
1141             if ( groupId == null || groupId.length() <= 0 )
1142             {
1143                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1144                     .setMessage( "'dependencyManagement.dependencies.dependency.groupId' for "
1145                                      + dependency.getManagementKey() + " is missing." )
1146                     .setLocation( dependency.getLocation( "" ) ) );
1147                 continue;
1148             }
1149             if ( artifactId == null || artifactId.length() <= 0 )
1150             {
1151                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1152                     .setMessage( "'dependencyManagement.dependencies.dependency.artifactId' for "
1153                                      + dependency.getManagementKey() + " is missing." )
1154                     .setLocation( dependency.getLocation( "" ) ) );
1155                 continue;
1156             }
1157             if ( version == null || version.length() <= 0 )
1158             {
1159                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1160                     .setMessage( "'dependencyManagement.dependencies.dependency.version' for "
1161                                      + dependency.getManagementKey() + " is missing." )
1162                     .setLocation( dependency.getLocation( "" ) ) );
1163                 continue;
1164             }
1165 
1166             String imported = groupId + ':' + artifactId + ':' + version;
1167 
1168             if ( importIds.contains( imported ) )
1169             {
1170                 String message = "The dependencies of type=pom and with scope=import form a cycle: ";
1171                 for ( String modelId : importIds )
1172                 {
1173                     message += modelId + " -> ";
1174                 }
1175                 message += imported;
1176                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage( message ) );
1177 
1178                 continue;
1179             }
1180 
1181             DependencyManagement importMgmt = getCache( request.getModelCache(), groupId, artifactId, version,
1182                                                         ModelCacheTag.IMPORT );
1183 
1184             if ( importMgmt == null )
1185             {
1186                 if ( workspaceResolver == null && modelResolver == null )
1187                 {
1188                     throw new NullPointerException( String.format(
1189                         "request.workspaceModelResolver and request.modelResolver cannot be null"
1190                         + " (parent POM %s and POM %s)",
1191                         ModelProblemUtils.toId( groupId, artifactId, version ),
1192                         ModelProblemUtils.toSourceHint( model ) ) );
1193                 }
1194 
1195                 Model importModel = null;
1196                 if ( workspaceResolver != null )
1197                 {
1198                     try
1199                     {
1200                         importModel = workspaceResolver.resolveEffectiveModel( groupId, artifactId, version );
1201                     }
1202                     catch ( UnresolvableModelException e )
1203                     {
1204                         problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE )
1205                             .setMessage( e.getMessage().toString() ).setException( e ) );
1206                         continue;
1207                     }
1208                 }
1209 
1210                 // no workspace resolver or workspace resolver returned null (i.e. model not in workspace)
1211                 if ( importModel == null )
1212                 {
1213                     final ModelSource importSource;
1214                     try
1215                     {
1216                         importSource = modelResolver.resolveModel( groupId, artifactId, version );
1217                     }
1218                     catch ( UnresolvableModelException e )
1219                     {
1220                         StringBuilder buffer = new StringBuilder( 256 );
1221                         buffer.append( "Non-resolvable import POM" );
1222                         if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) )
1223                         {
1224                             buffer.append( ' ' ).append( ModelProblemUtils.toId( groupId, artifactId, version ) );
1225                         }
1226                         buffer.append( ": " ).append( e.getMessage() );
1227 
1228                         problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1229                             .setMessage( buffer.toString() ).setLocation( dependency.getLocation( "" ) )
1230                             .setException( e ) );
1231                         continue;
1232                     }
1233 
1234                     if ( importRequest == null )
1235                     {
1236                         importRequest = new DefaultModelBuildingRequest();
1237                         importRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
1238                         importRequest.setModelCache( request.getModelCache() );
1239                         importRequest.setSystemProperties( request.getSystemProperties() );
1240                         importRequest.setUserProperties( request.getUserProperties() );
1241                         importRequest.setLocationTracking( request.isLocationTracking() );
1242                     }
1243 
1244                     importRequest.setModelSource( importSource );
1245                     importRequest.setModelResolver( modelResolver.newCopy() );
1246 
1247                     final ModelBuildingResult importResult;
1248                     try
1249                     {
1250                         importResult = build( importRequest );
1251                     }
1252                     catch ( ModelBuildingException e )
1253                     {
1254                         problems.addAll( e.getProblems() );
1255                         continue;
1256                     }
1257 
1258                     problems.addAll( importResult.getProblems() );
1259 
1260                     importModel = importResult.getEffectiveModel();
1261                 }
1262 
1263                 importMgmt = importModel.getDependencyManagement();
1264 
1265                 if ( importMgmt == null )
1266                 {
1267                     importMgmt = new DependencyManagement();
1268                 }
1269 
1270                 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, importMgmt );
1271             }
1272 
1273             if ( importMgmts == null )
1274             {
1275                 importMgmts = new ArrayList<>();
1276             }
1277 
1278             importMgmts.add( importMgmt );
1279         }
1280 
1281         importIds.remove( importing );
1282 
1283         dependencyManagementImporter.importManagement( model, importMgmts, request, problems );
1284     }
1285 
1286     private <T> void putCache( ModelCache modelCache, String groupId, String artifactId, String version,
1287                                ModelCacheTag<T> tag, T data )
1288     {
1289         if ( modelCache != null )
1290         {
1291             modelCache.put( groupId, artifactId, version, tag.getName(), tag.intoCache( data ) );
1292         }
1293     }
1294 
1295     private <T> T getCache( ModelCache modelCache, String groupId, String artifactId, String version,
1296                             ModelCacheTag<T> tag )
1297     {
1298         if ( modelCache != null )
1299         {
1300             Object data = modelCache.get( groupId, artifactId, version, tag.getName() );
1301             if ( data != null )
1302             {
1303                 return tag.fromCache( tag.getType().cast( data ) );
1304             }
1305         }
1306         return null;
1307     }
1308 
1309     private void fireEvent( Model model, ModelBuildingRequest request, ModelProblemCollector problems,
1310                             ModelBuildingEventCatapult catapult )
1311         throws ModelBuildingException
1312     {
1313         ModelBuildingListener listener = request.getModelBuildingListener();
1314 
1315         if ( listener != null )
1316         {
1317             ModelBuildingEvent event = new DefaultModelBuildingEvent( model, request, problems );
1318 
1319             catapult.fire( listener, event );
1320         }
1321     }
1322 
1323     private boolean containsCoordinates( String message, String groupId, String artifactId, String version )
1324     {
1325         return message != null && ( groupId == null || message.contains( groupId ) )
1326             && ( artifactId == null || message.contains( artifactId ) )
1327             && ( version == null || message.contains( version ) );
1328     }
1329 
1330     protected boolean hasModelErrors( ModelProblemCollectorExt problems )
1331     {
1332         if ( problems instanceof DefaultModelProblemCollector )
1333         {
1334             return ( (DefaultModelProblemCollector) problems ).hasErrors();
1335         }
1336         else
1337         {
1338             // the default execution path only knows the DefaultModelProblemCollector,
1339             // only reason it's not in signature is because it's package private
1340             throw new IllegalStateException();
1341         }
1342     }
1343 
1344     protected boolean hasFatalErrors( ModelProblemCollectorExt problems )
1345     {
1346         if ( problems instanceof DefaultModelProblemCollector )
1347         {
1348             return ( (DefaultModelProblemCollector) problems ).hasFatalErrors();
1349         }
1350         else
1351         {
1352             // the default execution path only knows the DefaultModelProblemCollector,
1353             // only reason it's not in signature is because it's package private
1354             throw new IllegalStateException();
1355         }
1356     }
1357 
1358 }