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 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 }