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