001    package org.apache.maven.model.building;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.io.File;
023    import java.io.IOException;
024    import java.util.ArrayList;
025    import java.util.Collection;
026    import java.util.Iterator;
027    import java.util.LinkedHashSet;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.HashMap;
031    import java.util.Properties;
032    
033    import org.apache.maven.model.Build;
034    import org.apache.maven.model.Dependency;
035    import org.apache.maven.model.DependencyManagement;
036    import org.apache.maven.model.InputLocation;
037    import org.apache.maven.model.InputSource;
038    import org.apache.maven.model.Model;
039    import org.apache.maven.model.Parent;
040    import org.apache.maven.model.Plugin;
041    import org.apache.maven.model.PluginManagement;
042    import org.apache.maven.model.Profile;
043    import org.apache.maven.model.Repository;
044    import org.apache.maven.model.building.ModelProblem.Severity;
045    import org.apache.maven.model.composition.DependencyManagementImporter;
046    import org.apache.maven.model.inheritance.InheritanceAssembler;
047    import org.apache.maven.model.interpolation.ModelInterpolator;
048    import org.apache.maven.model.io.ModelParseException;
049    import org.apache.maven.model.management.DependencyManagementInjector;
050    import org.apache.maven.model.management.PluginManagementInjector;
051    import org.apache.maven.model.normalization.ModelNormalizer;
052    import org.apache.maven.model.path.ModelPathTranslator;
053    import org.apache.maven.model.path.ModelUrlNormalizer;
054    import org.apache.maven.model.plugin.LifecycleBindingsInjector;
055    import org.apache.maven.model.plugin.PluginConfigurationExpander;
056    import org.apache.maven.model.plugin.ReportConfigurationExpander;
057    import org.apache.maven.model.plugin.ReportingConverter;
058    import org.apache.maven.model.profile.DefaultProfileActivationContext;
059    import org.apache.maven.model.profile.ProfileInjector;
060    import org.apache.maven.model.profile.ProfileSelector;
061    import org.apache.maven.model.resolution.InvalidRepositoryException;
062    import org.apache.maven.model.resolution.ModelResolver;
063    import org.apache.maven.model.resolution.UnresolvableModelException;
064    import org.apache.maven.model.superpom.SuperPomProvider;
065    import org.apache.maven.model.validation.ModelValidator;
066    import org.codehaus.plexus.component.annotations.Component;
067    import org.codehaus.plexus.component.annotations.Requirement;
068    
069    /**
070     * @author Benjamin Bentmann
071     */
072    @Component( role = ModelBuilder.class )
073    public class DefaultModelBuilder
074        implements ModelBuilder
075    {
076        @Requirement
077        private ModelProcessor modelProcessor;
078    
079        @Requirement
080        private ModelValidator modelValidator;
081    
082        @Requirement
083        private ModelNormalizer modelNormalizer;
084    
085        @Requirement
086        private ModelInterpolator modelInterpolator;
087    
088        @Requirement
089        private ModelPathTranslator modelPathTranslator;
090    
091        @Requirement
092        private ModelUrlNormalizer modelUrlNormalizer;
093    
094        @Requirement
095        private SuperPomProvider superPomProvider;
096    
097        @Requirement
098        private InheritanceAssembler inheritanceAssembler;
099    
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    }