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( null );
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         if ( !activeExternalProfiles.isEmpty() )
249         {
250             Properties profileProps = new Properties();
251             for ( Profile profile : activeExternalProfiles )
252             {
253                 profileProps.putAll( profile.getProperties() );
254             }
255             profileProps.putAll( profileActivationContext.getUserProperties() );
256             profileActivationContext.setUserProperties( profileProps );
257         }
258 
259         Model inputModel = readModel( request.getModelSource(), request.getPomFile(), request, problems );
260 
261         problems.setRootModel( inputModel );
262 
263         ModelData resultData = new ModelData( inputModel );
264         ModelData superData = new ModelData( getSuperModel() );
265 
266         Collection<String> parentIds = new LinkedHashSet<String>();
267         parentIds.add( ModelProblemUtils.toId( inputModel ) );
268 
269         List<ModelData> lineage = new ArrayList<ModelData>();
270 
271         for ( ModelData currentData = resultData; currentData != null; )
272         {
273             lineage.add( currentData );
274 
275             Model tmpModel = currentData.getModel();
276 
277             Model rawModel = tmpModel.clone();
278             currentData.setRawModel( rawModel );
279 
280             problems.setSource( tmpModel );
281 
282             modelNormalizer.mergeDuplicates( tmpModel, request, problems );
283 
284             List<Profile> activePomProfiles =
285                 profileSelector.getActiveProfiles( rawModel.getProfiles(), profileActivationContext, problems );
286             currentData.setActiveProfiles( activePomProfiles );
287 
288             for ( Profile activeProfile : activePomProfiles )
289             {
290                 profileInjector.injectProfile( tmpModel, activeProfile, request, problems );
291             }
292 
293             if ( currentData == resultData )
294             {
295                 for ( Profile activeProfile : activeExternalProfiles )
296                 {
297                     profileInjector.injectProfile( tmpModel, activeProfile, request, problems );
298                 }
299             }
300 
301             if ( currentData == superData )
302             {
303                 break;
304             }
305 
306             configureResolver( request.getModelResolver(), tmpModel, problems );
307 
308             currentData = readParent( tmpModel, request, problems );
309 
310             if ( currentData == null )
311             {
312                 currentData = superData;
313             }
314             else if ( !parentIds.add( currentData.getId() ) )
315             {
316                 String message = "The parents form a cycle: ";
317                 for ( String modelId : parentIds )
318                 {
319                     message += modelId + " -> ";
320                 }
321                 message += currentData.getId();
322 
323                 problems.add( ModelProblem.Severity.FATAL, message, null, null );
324                 throw new ModelBuildingException( problems.getRootModel(), problems.getRootModelId(),
325                                                   problems.getProblems() );
326             }
327         }
328 
329         problems.setSource( inputModel );
330         checkPluginVersions( lineage, request, problems );
331 
332         assembleInheritance( lineage, request, problems );
333 
334         Model resultModel = resultData.getModel();
335 
336         problems.setSource( resultModel );
337         problems.setRootModel( resultModel );
338 
339         resultModel = interpolateModel( resultModel, request, problems );
340         resultData.setModel( resultModel );
341 
342         modelUrlNormalizer.normalize( resultModel, request );
343 
344         resultData.setGroupId( resultModel.getGroupId() );
345         resultData.setArtifactId( resultModel.getArtifactId() );
346         resultData.setVersion( resultModel.getVersion() );
347 
348         result.setProblems( problems.getProblems() );
349 
350         result.setEffectiveModel( resultModel );
351 
352         result.setActiveExternalProfiles( activeExternalProfiles );
353 
354         for ( ModelData currentData : lineage )
355         {
356             String modelId = ( currentData != superData ) ? currentData.getId() : "";
357 
358             result.addModelId( modelId );
359             result.setActivePomProfiles( modelId, currentData.getActiveProfiles() );
360             result.setRawModel( modelId, currentData.getRawModel() );
361         }
362 
363         if ( !request.isTwoPhaseBuilding() )
364         {
365             build( request, result );
366         }
367 
368         return result;
369     }
370 
371     public ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result )
372         throws ModelBuildingException
373     {
374         return build( request, result, new LinkedHashSet<String>() );
375     }
376 
377     private ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result,
378                                        Collection<String> imports )
379         throws ModelBuildingException
380     {
381         Model resultModel = result.getEffectiveModel();
382 
383         DefaultModelProblemCollector problems = new DefaultModelProblemCollector( result.getProblems() );
384         problems.setSource( resultModel );
385         problems.setRootModel( resultModel );
386 
387         modelPathTranslator.alignToBaseDirectory( resultModel, resultModel.getProjectDirectory(), request );
388 
389         pluginManagementInjector.injectManagement( resultModel, request, problems );
390 
391         fireEvent( resultModel, request, problems, ModelBuildingEventCatapult.BUILD_EXTENSIONS_ASSEMBLED );
392 
393         if ( request.isProcessPlugins() )
394         {
395             if ( lifecycleBindingsInjector == null )
396             {
397                 throw new IllegalStateException( "lifecycle bindings injector is missing" );
398             }
399 
400             lifecycleBindingsInjector.injectLifecycleBindings( resultModel, request, problems );
401         }
402 
403         importDependencyManagement( resultModel, request, problems, imports );
404 
405         dependencyManagementInjector.injectManagement( resultModel, request, problems );
406 
407         modelNormalizer.injectDefaultValues( resultModel, request, problems );
408 
409         if ( request.isProcessPlugins() )
410         {
411             reportConfigurationExpander.expandPluginConfiguration( resultModel, request, problems );
412 
413             reportingConverter.convertReporting( resultModel, request, problems );
414 
415             pluginConfigurationExpander.expandPluginConfiguration( resultModel, request, problems );
416         }
417 
418         modelValidator.validateEffectiveModel( resultModel, request, problems );
419 
420         if ( problems.hasErrors() )
421         {
422             throw new ModelBuildingException( resultModel, problems.getRootModelId(), problems.getProblems() );
423         }
424 
425         return result;
426     }
427 
428     private Model readModel( ModelSource modelSource, File pomFile, ModelBuildingRequest request,
429                              DefaultModelProblemCollector problems )
430         throws ModelBuildingException
431     {
432         Model model;
433 
434         if ( modelSource == null )
435         {
436             if ( pomFile != null )
437             {
438                 modelSource = new FileModelSource( pomFile );
439             }
440             else
441             {
442                 throw new IllegalArgumentException( "neither model source nor input file are specified" );
443             }
444         }
445 
446         problems.setSource( modelSource.getLocation() );
447         try
448         {
449             boolean strict = request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
450             InputSource source = request.isLocationTracking() ? new InputSource() : null;
451 
452             Map<String, Object> options = new HashMap<String, Object>();
453             options.put( ModelProcessor.IS_STRICT, Boolean.valueOf( strict ) );
454             options.put( ModelProcessor.INPUT_SOURCE, source );
455             options.put( ModelProcessor.SOURCE, modelSource );
456 
457             try
458             {
459                 model = modelProcessor.read( modelSource.getInputStream(), options );
460             }
461             catch ( ModelParseException e )
462             {
463                 if ( !strict )
464                 {
465                     throw e;
466                 }
467 
468                 options.put( ModelProcessor.IS_STRICT, Boolean.FALSE );
469 
470                 try
471                 {
472                     model = modelProcessor.read( modelSource.getInputStream(), options );
473                 }
474                 catch ( ModelParseException ne )
475                 {
476                     // still unreadable even in non-strict mode, rethrow original error
477                     throw e;
478                 }
479 
480                 if ( pomFile != null )
481                 {
482                     problems.add( Severity.ERROR, "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage(),
483                                   null, e );
484                 }
485                 else
486                 {
487                     problems.add( Severity.WARNING, "Malformed POM " + modelSource.getLocation() + ": "
488                         + e.getMessage(), null, e );
489                 }
490             }
491 
492             if ( source != null )
493             {
494                 source.setModelId( ModelProblemUtils.toId( model ) );
495                 source.setLocation( pomFile != null ? pomFile.getAbsolutePath() : null );
496             }
497         }
498         catch ( ModelParseException e )
499         {
500             problems.add( Severity.FATAL, "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage(),
501                           null, e );
502             throw new ModelBuildingException( problems.getRootModel(), problems.getRootModelId(),
503                                               problems.getProblems() );
504         }
505         catch ( IOException e )
506         {
507             String msg = e.getMessage();
508             if ( msg == null || msg.length() <= 0 )
509             {
510                 // NOTE: There's java.nio.charset.MalformedInputException and sun.io.MalformedInputException
511                 if ( e.getClass().getName().endsWith( "MalformedInputException" ) )
512                 {
513                     msg = "Some input bytes do not match the file encoding.";
514                 }
515                 else
516                 {
517                     msg = e.getClass().getSimpleName();
518                 }
519             }
520             problems.add( Severity.FATAL, "Non-readable POM " + modelSource.getLocation() + ": " + msg, null, e );
521             throw new ModelBuildingException( problems.getRootModel(), problems.getRootModelId(),
522                                               problems.getProblems() );
523         }
524 
525         model.setPomFile( pomFile );
526 
527         problems.setSource( model );
528         modelValidator.validateRawModel( model, request, problems );
529 
530         if ( problems.hasFatalErrors() )
531         {
532             throw new ModelBuildingException( problems.getRootModel(), problems.getRootModelId(),
533                                               problems.getProblems() );
534         }
535 
536         return model;
537     }
538 
539     private DefaultProfileActivationContext getProfileActivationContext( ModelBuildingRequest request )
540     {
541         DefaultProfileActivationContext context = new DefaultProfileActivationContext();
542 
543         context.setActiveProfileIds( request.getActiveProfileIds() );
544         context.setInactiveProfileIds( request.getInactiveProfileIds() );
545         context.setSystemProperties( request.getSystemProperties() );
546         context.setUserProperties( request.getUserProperties() );
547         context.setProjectDirectory( ( request.getPomFile() != null ) ? request.getPomFile().getParentFile() : null );
548 
549         return context;
550     }
551 
552     private void configureResolver( ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems )
553     {
554         if ( modelResolver == null )
555         {
556             return;
557         }
558 
559         problems.setSource( model );
560 
561         List<Repository> repositories = model.getRepositories();
562 
563         for ( Repository repository : repositories )
564         {
565             try
566             {
567                 modelResolver.addRepository( repository );
568             }
569             catch ( InvalidRepositoryException e )
570             {
571                 problems.add( Severity.ERROR, "Invalid repository " + repository.getId() + ": " + e.getMessage(),
572                               repository.getLocation( "" ), e );
573             }
574         }
575     }
576 
577     private void checkPluginVersions( List<ModelData> lineage, ModelBuildingRequest request,
578                                       ModelProblemCollector problems )
579     {
580         if ( request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
581         {
582             return;
583         }
584 
585         Map<String, Plugin> plugins = new HashMap<String, Plugin>();
586         Map<String, String> versions = new HashMap<String, String>();
587         Map<String, String> managedVersions = new HashMap<String, String>();
588 
589         for ( int i = lineage.size() - 1; i >= 0; i-- )
590         {
591             Model model = lineage.get( i ).getModel();
592             Build build = model.getBuild();
593             if ( build != null )
594             {
595                 for ( Plugin plugin : build.getPlugins() )
596                 {
597                     String key = plugin.getKey();
598                     if ( versions.get( key ) == null )
599                     {
600                         versions.put( key, plugin.getVersion() );
601                         plugins.put( key, plugin );
602                     }
603                 }
604                 PluginManagement mngt = build.getPluginManagement();
605                 if ( mngt != null )
606                 {
607                     for ( Plugin plugin : mngt.getPlugins() )
608                     {
609                         String key = plugin.getKey();
610                         if ( managedVersions.get( key ) == null )
611                         {
612                             managedVersions.put( key, plugin.getVersion() );
613                         }
614                     }
615                 }
616             }
617         }
618 
619         for ( String key : versions.keySet() )
620         {
621             if ( versions.get( key ) == null && managedVersions.get( key ) == null )
622             {
623                 InputLocation location = plugins.get( key ).getLocation( "" );
624                 problems.add( Severity.WARNING, "'build.plugins.plugin.version' for " + key + " is missing.", location,
625                               null );
626             }
627         }
628     }
629 
630     private void assembleInheritance( List<ModelData> lineage, ModelBuildingRequest request,
631                                       ModelProblemCollector problems )
632     {
633         for ( int i = lineage.size() - 2; i >= 0; i-- )
634         {
635             Model parent = lineage.get( i + 1 ).getModel();
636             Model child = lineage.get( i ).getModel();
637             inheritanceAssembler.assembleModelInheritance( child, parent, request, problems );
638         }
639     }
640 
641     private Model interpolateModel( Model model, ModelBuildingRequest request, ModelProblemCollector problems )
642     {
643         Model result = modelInterpolator.interpolateModel( model, model.getProjectDirectory(), request, problems );
644         result.setPomFile( model.getPomFile() );
645         return result;
646     }
647 
648     private ModelData readParent( Model childModel, ModelBuildingRequest request,
649                                   DefaultModelProblemCollector problems )
650         throws ModelBuildingException
651     {
652         ModelData parentData;
653 
654         Parent parent = childModel.getParent();
655 
656         if ( parent != null )
657         {
658             String groupId = parent.getGroupId();
659             String artifactId = parent.getArtifactId();
660             String version = parent.getVersion();
661 
662             parentData = getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW );
663 
664             if ( parentData == null )
665             {
666                 parentData = readParentLocally( childModel, request, problems );
667 
668                 if ( parentData == null )
669                 {
670                     parentData = readParentExternally( childModel, request, problems );
671                 }
672 
673                 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW, parentData );
674             }
675             else
676             {
677                 /*
678                  * NOTE: This is a sanity check of the cache hit. If the cached parent POM was locally resolved, the
679                  * child's <relativePath> should point at that parent, too. If it doesn't, we ignore the cache and
680                  * resolve externally, to mimic the behavior if the cache didn't exist in the first place. Otherwise,
681                  * the cache would obscure a bad POM.
682                  */
683 
684                 File pomFile = parentData.getModel().getPomFile();
685                 if ( pomFile != null )
686                 {
687                     File expectedParentFile = getParentPomFile( childModel );
688 
689                     if ( !pomFile.equals( expectedParentFile ) )
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( Severity.ERROR, "Invalid packaging for parent POM "
701                     + ModelProblemUtils.toSourceHint( parentModel ) + ", must be \"pom\" but is \""
702                     + parentModel.getPackaging() + "\"", parentModel.getLocation( "packaging" ), null );
703             }
704         }
705         else
706         {
707             parentData = null;
708         }
709 
710         return parentData;
711     }
712 
713     private ModelData readParentLocally( Model childModel, ModelBuildingRequest request,
714                                          DefaultModelProblemCollector problems )
715         throws ModelBuildingException
716     {
717         File pomFile = getParentPomFile( childModel );
718 
719         if ( pomFile == null || !pomFile.isFile() )
720         {
721             return null;
722         }
723 
724         Model candidateModel = readModel( null, pomFile, request, problems );
725 
726         String groupId = candidateModel.getGroupId();
727         if ( groupId == null && candidateModel.getParent() != null )
728         {
729             groupId = candidateModel.getParent().getGroupId();
730         }
731         String artifactId = candidateModel.getArtifactId();
732         String version = candidateModel.getVersion();
733         if ( version == null && candidateModel.getParent() != null )
734         {
735             version = candidateModel.getParent().getVersion();
736         }
737 
738         Parent parent = childModel.getParent();
739 
740         if ( groupId == null || !groupId.equals( parent.getGroupId() ) || artifactId == null
741             || !artifactId.equals( parent.getArtifactId() ) )
742         {
743             StringBuilder buffer = new StringBuilder( 256 );
744             buffer.append( "'parent.relativePath'" );
745             if ( childModel != problems.getRootModel() )
746             {
747                 buffer.append( " of POM " ).append( ModelProblemUtils.toSourceHint( childModel ) );
748             }
749             buffer.append( " points at " ).append( groupId ).append( ":" ).append( artifactId );
750             buffer.append( " instead of " ).append( parent.getGroupId() ).append( ":" ).append( parent.getArtifactId() );
751             buffer.append( ", please verify your project structure" );
752 
753             problems.setSource( childModel );
754             problems.add( Severity.WARNING, buffer.toString(), parent.getLocation( "" ), null );
755             return null;
756         }
757         if ( version == null || !version.equals( parent.getVersion() ) )
758         {
759             return null;
760         }
761 
762         ModelData parentData = new ModelData( candidateModel, groupId, artifactId, version );
763 
764         return parentData;
765     }
766 
767     private File getParentPomFile( Model childModel )
768     {
769         File projectDirectory = childModel.getProjectDirectory();
770 
771         if ( projectDirectory == null )
772         {
773             return null;
774         }
775 
776         String parentPath = childModel.getParent().getRelativePath();
777 
778         if ( parentPath == null || parentPath.length() <= 0 )
779         {
780             return null;
781         }
782 
783         parentPath = parentPath.replace( '\\', File.separatorChar ).replace( '/', File.separatorChar );
784 
785         File pomFile = new File( new File( projectDirectory, parentPath ).toURI().normalize() );
786 
787         if ( pomFile.isDirectory() )
788         {
789             pomFile = modelProcessor.locatePom( pomFile );
790         }
791 
792         return pomFile;
793     }
794 
795     private ModelData readParentExternally( Model childModel, ModelBuildingRequest request,
796                                             DefaultModelProblemCollector problems )
797         throws ModelBuildingException
798     {
799         problems.setSource( childModel );
800 
801         Parent parent = childModel.getParent();
802 
803         String groupId = parent.getGroupId();
804         String artifactId = parent.getArtifactId();
805         String version = parent.getVersion();
806 
807         ModelResolver modelResolver = request.getModelResolver();
808 
809         if ( modelResolver == null )
810         {
811             throw new IllegalArgumentException( "no model resolver provided, cannot resolve parent POM "
812                 + ModelProblemUtils.toId( groupId, artifactId, version ) + " for POM "
813                 + ModelProblemUtils.toSourceHint( childModel ) );
814         }
815 
816         ModelSource modelSource;
817         try
818         {
819             modelSource = modelResolver.resolveModel( groupId, artifactId, version );
820         }
821         catch ( UnresolvableModelException e )
822         {
823             StringBuilder buffer = new StringBuilder( 256 );
824             buffer.append( "Non-resolvable parent POM" );
825             if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) )
826             {
827                 buffer.append( " " ).append( ModelProblemUtils.toId( groupId, artifactId, version ) );
828             }
829             if ( childModel != problems.getRootModel() )
830             {
831                 buffer.append( " for " ).append( ModelProblemUtils.toId( childModel ) );
832             }
833             buffer.append( ": " ).append( e.getMessage() );
834             if ( childModel.getProjectDirectory() != null )
835             {
836                 if ( parent.getRelativePath() == null || parent.getRelativePath().length() <= 0 )
837                 {
838                     buffer.append( " and 'parent.relativePath' points at no local POM" );
839                 }
840                 else
841                 {
842                     buffer.append( " and 'parent.relativePath' points at wrong local POM" );
843                 }
844             }
845 
846             problems.add( Severity.FATAL, buffer.toString(), parent.getLocation( "" ), e );
847             throw new ModelBuildingException( problems.getRootModel(), problems.getRootModelId(),
848                                               problems.getProblems() );
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 =
867             new ModelData( parentModel, parent.getGroupId(), parent.getArtifactId(), parent.getVersion() );
868 
869         return parentData;
870     }
871 
872     private Model getSuperModel()
873     {
874         return superPomProvider.getSuperModel( "4.0.0" ).clone();
875     }
876 
877     private void importDependencyManagement( Model model, ModelBuildingRequest request,
878                                              DefaultModelProblemCollector problems, Collection<String> importIds )
879     {
880         DependencyManagement depMngt = model.getDependencyManagement();
881 
882         if ( depMngt == null )
883         {
884             return;
885         }
886 
887         String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion();
888 
889         importIds.add( importing );
890 
891         ModelResolver modelResolver = request.getModelResolver();
892 
893         ModelBuildingRequest importRequest = null;
894 
895         List<DependencyManagement> importMngts = null;
896 
897         for ( Iterator<Dependency> it = depMngt.getDependencies().iterator(); it.hasNext(); )
898         {
899             Dependency dependency = it.next();
900 
901             if ( !"pom".equals( dependency.getType() ) || !"import".equals( dependency.getScope() ) )
902             {
903                 continue;
904             }
905 
906             it.remove();
907 
908             String groupId = dependency.getGroupId();
909             String artifactId = dependency.getArtifactId();
910             String version = dependency.getVersion();
911 
912             String imported = groupId + ':' + artifactId + ':' + version;
913 
914             if ( importIds.contains( imported ) )
915             {
916                 String message = "The dependencies of type=pom and with scope=import form a cycle: ";
917                 for ( String modelId : importIds )
918                 {
919                     message += modelId + " -> ";
920                 }
921                 message += imported;
922                 problems.add( Severity.ERROR, message, null, null );
923 
924                 continue;
925             }
926 
927             DependencyManagement importMngt =
928                 getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT );
929 
930             if ( importMngt == null )
931             {
932                 if ( modelResolver == null )
933                 {
934                     throw new IllegalArgumentException( "no model resolver provided, cannot resolve import POM "
935                         + ModelProblemUtils.toId( groupId, artifactId, version ) + " for POM "
936                         + ModelProblemUtils.toSourceHint( model ) );
937                 }
938 
939                 ModelSource importSource;
940                 try
941                 {
942                     importSource = modelResolver.resolveModel( groupId, artifactId, version );
943                 }
944                 catch ( UnresolvableModelException e )
945                 {
946                     StringBuilder buffer = new StringBuilder( 256 );
947                     buffer.append( "Non-resolvable import POM" );
948                     if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) )
949                     {
950                         buffer.append( " " ).append( ModelProblemUtils.toId( groupId, artifactId, version ) );
951                     }
952                     buffer.append( ": " ).append( e.getMessage() );
953 
954                     problems.add( Severity.ERROR, buffer.toString(), dependency.getLocation( "" ), e );
955                     continue;
956                 }
957 
958                 if ( importRequest == null )
959                 {
960                     importRequest = new DefaultModelBuildingRequest();
961                     importRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
962                     importRequest.setModelCache( request.getModelCache() );
963                     importRequest.setSystemProperties( request.getSystemProperties() );
964                     importRequest.setUserProperties( request.getUserProperties() );
965                 }
966 
967                 importRequest.setModelSource( importSource );
968                 importRequest.setModelResolver( modelResolver.newCopy() );
969 
970                 ModelBuildingResult importResult;
971                 try
972                 {
973                     importResult = build( importRequest, importIds );
974                 }
975                 catch ( ModelBuildingException e )
976                 {
977                     problems.addAll( e.getProblems() );
978                     continue;
979                 }
980 
981                 problems.addAll( importResult.getProblems() );
982 
983                 Model importModel = importResult.getEffectiveModel();
984 
985                 importMngt = importModel.getDependencyManagement();
986 
987                 if ( importMngt == null )
988                 {
989                     importMngt = new DependencyManagement();
990                 }
991 
992                 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, importMngt );
993             }
994 
995             if ( importMngts == null )
996             {
997                 importMngts = new ArrayList<DependencyManagement>();
998             }
999 
1000             importMngts.add( importMngt );
1001         }
1002 
1003         importIds.remove( importing );
1004 
1005         dependencyManagementImporter.importManagement( model, importMngts, request, problems );
1006     }
1007 
1008     private <T> void putCache( ModelCache modelCache, String groupId, String artifactId, String version,
1009                                ModelCacheTag<T> tag, T data )
1010     {
1011         if ( modelCache != null )
1012         {
1013             modelCache.put( groupId, artifactId, version, tag.getName(), tag.intoCache( data ) );
1014         }
1015     }
1016 
1017     private <T> T getCache( ModelCache modelCache, String groupId, String artifactId, String version,
1018                             ModelCacheTag<T> tag )
1019     {
1020         if ( modelCache != null )
1021         {
1022             Object data = modelCache.get( groupId, artifactId, version, tag.getName() );
1023             if ( data != null )
1024             {
1025                 return tag.fromCache( tag.getType().cast( data ) );
1026             }
1027         }
1028         return null;
1029     }
1030 
1031     private void fireEvent( Model model, ModelBuildingRequest request, ModelProblemCollector problems,
1032                             ModelBuildingEventCatapult catapult )
1033         throws ModelBuildingException
1034     {
1035         ModelBuildingListener listener = request.getModelBuildingListener();
1036 
1037         if ( listener != null )
1038         {
1039             ModelBuildingEvent event = new DefaultModelBuildingEvent( model, request, problems );
1040 
1041             catapult.fire( listener, event );
1042         }
1043     }
1044 
1045     private boolean containsCoordinates( String message, String groupId, String artifactId, String version )
1046     {
1047         return message != null && message.contains( groupId ) && message.contains( artifactId )
1048             && message.contains( version );
1049     }
1050 
1051 }