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.building.ModelProblem.Version;
046    import org.apache.maven.model.composition.DependencyManagementImporter;
047    import org.apache.maven.model.inheritance.InheritanceAssembler;
048    import org.apache.maven.model.interpolation.ModelInterpolator;
049    import org.apache.maven.model.io.ModelParseException;
050    import org.apache.maven.model.management.DependencyManagementInjector;
051    import org.apache.maven.model.management.PluginManagementInjector;
052    import org.apache.maven.model.normalization.ModelNormalizer;
053    import org.apache.maven.model.path.ModelPathTranslator;
054    import org.apache.maven.model.path.ModelUrlNormalizer;
055    import org.apache.maven.model.plugin.LifecycleBindingsInjector;
056    import org.apache.maven.model.plugin.PluginConfigurationExpander;
057    import org.apache.maven.model.plugin.ReportConfigurationExpander;
058    import org.apache.maven.model.plugin.ReportingConverter;
059    import org.apache.maven.model.profile.DefaultProfileActivationContext;
060    import org.apache.maven.model.profile.ProfileInjector;
061    import org.apache.maven.model.profile.ProfileSelector;
062    import org.apache.maven.model.resolution.InvalidRepositoryException;
063    import org.apache.maven.model.resolution.ModelResolver;
064    import org.apache.maven.model.resolution.UnresolvableModelException;
065    import org.apache.maven.model.superpom.SuperPomProvider;
066    import org.apache.maven.model.validation.ModelValidator;
067    import org.codehaus.plexus.component.annotations.Component;
068    import org.codehaus.plexus.component.annotations.Requirement;
069    
070    /**
071     * @author Benjamin Bentmann
072     */
073    @Component( role = ModelBuilder.class )
074    public class DefaultModelBuilder
075        implements ModelBuilder
076    {
077        @Requirement
078        private ModelProcessor modelProcessor;
079    
080        @Requirement
081        private ModelValidator modelValidator;
082    
083        @Requirement
084        private ModelNormalizer modelNormalizer;
085    
086        @Requirement
087        private ModelInterpolator modelInterpolator;
088    
089        @Requirement
090        private ModelPathTranslator modelPathTranslator;
091    
092        @Requirement
093        private ModelUrlNormalizer modelUrlNormalizer;
094    
095        @Requirement
096        private SuperPomProvider superPomProvider;
097    
098        @Requirement
099        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    }