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( inputModel );
261         ModelData superData = new ModelData( 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             List<Profile> activePomProfiles =
282                 profileSelector.getActiveProfiles( rawModel.getProfiles(), profileActivationContext, problems );
283             currentData.setActiveProfiles( activePomProfiles );
284 
285             for ( Profile activeProfile : activePomProfiles )
286             {
287                 profileInjector.injectProfile( tmpModel, activeProfile, request, problems );
288             }
289 
290             if ( currentData == resultData )
291             {
292                 for ( Profile activeProfile : activeExternalProfiles )
293                 {
294                     profileInjector.injectProfile( tmpModel, activeProfile, request, problems );
295                 }
296             }
297 
298             if ( currentData == superData )
299             {
300                 break;
301             }
302 
303             configureResolver( request.getModelResolver(), tmpModel, problems );
304 
305             currentData = readParent( tmpModel, request, problems );
306 
307             if ( currentData == null )
308             {
309                 currentData = superData;
310             }
311             else if ( !parentIds.add( currentData.getId() ) )
312             {
313                 String message = "The parents form a cycle: ";
314                 for ( String modelId : parentIds )
315                 {
316                     message += modelId + " -> ";
317                 }
318                 message += currentData.getId();
319 
320                 problems.add( new ModelProblemCollectorRequest(ModelProblem.Severity.FATAL, ModelProblem.Version.BASE).setMessage(message));
321                 throw problems.newModelBuildingException();
322             }
323         }
324 
325         problems.setSource( inputModel );
326         checkPluginVersions( lineage, request, problems );
327 
328         assembleInheritance( lineage, request, problems );
329 
330         Model resultModel = resultData.getModel();
331 
332         problems.setSource( resultModel );
333         problems.setRootModel( resultModel );
334 
335         resultModel = interpolateModel( resultModel, request, problems );
336         resultData.setModel( resultModel );
337 
338         modelUrlNormalizer.normalize( resultModel, request );
339 
340         resultData.setGroupId( resultModel.getGroupId() );
341         resultData.setArtifactId( resultModel.getArtifactId() );
342         resultData.setVersion( resultModel.getVersion() );
343 
344         result.setEffectiveModel( resultModel );
345 
346         for ( ModelData currentData : lineage )
347         {
348             String modelId = ( currentData != superData ) ? currentData.getId() : "";
349 
350             result.addModelId( modelId );
351             result.setActivePomProfiles( modelId, currentData.getActiveProfiles() );
352             result.setRawModel( modelId, currentData.getRawModel() );
353         }
354 
355         if ( !request.isTwoPhaseBuilding() )
356         {
357             build( request, result );
358         }
359 
360         return result;
361     }
362 
363     public ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result )
364         throws ModelBuildingException
365     {
366         return build( request, result, new LinkedHashSet<String>() );
367     }
368 
369     private ModelBuildingResult build( ModelBuildingRequest request, ModelBuildingResult result,
370                                        Collection<String> imports )
371         throws ModelBuildingException
372     {
373         Model resultModel = result.getEffectiveModel();
374 
375         DefaultModelProblemCollector problems = new DefaultModelProblemCollector( result );
376         problems.setSource( resultModel );
377         problems.setRootModel( resultModel );
378 
379         modelPathTranslator.alignToBaseDirectory( resultModel, resultModel.getProjectDirectory(), request );
380 
381         pluginManagementInjector.injectManagement( resultModel, request, problems );
382 
383         fireEvent( resultModel, request, problems, ModelBuildingEventCatapult.BUILD_EXTENSIONS_ASSEMBLED );
384 
385         if ( request.isProcessPlugins() )
386         {
387             if ( lifecycleBindingsInjector == null )
388             {
389                 throw new IllegalStateException( "lifecycle bindings injector is missing" );
390             }
391 
392             lifecycleBindingsInjector.injectLifecycleBindings( resultModel, request, problems );
393         }
394 
395         importDependencyManagement( resultModel, request, problems, imports );
396 
397         dependencyManagementInjector.injectManagement( resultModel, request, problems );
398 
399         modelNormalizer.injectDefaultValues( resultModel, request, problems );
400 
401         if ( request.isProcessPlugins() )
402         {
403             reportConfigurationExpander.expandPluginConfiguration( resultModel, request, problems );
404 
405             reportingConverter.convertReporting( resultModel, request, problems );
406 
407             pluginConfigurationExpander.expandPluginConfiguration( resultModel, request, problems );
408         }
409 
410         modelValidator.validateEffectiveModel( resultModel, request, problems );
411 
412         if ( hasModelErrors(problems) )
413         {
414             throw problems.newModelBuildingException();
415         }
416 
417         return result;
418     }
419 
420     private Model readModel( ModelSource modelSource, File pomFile, ModelBuildingRequest request,
421                              DefaultModelProblemCollector problems )
422         throws ModelBuildingException
423     {
424         Model model;
425 
426         if ( modelSource == null )
427         {
428             if ( pomFile != null )
429             {
430                 modelSource = new FileModelSource( pomFile );
431             }
432             else
433             {
434                 throw new IllegalArgumentException( "neither model source nor input file are specified" );
435             }
436         }
437 
438         problems.setSource( modelSource.getLocation() );
439         try
440         {
441             boolean strict = request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
442             InputSource source = request.isLocationTracking() ? new InputSource() : null;
443 
444             Map<String, Object> options = new HashMap<String, Object>();
445             options.put( ModelProcessor.IS_STRICT, Boolean.valueOf( strict ) );
446             options.put( ModelProcessor.INPUT_SOURCE, source );
447             options.put( ModelProcessor.SOURCE, modelSource );
448 
449             try
450             {
451                 model = modelProcessor.read( modelSource.getInputStream(), options );
452             }
453             catch ( ModelParseException e )
454             {
455                 if ( !strict )
456                 {
457                     throw e;
458                 }
459 
460                 options.put( ModelProcessor.IS_STRICT, Boolean.FALSE );
461 
462                 try
463                 {
464                     model = modelProcessor.read( modelSource.getInputStream(), options );
465                 }
466                 catch ( ModelParseException ne )
467                 {
468                     // still unreadable even in non-strict mode, rethrow original error
469                     throw e;
470                 }
471 
472                 if ( pomFile != null )
473                 {
474                     problems.add( new ModelProblemCollectorRequest(Severity.ERROR, Version.V20)
475                             .setMessage("Malformed POM " + modelSource.getLocation() + ": " + e.getMessage())
476                             .setException(e ));
477                 }
478                 else
479                 {
480                     problems.add( new ModelProblemCollectorRequest(Severity.WARNING, Version.V20)
481                             .setMessage("Malformed POM " + modelSource.getLocation() + ": " + e.getMessage())
482                             .setException(e));
483                 }
484             }
485 
486             if ( source != null )
487             {
488                 source.setModelId( ModelProblemUtils.toId( model ) );
489                 source.setLocation( modelSource.getLocation() );
490             }
491         }
492         catch ( ModelParseException e )
493         {
494             problems.add( new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE)
495                     .setMessage("Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage())
496                     .setException(e));
497             throw problems.newModelBuildingException();
498         }
499         catch ( IOException e )
500         {
501             String msg = e.getMessage();
502             if ( msg == null || msg.length() <= 0 )
503             {
504                 // NOTE: There's java.nio.charset.MalformedInputException and sun.io.MalformedInputException
505                 if ( e.getClass().getName().endsWith( "MalformedInputException" ) )
506                 {
507                     msg = "Some input bytes do not match the file encoding.";
508                 }
509                 else
510                 {
511                     msg = e.getClass().getSimpleName();
512                 }
513             }
514             problems.add( new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE)
515                     .setMessage("Non-readable POM " + modelSource.getLocation() + ": " + msg)
516                     .setException(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 ( hasFatalErrors(problems) )
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( new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
566                         .setMessage( "Invalid repository " + repository.getId() + ": " + e.getMessage())
567                         .setLocation(repository.getLocation( "" ))
568                         .setException(e) );
569             }
570         }
571     }
572 
573     private void checkPluginVersions( List<ModelData> lineage, ModelBuildingRequest request,
574                                       ModelProblemCollector problems )
575     {
576         if ( request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
577         {
578             return;
579         }
580 
581         Map<String, Plugin> plugins = new HashMap<String, Plugin>();
582         Map<String, String> versions = new HashMap<String, String>();
583         Map<String, String> managedVersions = new HashMap<String, String>();
584 
585         for ( int i = lineage.size() - 1; i >= 0; i-- )
586         {
587             Model model = lineage.get( i ).getModel();
588             Build build = model.getBuild();
589             if ( build != null )
590             {
591                 for ( Plugin plugin : build.getPlugins() )
592                 {
593                     String key = plugin.getKey();
594                     if ( versions.get( key ) == null )
595                     {
596                         versions.put( key, plugin.getVersion() );
597                         plugins.put( key, plugin );
598                     }
599                 }
600                 PluginManagement mngt = build.getPluginManagement();
601                 if ( mngt != null )
602                 {
603                     for ( Plugin plugin : mngt.getPlugins() )
604                     {
605                         String key = plugin.getKey();
606                         if ( managedVersions.get( key ) == null )
607                         {
608                             managedVersions.put( key, plugin.getVersion() );
609                         }
610                     }
611                 }
612             }
613         }
614 
615         for ( String key : versions.keySet() )
616         {
617             if ( versions.get( key ) == null && managedVersions.get( key ) == null )
618             {
619                 InputLocation location = plugins.get( key ).getLocation( "" );
620                 problems.add( new ModelProblemCollectorRequest(Severity.WARNING, Version.V20)
621                         .setMessage( "'build.plugins.plugin.version' for " + key + " is missing.")
622                         .setLocation(location));
623             }
624         }
625     }
626 
627     private void assembleInheritance( List<ModelData> lineage, ModelBuildingRequest request,
628                                       ModelProblemCollector problems )
629     {
630         for ( int i = lineage.size() - 2; i >= 0; i-- )
631         {
632             Model parent = lineage.get( i + 1 ).getModel();
633             Model child = lineage.get( i ).getModel();
634             inheritanceAssembler.assembleModelInheritance( child, parent, request, problems );
635         }
636     }
637 
638     private Model interpolateModel( Model model, ModelBuildingRequest request, ModelProblemCollector problems )
639     {
640         Model result = modelInterpolator.interpolateModel( model, model.getProjectDirectory(), request, problems );
641         result.setPomFile( model.getPomFile() );
642         return result;
643     }
644 
645     private ModelData readParent( Model childModel, ModelBuildingRequest request,
646                                   DefaultModelProblemCollector problems )
647         throws ModelBuildingException
648     {
649         ModelData parentData;
650 
651         Parent parent = childModel.getParent();
652 
653         if ( parent != null )
654         {
655             String groupId = parent.getGroupId();
656             String artifactId = parent.getArtifactId();
657             String version = parent.getVersion();
658 
659             parentData = getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW );
660 
661             if ( parentData == null )
662             {
663                 parentData = readParentLocally( childModel, request, problems );
664 
665                 if ( parentData == null )
666                 {
667                     parentData = readParentExternally( childModel, request, problems );
668                 }
669 
670                 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW, parentData );
671             }
672             else
673             {
674                 /*
675                  * NOTE: This is a sanity check of the cache hit. If the cached parent POM was locally resolved, the
676                  * child's <relativePath> should point at that parent, too. If it doesn't, we ignore the cache and
677                  * resolve externally, to mimic the behavior if the cache didn't exist in the first place. Otherwise,
678                  * the cache would obscure a bad POM.
679                  */
680 
681                 File pomFile = parentData.getModel().getPomFile();
682                 if ( pomFile != null )
683                 {
684                     File expectedParentFile = getParentPomFile( childModel );
685 
686                     if ( !pomFile.equals( expectedParentFile ) )
687                     {
688                         parentData = readParentExternally( childModel, request, problems );
689                     }
690                 }
691             }
692 
693             Model parentModel = parentData.getModel();
694 
695             if ( !"pom".equals( parentModel.getPackaging() ) )
696             {
697                 problems.add( new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
698                         .setMessage( "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint( parentModel ) + ", must be \"pom\" but is \""
699                                     + parentModel.getPackaging() + "\"")
700                         .setLocation(parentModel.getLocation( "packaging" )));
701             }
702         }
703         else
704         {
705             parentData = null;
706         }
707 
708         return parentData;
709     }
710 
711     private ModelData readParentLocally( Model childModel, ModelBuildingRequest request,
712                                          DefaultModelProblemCollector problems )
713         throws ModelBuildingException
714     {
715         File pomFile = getParentPomFile( childModel );
716 
717         if ( pomFile == null || !pomFile.isFile() )
718         {
719             return null;
720         }
721 
722         Model candidateModel = readModel( null, pomFile, request, problems );
723 
724         String groupId = candidateModel.getGroupId();
725         if ( groupId == null && candidateModel.getParent() != null )
726         {
727             groupId = candidateModel.getParent().getGroupId();
728         }
729         String artifactId = candidateModel.getArtifactId();
730         String version = candidateModel.getVersion();
731         if ( version == null && candidateModel.getParent() != null )
732         {
733             version = candidateModel.getParent().getVersion();
734         }
735 
736         Parent parent = childModel.getParent();
737 
738         if ( groupId == null || !groupId.equals( parent.getGroupId() ) || artifactId == null
739             || !artifactId.equals( parent.getArtifactId() ) )
740         {
741             StringBuilder buffer = new StringBuilder( 256 );
742             buffer.append( "'parent.relativePath'" );
743             if ( childModel != problems.getRootModel() )
744             {
745                 buffer.append( " of POM " ).append( ModelProblemUtils.toSourceHint( childModel ) );
746             }
747             buffer.append( " points at " ).append( groupId ).append( ":" ).append( artifactId );
748             buffer.append( " instead of " ).append( parent.getGroupId() ).append( ":" ).append( parent.getArtifactId() );
749             buffer.append( ", please verify your project structure" );
750 
751             problems.setSource( childModel );
752             problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.BASE)
753                     .setMessage( buffer.toString())
754                     .setLocation( parent.getLocation( "" )));
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( new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE)
847                     .setMessage( buffer.toString())
848                     .setLocation(parent.getLocation( "" ))
849                     .setException(e));
850             throw problems.newModelBuildingException();
851         }
852 
853         ModelBuildingRequest lenientRequest = request;
854         if ( request.getValidationLevel() > ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 )
855         {
856             lenientRequest = new FilterModelBuildingRequest( request )
857             {
858                 @Override
859                 public int getValidationLevel()
860                 {
861                     return ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
862                 }
863             };
864         }
865 
866         Model parentModel = readModel( modelSource, null, lenientRequest, problems );
867 
868         ModelData parentData = new ModelData( parentModel, groupId, artifactId, version );
869 
870         return parentData;
871     }
872 
873     private Model getSuperModel()
874     {
875         return superPomProvider.getSuperModel( "4.0.0" ).clone();
876     }
877 
878     private void importDependencyManagement( Model model, ModelBuildingRequest request,
879                                              DefaultModelProblemCollector problems, Collection<String> importIds )
880     {
881         DependencyManagement depMngt = model.getDependencyManagement();
882 
883         if ( depMngt == null )
884         {
885             return;
886         }
887 
888         String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion();
889 
890         importIds.add( importing );
891 
892         ModelResolver modelResolver = request.getModelResolver();
893 
894         ModelBuildingRequest importRequest = null;
895 
896         List<DependencyManagement> importMngts = null;
897 
898         for ( Iterator<Dependency> it = depMngt.getDependencies().iterator(); it.hasNext(); )
899         {
900             Dependency dependency = it.next();
901 
902             if ( !"pom".equals( dependency.getType() ) || !"import".equals( dependency.getScope() ) )
903             {
904                 continue;
905             }
906 
907             it.remove();
908 
909             String groupId = dependency.getGroupId();
910             String artifactId = dependency.getArtifactId();
911             String version = dependency.getVersion();
912 
913             if ( groupId == null || groupId.length() <= 0 )
914             {
915                 problems.add( new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
916                         .setMessage( "'dependencyManagement.dependencies.dependency.groupId' for " + dependency.getManagementKey() + " is missing.")
917                         .setLocation( dependency.getLocation( "" )));
918                 continue;
919             }
920             if ( artifactId == null || artifactId.length() <= 0 )
921             {
922                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE)
923                         .setMessage( "'dependencyManagement.dependencies.dependency.artifactId' for " + 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 " + dependency.getManagementKey() + " is missing.")
931                         .setLocation( dependency.getLocation( "" )));
932                 continue;
933             }
934 
935             String imported = groupId + ':' + artifactId + ':' + version;
936 
937             if ( importIds.contains( imported ) )
938             {
939                 String message = "The dependencies of type=pom and with scope=import form a cycle: ";
940                 for ( String modelId : importIds )
941                 {
942                     message += modelId + " -> ";
943                 }
944                 message += imported;
945                 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage( message ));
946 
947                 continue;
948             }
949 
950             DependencyManagement importMngt =
951                 getCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT );
952 
953             if ( importMngt == null )
954             {
955                 if ( modelResolver == null )
956                 {
957                     throw new IllegalArgumentException( "no model resolver provided, cannot resolve import POM "
958                         + ModelProblemUtils.toId( groupId, artifactId, version ) + " for POM "
959                         + ModelProblemUtils.toSourceHint( model ) );
960                 }
961 
962                 ModelSource importSource;
963                 try
964                 {
965                     importSource = modelResolver.resolveModel( groupId, artifactId, version );
966                 }
967                 catch ( UnresolvableModelException e )
968                 {
969                     StringBuilder buffer = new StringBuilder( 256 );
970                     buffer.append( "Non-resolvable import POM" );
971                     if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) )
972                     {
973                         buffer.append( " " ).append( ModelProblemUtils.toId( groupId, artifactId, version ) );
974                     }
975                     buffer.append( ": " ).append( e.getMessage() );
976 
977                     problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
978                             .setMessage( buffer.toString() )
979                             .setLocation( dependency.getLocation( "" ))
980                             .setException( e ));
981                     continue;
982                 }
983 
984                 if ( importRequest == null )
985                 {
986                     importRequest = new DefaultModelBuildingRequest();
987                     importRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
988                     importRequest.setModelCache( request.getModelCache() );
989                     importRequest.setSystemProperties( request.getSystemProperties() );
990                     importRequest.setUserProperties( request.getUserProperties() );
991                     importRequest.setLocationTracking( request.isLocationTracking() );
992                 }
993 
994                 importRequest.setModelSource( importSource );
995                 importRequest.setModelResolver( modelResolver.newCopy() );
996 
997                 ModelBuildingResult importResult;
998                 try
999                 {
1000                     importResult = build( importRequest );
1001                 }
1002                 catch ( ModelBuildingException e )
1003                 {
1004                     problems.addAll( e.getProblems() );
1005                     continue;
1006                 }
1007 
1008                 problems.addAll( importResult.getProblems() );
1009 
1010                 Model importModel = importResult.getEffectiveModel();
1011 
1012                 importMngt = importModel.getDependencyManagement();
1013 
1014                 if ( importMngt == null )
1015                 {
1016                     importMngt = new DependencyManagement();
1017                 }
1018 
1019                 putCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, importMngt );
1020             }
1021 
1022             if ( importMngts == null )
1023             {
1024                 importMngts = new ArrayList<DependencyManagement>();
1025             }
1026 
1027             importMngts.add( importMngt );
1028         }
1029 
1030         importIds.remove( importing );
1031 
1032         dependencyManagementImporter.importManagement( model, importMngts, request, problems );
1033     }
1034 
1035     private <T> void putCache( ModelCache modelCache, String groupId, String artifactId, String version,
1036                                ModelCacheTag<T> tag, T data )
1037     {
1038         if ( modelCache != null )
1039         {
1040             modelCache.put( groupId, artifactId, version, tag.getName(), tag.intoCache( data ) );
1041         }
1042     }
1043 
1044     private <T> T getCache( ModelCache modelCache, String groupId, String artifactId, String version,
1045                             ModelCacheTag<T> tag )
1046     {
1047         if ( modelCache != null )
1048         {
1049             Object data = modelCache.get( groupId, artifactId, version, tag.getName() );
1050             if ( data != null )
1051             {
1052                 return tag.fromCache( tag.getType().cast( data ) );
1053             }
1054         }
1055         return null;
1056     }
1057 
1058     private void fireEvent( Model model, ModelBuildingRequest request, ModelProblemCollector problems,
1059                             ModelBuildingEventCatapult catapult )
1060         throws ModelBuildingException
1061     {
1062         ModelBuildingListener listener = request.getModelBuildingListener();
1063 
1064         if ( listener != null )
1065         {
1066             ModelBuildingEvent event = new DefaultModelBuildingEvent( model, request, problems );
1067 
1068             catapult.fire( listener, event );
1069         }
1070     }
1071 
1072     private boolean containsCoordinates( String message, String groupId, String artifactId, String version )
1073     {
1074         return message != null && ( groupId == null || message.contains( groupId ) )
1075             && ( artifactId == null || message.contains( artifactId ) )
1076             && ( version == null || message.contains( version ) );
1077     }
1078 
1079     protected boolean hasModelErrors(ModelProblemCollectorExt problems) {
1080         if (problems instanceof DefaultModelProblemCollector) {
1081             return ((DefaultModelProblemCollector)problems).hasErrors();
1082         } else {
1083             //the default execution path only knows the DefaultModelProblemCollector,
1084             // only reason it's not in signature is because it's package private
1085             throw new IllegalStateException(); 
1086         }
1087         
1088     }
1089 
1090     protected boolean hasFatalErrors(ModelProblemCollectorExt problems) {
1091         if (problems instanceof DefaultModelProblemCollector) {
1092             return ((DefaultModelProblemCollector)problems).hasFatalErrors();
1093         } else {
1094             //the default execution path only knows the DefaultModelProblemCollector,
1095             // only reason it's not in signature is because it's package private
1096             throw new IllegalStateException(); 
1097         }
1098     }
1099 
1100 }