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 replaceRepositories )
584     {
585         if ( modelResolver == null )
586         {
587             return;
588         }
589 
590         problems.setSource( model );
591 
592         List<Repository> repositories = model.getRepositories();
593 
594         for ( Repository repository : repositories )
595         {
596             try
597             {
598                 modelResolver.addRepository( repository, replaceRepositories );
599             }
600             catch ( InvalidRepositoryException e )
601             {
602                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
603                         .setMessage( "Invalid repository " + repository.getId() + ": " + e.getMessage() )
604                         .setLocation( repository.getLocation( "" ) )
605                         .setException( e ) );
606             }
607         }
608     }
609 
610     private void checkPluginVersions( List<ModelData> lineage, ModelBuildingRequest request,
611                                       ModelProblemCollector problems )
612     {
613         if ( request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
614         {
615             return;
616         }
617 
618         Map<String, Plugin> plugins = new HashMap<String, Plugin>();
619         Map<String, String> versions = new HashMap<String, String>();
620         Map<String, String> managedVersions = new HashMap<String, String>();
621 
622         for ( int i = lineage.size() - 1; i >= 0; i-- )
623         {
624             Model model = lineage.get( i ).getModel();
625             Build build = model.getBuild();
626             if ( build != null )
627             {
628                 for ( Plugin plugin : build.getPlugins() )
629                 {
630                     String key = plugin.getKey();
631                     if ( versions.get( key ) == null )
632                     {
633                         versions.put( key, plugin.getVersion() );
634                         plugins.put( key, plugin );
635                     }
636                 }
637                 PluginManagement mngt = build.getPluginManagement();
638                 if ( mngt != null )
639                 {
640                     for ( Plugin plugin : mngt.getPlugins() )
641                     {
642                         String key = plugin.getKey();
643                         if ( managedVersions.get( key ) == null )
644                         {
645                             managedVersions.put( key, plugin.getVersion() );
646                         }
647                     }
648                 }
649             }
650         }
651 
652         for ( String key : versions.keySet() )
653         {
654             if ( versions.get( key ) == null && managedVersions.get( key ) == null )
655             {
656                 InputLocation location = plugins.get( key ).getLocation( "" );
657                 problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.V20 )
658                         .setMessage( "'build.plugins.plugin.version' for " + key + " is missing." )
659                         .setLocation( location ) );
660             }
661         }
662     }
663 
664     private void assembleInheritance( List<ModelData> lineage, ModelBuildingRequest request,
665                                       ModelProblemCollector problems )
666     {
667         for ( int i = lineage.size() - 2; i >= 0; i-- )
668         {
669             Model parent = lineage.get( i + 1 ).getModel();
670             Model child = lineage.get( i ).getModel();
671             inheritanceAssembler.assembleModelInheritance( child, parent, request, problems );
672         }
673     }
674 
675     private Map<String, Activation> getProfileActivations( Model model, boolean clone )
676     {
677         Map<String, Activation> activations = new HashMap<String, Activation>();
678         for ( Profile profile : model.getProfiles() )
679         {
680             Activation activation = profile.getActivation();
681 
682             if ( activation == null )
683             {
684                 continue;
685             }
686 
687             if ( clone )
688             {
689                 activation = activation.clone();
690             }
691 
692             activations.put( profile.getId(), activation );
693         }
694 
695         return activations;
696     }
697 
698     private void injectProfileActivations( Model model, Map<String, Activation> activations )
699     {
700         for ( Profile profile : model.getProfiles() )
701         {
702             Activation activation = profile.getActivation();
703 
704             if ( activation == null )
705             {
706                 continue;
707             }
708 
709             // restore activation
710             profile.setActivation( activations.get( profile.getId() ) );
711         }
712     }
713 
714     private Model interpolateModel( Model model, ModelBuildingRequest request, ModelProblemCollector problems )
715     {
716         // save profile activations before interpolation, since they are evaluated with limited scope
717         Map<String, Activation> originalActivations = getProfileActivations( model, true );
718 
719         Model result = modelInterpolator.interpolateModel( model, model.getProjectDirectory(), request, problems );
720         result.setPomFile( model.getPomFile() );
721 
722         // restore profiles with file activation to their value before full interpolation
723         injectProfileActivations( model, originalActivations );
724 
725         return result;
726     }
727 
728     private ModelData readParent( Model childModel, ModelSource childSource, ModelBuildingRequest request,
729                                   DefaultModelProblemCollector problems )
730         throws ModelBuildingException
731     {
732         ModelData parentData;
733 
734         Parent parent = childModel.getParent();
735 
736         if ( parent != null )
737         {
738             String groupId = parent.getGroupId();
739             String artifactId = parent.getArtifactId();
740             String version = parent.getVersion();
741 
742             parentData = getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW );
743 
744             if ( parentData == null )
745             {
746                 parentData = readParentLocally( childModel, childSource, request, problems );
747 
748                 if ( parentData == null )
749                 {
750                     parentData = readParentExternally( childModel, request, problems );
751                 }
752 
753                 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW, parentData );
754             }
755             else
756             {
757                 /*
758                  * NOTE: This is a sanity check of the cache hit. If the cached parent POM was locally resolved, the
759                  * child's <relativePath> should point at that parent, too. If it doesn't, we ignore the cache and
760                  * resolve externally, to mimic the behavior if the cache didn't exist in the first place. Otherwise,
761                  * the cache would obscure a bad POM.
762                  */
763 
764                 File pomFile = parentData.getModel().getPomFile();
765                 if ( pomFile != null )
766                 {
767                     ModelSource expectedParentSource = getParentPomFile( childModel, childSource );
768 
769                     if ( expectedParentSource instanceof ModelSource2
770                         && !pomFile.toURI().equals( ( (ModelSource2) expectedParentSource ).getLocationURI() ) )
771                     {
772                         parentData = readParentExternally( childModel, request, problems );
773                     }
774                 }
775             }
776 
777             Model parentModel = parentData.getModel();
778 
779             if ( !"pom".equals( parentModel.getPackaging() ) )
780             {
781                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
782                         .setMessage( "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint( parentModel )
783                                      + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"" )
784                         .setLocation( parentModel.getLocation( "packaging" ) ) );
785             }
786         }
787         else
788         {
789             parentData = null;
790         }
791 
792         return parentData;
793     }
794 
795     private ModelData readParentLocally( Model childModel, ModelSource childSource, ModelBuildingRequest request,
796                                          DefaultModelProblemCollector problems )
797         throws ModelBuildingException
798     {
799         ModelSource candidateSource = getParentPomFile( childModel, childSource );
800 
801         if ( candidateSource == null )
802         {
803             return null;
804         }
805 
806         File pomFile = null;
807         if ( candidateSource instanceof FileModelSource )
808         {
809             pomFile = ( (FileModelSource) candidateSource ).getPomFile();
810         }
811 
812         Model candidateModel = readModel( candidateSource, pomFile, request, problems );
813 
814         String groupId = candidateModel.getGroupId();
815         if ( groupId == null && candidateModel.getParent() != null )
816         {
817             groupId = candidateModel.getParent().getGroupId();
818         }
819         String artifactId = candidateModel.getArtifactId();
820         String version = candidateModel.getVersion();
821         if ( version == null && candidateModel.getParent() != null )
822         {
823             version = candidateModel.getParent().getVersion();
824         }
825 
826         Parent parent = childModel.getParent();
827 
828         if ( groupId == null || !groupId.equals( parent.getGroupId() ) || artifactId == null
829             || !artifactId.equals( parent.getArtifactId() ) )
830         {
831             StringBuilder buffer = new StringBuilder( 256 );
832             buffer.append( "'parent.relativePath'" );
833             if ( childModel != problems.getRootModel() )
834             {
835                 buffer.append( " of POM " ).append( ModelProblemUtils.toSourceHint( childModel ) );
836             }
837             buffer.append( " points at " ).append( groupId ).append( ":" ).append( artifactId );
838             buffer.append( " instead of " ).append( parent.getGroupId() ).append( ":" ).append( parent.getArtifactId() );
839             buffer.append( ", please verify your project structure" );
840 
841             problems.setSource( childModel );
842             problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.BASE )
843                     .setMessage( buffer.toString() )
844                     .setLocation( parent.getLocation( "" ) ) );
845             return null;
846         }
847         if ( version == null || !version.equals( parent.getVersion() ) )
848         {
849             return null;
850         }
851 
852         ModelData parentData = new ModelData( candidateSource, candidateModel, groupId, artifactId, version );
853 
854         return parentData;
855     }
856 
857     private ModelSource getParentPomFile( Model childModel, ModelSource source )
858     {
859         if ( !( source instanceof ModelSource2 ) )
860         {
861             return null;
862         }
863 
864         String parentPath = childModel.getParent().getRelativePath();
865 
866         if ( parentPath == null || parentPath.length() <= 0 )
867         {
868             return null;
869         }
870 
871         return ( (ModelSource2) source ).getRelatedSource( parentPath );
872     }
873 
874     private ModelData readParentExternally( Model childModel, ModelBuildingRequest request,
875                                             DefaultModelProblemCollector problems )
876         throws ModelBuildingException
877     {
878         problems.setSource( childModel );
879 
880         Parent parent = childModel.getParent().clone();
881 
882         String groupId = parent.getGroupId();
883         String artifactId = parent.getArtifactId();
884         String version = parent.getVersion();
885 
886         ModelResolver modelResolver = request.getModelResolver();
887 
888         if ( modelResolver == null )
889         {
890             throw new IllegalArgumentException( "no model resolver provided, cannot resolve parent POM "
891                 + ModelProblemUtils.toId( groupId, artifactId, version ) + " for POM "
892                 + ModelProblemUtils.toSourceHint( childModel ) );
893         }
894 
895         ModelSource modelSource;
896         try
897         {
898             modelSource = modelResolver.resolveModel( parent );
899         }
900         catch ( UnresolvableModelException e )
901         {
902             StringBuilder buffer = new StringBuilder( 256 );
903             buffer.append( "Non-resolvable parent POM" );
904             if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) )
905             {
906                 buffer.append( " " ).append( ModelProblemUtils.toId( groupId, artifactId, version ) );
907             }
908             if ( childModel != problems.getRootModel() )
909             {
910                 buffer.append( " for " ).append( ModelProblemUtils.toId( childModel ) );
911             }
912             buffer.append( ": " ).append( e.getMessage() );
913             if ( childModel.getProjectDirectory() != null )
914             {
915                 if ( parent.getRelativePath() == null || parent.getRelativePath().length() <= 0 )
916                 {
917                     buffer.append( " and 'parent.relativePath' points at no local POM" );
918                 }
919                 else
920                 {
921                     buffer.append( " and 'parent.relativePath' points at wrong local POM" );
922                 }
923             }
924 
925             problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE )
926                     .setMessage( buffer.toString() )
927                     .setLocation( parent.getLocation( "" ) )
928                     .setException( e ) );
929             throw problems.newModelBuildingException();
930         }
931 
932         ModelBuildingRequest lenientRequest = request;
933         if ( request.getValidationLevel() > ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
934         {
935             lenientRequest = new FilterModelBuildingRequest( request )
936             {
937                 @Override
938                 public int getValidationLevel()
939                 {
940                     return ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
941                 }
942             };
943         }
944 
945         Model parentModel = readModel( modelSource, null, lenientRequest, problems );
946 
947         if ( !parent.getVersion().equals( version ) )
948         {
949             if ( childModel.getVersion() == null )
950             {
951                 problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ).
952                     setMessage( "Version must be a constant" ).
953                     setLocation( childModel.getLocation( "" ) ) );
954 
955             }
956             else
957             {
958                 if ( childModel.getVersion().indexOf( "${" ) > -1 )
959                 {
960                     problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ).
961                         setMessage( "Version must be a constant" ).
962                         setLocation( childModel.getLocation( "version" ) ) );
963 
964                 }
965             }
966 
967             // MNG-2199: What else to check here ?
968         }
969 
970         ModelData parentData = new ModelData( modelSource, parentModel, parent.getGroupId(), parent.getArtifactId(),
971                                               parent.getVersion() );
972 
973         return parentData;
974     }
975 
976     private Model getSuperModel()
977     {
978         return superPomProvider.getSuperModel( "4.0.0" ).clone();
979     }
980 
981     private void importDependencyManagement( Model model, ModelBuildingRequest request,
982                                              DefaultModelProblemCollector problems, Collection<String> importIds )
983     {
984         DependencyManagement depMngt = model.getDependencyManagement();
985 
986         if ( depMngt == null )
987         {
988             return;
989         }
990 
991         String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion();
992 
993         importIds.add( importing );
994 
995         ModelResolver modelResolver = request.getModelResolver();
996 
997         ModelBuildingRequest importRequest = null;
998 
999         List<DependencyManagement> importMngts = null;
1000 
1001         for ( Iterator<Dependency> it = depMngt.getDependencies().iterator(); it.hasNext(); )
1002         {
1003             Dependency dependency = it.next();
1004 
1005             if ( !"pom".equals( dependency.getType() ) || !"import".equals( dependency.getScope() ) )
1006             {
1007                 continue;
1008             }
1009 
1010             it.remove();
1011 
1012             String groupId = dependency.getGroupId();
1013             String artifactId = dependency.getArtifactId();
1014             String version = dependency.getVersion();
1015 
1016             if ( groupId == null || groupId.length() <= 0 )
1017             {
1018                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1019                         .setMessage( "'dependencyManagement.dependencies.dependency.groupId' for "
1020                                         + dependency.getManagementKey() + " is missing." )
1021                         .setLocation( dependency.getLocation( "" ) ) );
1022                 continue;
1023             }
1024             if ( artifactId == null || artifactId.length() <= 0 )
1025             {
1026                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1027                         .setMessage( "'dependencyManagement.dependencies.dependency.artifactId' for "
1028                                         + dependency.getManagementKey() + " is missing." )
1029                         .setLocation( dependency.getLocation( "" ) ) );
1030                 continue;
1031             }
1032             if ( version == null || version.length() <= 0 )
1033             {
1034                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1035                         .setMessage( "'dependencyManagement.dependencies.dependency.version' for "
1036                                         + dependency.getManagementKey() + " is missing." )
1037                         .setLocation( dependency.getLocation( "" ) ) );
1038                 continue;
1039             }
1040 
1041             String imported = groupId + ':' + artifactId + ':' + version;
1042 
1043             if ( importIds.contains( imported ) )
1044             {
1045                 String message = "The dependencies of type=pom and with scope=import form a cycle: ";
1046                 for ( String modelId : importIds )
1047                 {
1048                     message += modelId + " -> ";
1049                 }
1050                 message += imported;
1051                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage( message ) );
1052 
1053                 continue;
1054             }
1055 
1056             DependencyManagement importMngt =
1057                 getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT );
1058 
1059             if ( importMngt == null )
1060             {
1061                 if ( modelResolver == null )
1062                 {
1063                     throw new IllegalArgumentException( "no model resolver provided, cannot resolve import POM "
1064                         + ModelProblemUtils.toId( groupId, artifactId, version ) + " for POM "
1065                         + ModelProblemUtils.toSourceHint( model ) );
1066                 }
1067 
1068                 ModelSource importSource;
1069                 try
1070                 {
1071                     importSource = modelResolver.resolveModel( groupId, artifactId, version );
1072                 }
1073                 catch ( UnresolvableModelException e )
1074                 {
1075                     StringBuilder buffer = new StringBuilder( 256 );
1076                     buffer.append( "Non-resolvable import POM" );
1077                     if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) )
1078                     {
1079                         buffer.append( " " ).append( ModelProblemUtils.toId( groupId, artifactId, version ) );
1080                     }
1081                     buffer.append( ": " ).append( e.getMessage() );
1082 
1083                     problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
1084                             .setMessage( buffer.toString() )
1085                             .setLocation( dependency.getLocation( "" ) )
1086                             .setException( e ) );
1087                     continue;
1088                 }
1089 
1090                 if ( importRequest == null )
1091                 {
1092                     importRequest = new DefaultModelBuildingRequest();
1093                     importRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
1094                     importRequest.setModelCache( request.getModelCache() );
1095                     importRequest.setSystemProperties( request.getSystemProperties() );
1096                     importRequest.setUserProperties( request.getUserProperties() );
1097                     importRequest.setLocationTracking( request.isLocationTracking() );
1098                 }
1099 
1100                 importRequest.setModelSource( importSource );
1101                 importRequest.setModelResolver( modelResolver.newCopy() );
1102 
1103                 ModelBuildingResult importResult;
1104                 try
1105                 {
1106                     importResult = build( importRequest );
1107                 }
1108                 catch ( ModelBuildingException e )
1109                 {
1110                     problems.addAll( e.getProblems() );
1111                     continue;
1112                 }
1113 
1114                 problems.addAll( importResult.getProblems() );
1115 
1116                 Model importModel = importResult.getEffectiveModel();
1117 
1118                 importMngt = importModel.getDependencyManagement();
1119 
1120                 if ( importMngt == null )
1121                 {
1122                     importMngt = new DependencyManagement();
1123                 }
1124 
1125                 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, importMngt );
1126             }
1127 
1128             if ( importMngts == null )
1129             {
1130                 importMngts = new ArrayList<DependencyManagement>();
1131             }
1132 
1133             importMngts.add( importMngt );
1134         }
1135 
1136         importIds.remove( importing );
1137 
1138         dependencyManagementImporter.importManagement( model, importMngts, request, problems );
1139     }
1140 
1141     private <T> void putCache( ModelCache modelCache, String groupId, String artifactId, String version,
1142                                ModelCacheTag<T> tag, T data )
1143     {
1144         if ( modelCache != null )
1145         {
1146             modelCache.put( groupId, artifactId, version, tag.getName(), tag.intoCache( data ) );
1147         }
1148     }
1149 
1150     private <T> T getCache( ModelCache modelCache, String groupId, String artifactId, String version,
1151                             ModelCacheTag<T> tag )
1152     {
1153         if ( modelCache != null )
1154         {
1155             Object data = modelCache.get( groupId, artifactId, version, tag.getName() );
1156             if ( data != null )
1157             {
1158                 return tag.fromCache( tag.getType().cast( data ) );
1159             }
1160         }
1161         return null;
1162     }
1163 
1164     private void fireEvent( Model model, ModelBuildingRequest request, ModelProblemCollector problems,
1165                             ModelBuildingEventCatapult catapult )
1166         throws ModelBuildingException
1167     {
1168         ModelBuildingListener listener = request.getModelBuildingListener();
1169 
1170         if ( listener != null )
1171         {
1172             ModelBuildingEvent event = new DefaultModelBuildingEvent( model, request, problems );
1173 
1174             catapult.fire( listener, event );
1175         }
1176     }
1177 
1178     private boolean containsCoordinates( String message, String groupId, String artifactId, String version )
1179     {
1180         return message != null && ( groupId == null || message.contains( groupId ) )
1181             && ( artifactId == null || message.contains( artifactId ) )
1182             && ( version == null || message.contains( version ) );
1183     }
1184 
1185     protected boolean hasModelErrors( ModelProblemCollectorExt problems )
1186     {
1187         if ( problems instanceof DefaultModelProblemCollector )
1188         {
1189             return ( (DefaultModelProblemCollector) problems ).hasErrors();
1190         }
1191         else
1192         {
1193             // the default execution path only knows the DefaultModelProblemCollector,
1194             // only reason it's not in signature is because it's package private
1195             throw new IllegalStateException();
1196         }
1197     }
1198 
1199     protected boolean hasFatalErrors( ModelProblemCollectorExt problems )
1200     {
1201         if ( problems instanceof DefaultModelProblemCollector )
1202         {
1203             return ( (DefaultModelProblemCollector) problems ).hasFatalErrors();
1204         }
1205         else
1206         {
1207             // the default execution path only knows the DefaultModelProblemCollector,
1208             // only reason it's not in signature is because it's package private
1209             throw new IllegalStateException();
1210         }
1211     }
1212 
1213 }