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