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