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