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