| package org.apache.maven.model.building; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import static org.apache.maven.model.building.Result.error; |
| import static org.apache.maven.model.building.Result.newResult; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| import javax.inject.Singleton; |
| |
| import org.apache.maven.artifact.versioning.DefaultArtifactVersion; |
| import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; |
| import org.apache.maven.artifact.versioning.VersionRange; |
| import org.apache.maven.building.Source; |
| import org.apache.maven.feature.Features; |
| import org.apache.maven.model.Activation; |
| import org.apache.maven.model.ActivationFile; |
| import org.apache.maven.model.Build; |
| import org.apache.maven.model.Dependency; |
| import org.apache.maven.model.DependencyManagement; |
| import org.apache.maven.model.InputLocation; |
| import org.apache.maven.model.InputSource; |
| import org.apache.maven.model.Model; |
| import org.apache.maven.model.Parent; |
| import org.apache.maven.model.Plugin; |
| import org.apache.maven.model.PluginManagement; |
| import org.apache.maven.model.Profile; |
| import org.apache.maven.model.Repository; |
| import org.apache.maven.model.building.ModelProblem.Severity; |
| import org.apache.maven.model.building.ModelProblem.Version; |
| import org.apache.maven.model.composition.DependencyManagementImporter; |
| import org.apache.maven.model.inheritance.InheritanceAssembler; |
| import org.apache.maven.model.interpolation.ModelInterpolator; |
| import org.apache.maven.model.io.ModelParseException; |
| import org.apache.maven.model.io.ModelReader; |
| import org.apache.maven.model.management.DependencyManagementInjector; |
| import org.apache.maven.model.management.PluginManagementInjector; |
| import org.apache.maven.model.merge.ModelMerger; |
| import org.apache.maven.model.normalization.ModelNormalizer; |
| import org.apache.maven.model.path.ModelPathTranslator; |
| import org.apache.maven.model.path.ModelUrlNormalizer; |
| import org.apache.maven.model.path.ProfileActivationFilePathInterpolator; |
| import org.apache.maven.model.plugin.LifecycleBindingsInjector; |
| import org.apache.maven.model.plugin.PluginConfigurationExpander; |
| import org.apache.maven.model.plugin.ReportConfigurationExpander; |
| import org.apache.maven.model.plugin.ReportingConverter; |
| import org.apache.maven.model.profile.DefaultProfileActivationContext; |
| import org.apache.maven.model.profile.ProfileActivationContext; |
| import org.apache.maven.model.profile.ProfileInjector; |
| import org.apache.maven.model.profile.ProfileSelector; |
| import org.apache.maven.model.resolution.InvalidRepositoryException; |
| import org.apache.maven.model.resolution.ModelResolver; |
| import org.apache.maven.model.resolution.UnresolvableModelException; |
| import org.apache.maven.model.resolution.WorkspaceModelResolver; |
| import org.apache.maven.model.superpom.SuperPomProvider; |
| import org.apache.maven.model.validation.ModelValidator; |
| import org.codehaus.plexus.interpolation.InterpolationException; |
| import org.codehaus.plexus.interpolation.MapBasedValueSource; |
| import org.codehaus.plexus.interpolation.StringSearchInterpolator; |
| import org.eclipse.sisu.Nullable; |
| |
| /** |
| * @author Benjamin Bentmann |
| */ |
| @Named |
| @Singleton |
| public class DefaultModelBuilder |
| implements ModelBuilder |
| { |
| private final ModelMerger modelMerger = new FileToRawModelMerger(); |
| |
| private final ModelProcessor modelProcessor; |
| private final ModelValidator modelValidator; |
| private final ModelNormalizer modelNormalizer; |
| private final ModelInterpolator modelInterpolator; |
| private final ModelPathTranslator modelPathTranslator; |
| private final ModelUrlNormalizer modelUrlNormalizer; |
| private final SuperPomProvider superPomProvider; |
| private final InheritanceAssembler inheritanceAssembler; |
| private final ProfileSelector profileSelector; |
| private final ProfileInjector profileInjector; |
| private final PluginManagementInjector pluginManagementInjector; |
| private final DependencyManagementInjector dependencyManagementInjector; |
| private final DependencyManagementImporter dependencyManagementImporter; |
| private final LifecycleBindingsInjector lifecycleBindingsInjector; |
| private final PluginConfigurationExpander pluginConfigurationExpander; |
| private final ReportConfigurationExpander reportConfigurationExpander; |
| private final ReportingConverter reportingConverter; |
| private final ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator; |
| |
| @Inject |
| public DefaultModelBuilder( |
| ModelProcessor modelProcessor, |
| ModelValidator modelValidator, |
| ModelNormalizer modelNormalizer, |
| ModelInterpolator modelInterpolator, |
| ModelPathTranslator modelPathTranslator, |
| ModelUrlNormalizer modelUrlNormalizer, |
| SuperPomProvider superPomProvider, |
| InheritanceAssembler inheritanceAssembler, |
| ProfileSelector profileSelector, |
| ProfileInjector profileInjector, |
| PluginManagementInjector pluginManagementInjector, |
| DependencyManagementInjector dependencyManagementInjector, |
| DependencyManagementImporter dependencyManagementImporter, |
| @Nullable LifecycleBindingsInjector lifecycleBindingsInjector, |
| PluginConfigurationExpander pluginConfigurationExpander, |
| ReportConfigurationExpander reportConfigurationExpander, |
| ReportingConverter reportingConverter, |
| ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator ) |
| { |
| this.modelProcessor = modelProcessor; |
| this.modelValidator = modelValidator; |
| this.modelNormalizer = modelNormalizer; |
| this.modelInterpolator = modelInterpolator; |
| this.modelPathTranslator = modelPathTranslator; |
| this.modelUrlNormalizer = modelUrlNormalizer; |
| this.superPomProvider = superPomProvider; |
| this.inheritanceAssembler = inheritanceAssembler; |
| this.profileSelector = profileSelector; |
| this.profileInjector = profileInjector; |
| this.pluginManagementInjector = pluginManagementInjector; |
| this.dependencyManagementInjector = dependencyManagementInjector; |
| this.dependencyManagementImporter = dependencyManagementImporter; |
| this.lifecycleBindingsInjector = lifecycleBindingsInjector; |
| this.pluginConfigurationExpander = pluginConfigurationExpander; |
| this.reportConfigurationExpander = reportConfigurationExpander; |
| this.reportingConverter = reportingConverter; |
| this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator; |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#set |
| */ |
| @Deprecated |
| public DefaultModelBuilder setModelProcessor( ModelProcessor modelProcessor ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setModelProcessor(ModelProcessor) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setModelValidator( ModelValidator modelValidator ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setModelNormalizer(ModelNormalizer) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setModelNormalizer( ModelNormalizer modelNormalizer ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setModelInterpolator(ModelInterpolator) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setModelInterpolator( ModelInterpolator modelInterpolator ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#set |
| */ |
| @Deprecated |
| public DefaultModelBuilder setModelPathTranslator( ModelPathTranslator modelPathTranslator ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setModelUrlNormalizer(ModelUrlNormalizer) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setModelUrlNormalizer( ModelUrlNormalizer modelUrlNormalizer ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setSuperPomProvider(SuperPomProvider) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setSuperPomProvider( SuperPomProvider superPomProvider ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setInheritanceAssembler(InheritanceAssembler) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setInheritanceAssembler( InheritanceAssembler inheritanceAssembler ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#set |
| */ |
| @Deprecated |
| public DefaultModelBuilder setProfileSelector( ProfileSelector profileSelector ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setProfileInjector(ProfileInjector) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setProfileInjector( ProfileInjector profileInjector ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setPluginManagementInjector(PluginManagementInjector) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setPluginManagementInjector( PluginManagementInjector pluginManagementInjector ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setDependencyManagementInjector(DependencyManagementInjector) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setDependencyManagementInjector( |
| DependencyManagementInjector dependencyManagementInjector ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setDependencyManagementImporter(DependencyManagementImporter) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setDependencyManagementImporter( |
| DependencyManagementImporter dependencyManagementImporter ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setLifecycleBindingsInjector(LifecycleBindingsInjector) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setLifecycleBindingsInjector( LifecycleBindingsInjector lifecycleBindingsInjector ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setPluginConfigurationExpander(PluginConfigurationExpander) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setPluginConfigurationExpander( PluginConfigurationExpander pluginConfigurationExpander ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setReportConfigurationExpander(ReportConfigurationExpander) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setReportConfigurationExpander( ReportConfigurationExpander reportConfigurationExpander ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setReportingConverter(ReportingConverter) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setReportingConverter( ReportingConverter reportingConverter ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| /** |
| * @deprecated since Maven 4 |
| * @see DefaultModelBuilderFactory#setProfileActivationFilePathInterpolator(ProfileActivationFilePathInterpolator) |
| */ |
| @Deprecated |
| public DefaultModelBuilder setProfileActivationFilePathInterpolator( |
| ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator ) |
| { |
| return new DefaultModelBuilder( modelProcessor, modelValidator, modelNormalizer, modelInterpolator, |
| modelPathTranslator, modelUrlNormalizer, superPomProvider, inheritanceAssembler, profileSelector, |
| profileInjector, pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, |
| lifecycleBindingsInjector, pluginConfigurationExpander, reportConfigurationExpander, |
| reportingConverter, profileActivationFilePathInterpolator ); |
| } |
| |
| @Override |
| public DefaultTransformerContextBuilder newTransformerContextBuilder() |
| { |
| return new DefaultTransformerContextBuilder(); |
| } |
| |
| @Override |
| public ModelBuildingResult build( ModelBuildingRequest request ) |
| throws ModelBuildingException |
| { |
| return build( request, new LinkedHashSet<>() ); |
| } |
| |
| protected ModelBuildingResult build( ModelBuildingRequest request, Collection<String> importIds ) |
| throws ModelBuildingException |
| { |
| // phase 1 |
| DefaultModelBuildingResult result = new DefaultModelBuildingResult(); |
| |
| DefaultModelProblemCollector problems = new DefaultModelProblemCollector( result ); |
| |
| // read and validate raw model |
| Model fileModel = readFileModel( request, problems ); |
| |
| request.setFileModel( fileModel ); |
| result.setFileModel( fileModel ); |
| |
| activateFileModel( request, result, problems ); |
| |
| if ( !request.isTwoPhaseBuilding() ) |
| { |
| return build( request, result, importIds ); |
| } |
| else if ( hasModelErrors( problems ) ) |
| { |
| throw problems.newModelBuildingException(); |
| } |
| |
| return result; |
| } |
| |
| private void activateFileModel( final ModelBuildingRequest request, final DefaultModelBuildingResult result, |
| DefaultModelProblemCollector problems ) |
| throws ModelBuildingException |
| { |
| Model inputModel = request.getFileModel(); |
| problems.setRootModel( inputModel ); |
| |
| // profile activation |
| DefaultProfileActivationContext profileActivationContext = getProfileActivationContext( request ); |
| |
| problems.setSource( "(external profiles)" ); |
| List<Profile> activeExternalProfiles = profileSelector.getActiveProfiles( request.getProfiles(), |
| profileActivationContext, problems ); |
| |
| result.setActiveExternalProfiles( activeExternalProfiles ); |
| |
| if ( !activeExternalProfiles.isEmpty() ) |
| { |
| Properties profileProps = new Properties(); |
| for ( Profile profile : activeExternalProfiles ) |
| { |
| profileProps.putAll( profile.getProperties() ); |
| } |
| profileProps.putAll( profileActivationContext.getUserProperties() ); |
| profileActivationContext.setUserProperties( profileProps ); |
| } |
| |
| profileActivationContext.setProjectProperties( inputModel.getProperties() ); |
| problems.setSource( inputModel ); |
| List<Profile> activePomProfiles = profileSelector.getActiveProfiles( inputModel.getProfiles(), |
| profileActivationContext, problems ); |
| |
| // model normalization |
| problems.setSource( inputModel ); |
| modelNormalizer.mergeDuplicates( inputModel, request, problems ); |
| |
| Map<String, Activation> interpolatedActivations = getProfileActivations( inputModel, false ); |
| injectProfileActivations( inputModel, interpolatedActivations ); |
| |
| // profile injection |
| for ( Profile activeProfile : activePomProfiles ) |
| { |
| profileInjector.injectProfile( inputModel, activeProfile, request, problems ); |
| } |
| |
| for ( Profile activeProfile : activeExternalProfiles ) |
| { |
| profileInjector.injectProfile( inputModel, activeProfile, request, problems ); |
| } |
| } |
| |
| @SuppressWarnings( "checkstyle:methodlength" ) |
| private Model readEffectiveModel( final ModelBuildingRequest request, final DefaultModelBuildingResult result, |
| DefaultModelProblemCollector problems ) |
| throws ModelBuildingException |
| { |
| Model inputModel = |
| readRawModel( request, problems ); |
| |
| problems.setRootModel( inputModel ); |
| |
| ModelData resultData = new ModelData( request.getModelSource(), inputModel ); |
| ModelData superData = new ModelData( null, getSuperModel() ); |
| |
| // profile activation |
| DefaultProfileActivationContext profileActivationContext = getProfileActivationContext( request ); |
| |
| List<Profile> activeExternalProfiles = result.getActiveExternalProfiles(); |
| |
| if ( !activeExternalProfiles.isEmpty() ) |
| { |
| Properties profileProps = new Properties(); |
| for ( Profile profile : activeExternalProfiles ) |
| { |
| profileProps.putAll( profile.getProperties() ); |
| } |
| profileProps.putAll( profileActivationContext.getUserProperties() ); |
| profileActivationContext.setUserProperties( profileProps ); |
| } |
| |
| Collection<String> parentIds = new LinkedHashSet<>(); |
| |
| List<Model> lineage = new ArrayList<>(); |
| |
| for ( ModelData currentData = resultData; ; ) |
| { |
| String modelId = currentData.getId(); |
| result.addModelId( modelId ); |
| |
| Model rawModel = currentData.getModel(); |
| result.setRawModel( modelId, rawModel ); |
| |
| profileActivationContext.setProjectProperties( rawModel.getProperties() ); |
| problems.setSource( rawModel ); |
| List<Profile> activePomProfiles = profileSelector.getActiveProfiles( rawModel.getProfiles(), |
| profileActivationContext, problems ); |
| result.setActivePomProfiles( modelId, activePomProfiles ); |
| |
| Model tmpModel = rawModel.clone(); |
| |
| problems.setSource( tmpModel ); |
| |
| // model normalization |
| modelNormalizer.mergeDuplicates( tmpModel, request, problems ); |
| |
| profileActivationContext.setProjectProperties( tmpModel.getProperties() ); |
| |
| Map<String, Activation> interpolatedActivations = getInterpolatedActivations( rawModel, |
| profileActivationContext, |
| problems ); |
| injectProfileActivations( tmpModel, interpolatedActivations ); |
| |
| // profile injection |
| for ( Profile activeProfile : result.getActivePomProfiles( modelId ) ) |
| { |
| profileInjector.injectProfile( tmpModel, activeProfile, request, problems ); |
| } |
| |
| if ( currentData == resultData ) |
| { |
| for ( Profile activeProfile : activeExternalProfiles ) |
| { |
| profileInjector.injectProfile( tmpModel, activeProfile, request, problems ); |
| } |
| result.setEffectiveModel( tmpModel ); |
| } |
| |
| lineage.add( tmpModel ); |
| |
| if ( currentData == superData ) |
| { |
| break; |
| } |
| |
| configureResolver( request.getModelResolver(), tmpModel, problems ); |
| |
| ModelData parentData = |
| readParent( currentData.getModel(), currentData.getSource(), request, result, problems ); |
| |
| if ( parentData == null ) |
| { |
| currentData = superData; |
| } |
| else if ( !parentIds.add( parentData.getId() ) ) |
| { |
| StringBuilder message = new StringBuilder( "The parents form a cycle: " ); |
| for ( String parentId : parentIds ) |
| { |
| message.append( parentId ).append( " -> " ); |
| } |
| message.append( parentData.getId() ); |
| |
| problems.add( new ModelProblemCollectorRequest( ModelProblem.Severity.FATAL, ModelProblem.Version.BASE ) |
| .setMessage( message.toString() ) ); |
| |
| throw problems.newModelBuildingException(); |
| } |
| else |
| { |
| currentData = parentData; |
| } |
| } |
| |
| problems.setSource( result.getRawModel() ); |
| checkPluginVersions( lineage, request, problems ); |
| |
| // inheritance assembly |
| assembleInheritance( lineage, request, problems ); |
| |
| Model resultModel = lineage.get( 0 ); |
| |
| // consider caching inherited model |
| |
| problems.setSource( resultModel ); |
| problems.setRootModel( resultModel ); |
| |
| // model interpolation |
| resultModel = interpolateModel( resultModel, request, problems ); |
| |
| // url normalization |
| modelUrlNormalizer.normalize( resultModel, request ); |
| |
| result.setEffectiveModel( resultModel ); |
| |
| // Now the fully interpolated model is available: reconfigure the resolver |
| configureResolver( request.getModelResolver(), resultModel, problems, true ); |
| |
| return resultModel; |
| } |
| |
| private Map<String, Activation> getInterpolatedActivations( Model rawModel, |
| DefaultProfileActivationContext context, |
| DefaultModelProblemCollector problems ) |
| { |
| Map<String, Activation> interpolatedActivations = getProfileActivations( rawModel, true ); |
| for ( Activation activation : interpolatedActivations.values() ) |
| { |
| if ( activation.getFile() != null ) |
| { |
| replaceWithInterpolatedValue( activation.getFile(), context, problems ); |
| } |
| } |
| return interpolatedActivations; |
| } |
| |
| private void replaceWithInterpolatedValue( ActivationFile activationFile, ProfileActivationContext context, |
| DefaultModelProblemCollector problems ) |
| { |
| try |
| { |
| if ( isNotEmpty( activationFile.getExists() ) ) |
| { |
| String path = activationFile.getExists(); |
| String absolutePath = profileActivationFilePathInterpolator.interpolate( path, context ); |
| activationFile.setExists( absolutePath ); |
| } |
| else if ( isNotEmpty( activationFile.getMissing() ) ) |
| { |
| String path = activationFile.getMissing(); |
| String absolutePath = profileActivationFilePathInterpolator.interpolate( path, context ); |
| activationFile.setMissing( absolutePath ); |
| } |
| } |
| catch ( InterpolationException e ) |
| { |
| String path = isNotEmpty( |
| activationFile.getExists() ) ? activationFile.getExists() : activationFile.getMissing(); |
| |
| problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage( |
| "Failed to interpolate file location " + path + ": " + e.getMessage() ).setLocation( |
| activationFile.getLocation( isNotEmpty( activationFile.getExists() ) ? "exists" : "missing" ) ) |
| .setException( e ) ); |
| } |
| } |
| |
| private static boolean isNotEmpty( String string ) |
| { |
| return string != null && !string.isEmpty(); |
| } |
| |
| @Override |
| public ModelBuildingResult build( final ModelBuildingRequest request, final ModelBuildingResult result ) |
| throws ModelBuildingException |
| { |
| return build( request, result, new LinkedHashSet<>() ); |
| } |
| |
| private ModelBuildingResult build( final ModelBuildingRequest request, final ModelBuildingResult phaseOneResult, |
| Collection<String> imports ) |
| throws ModelBuildingException |
| { |
| DefaultModelBuildingResult result = asDefaultModelBuildingResult( phaseOneResult ); |
| |
| DefaultModelProblemCollector problems = new DefaultModelProblemCollector( result ); |
| |
| // phase 2 |
| Model resultModel = readEffectiveModel( request, result, problems ); |
| problems.setSource( resultModel ); |
| problems.setRootModel( resultModel ); |
| |
| // model path translation |
| modelPathTranslator.alignToBaseDirectory( resultModel, resultModel.getProjectDirectory(), request ); |
| |
| // plugin management injection |
| pluginManagementInjector.injectManagement( resultModel, request, problems ); |
| |
| fireEvent( resultModel, request, problems, ModelBuildingEventCatapult.BUILD_EXTENSIONS_ASSEMBLED ); |
| |
| if ( request.isProcessPlugins() ) |
| { |
| if ( lifecycleBindingsInjector == null ) |
| { |
| throw new IllegalStateException( "lifecycle bindings injector is missing" ); |
| } |
| |
| // lifecycle bindings injection |
| lifecycleBindingsInjector.injectLifecycleBindings( resultModel, request, problems ); |
| } |
| |
| // dependency management import |
| importDependencyManagement( resultModel, request, problems, imports ); |
| |
| // dependency management injection |
| dependencyManagementInjector.injectManagement( resultModel, request, problems ); |
| |
| modelNormalizer.injectDefaultValues( resultModel, request, problems ); |
| |
| if ( request.isProcessPlugins() ) |
| { |
| // reports configuration |
| reportConfigurationExpander.expandPluginConfiguration( resultModel, request, problems ); |
| |
| // reports conversion to decoupled site plugin |
| reportingConverter.convertReporting( resultModel, request, problems ); |
| |
| // plugins configuration |
| pluginConfigurationExpander.expandPluginConfiguration( resultModel, request, problems ); |
| } |
| |
| // effective model validation |
| modelValidator.validateEffectiveModel( resultModel, request, problems ); |
| |
| if ( hasModelErrors( problems ) ) |
| { |
| throw problems.newModelBuildingException(); |
| } |
| |
| return result; |
| } |
| |
| private DefaultModelBuildingResult asDefaultModelBuildingResult( ModelBuildingResult phaseOneResult ) |
| { |
| if ( phaseOneResult instanceof DefaultModelBuildingResult ) |
| { |
| return (DefaultModelBuildingResult) phaseOneResult; |
| } |
| else |
| { |
| return new DefaultModelBuildingResult( phaseOneResult ); |
| } |
| } |
| |
| @Override |
| public Result<? extends Model> buildRawModel( File pomFile, int validationLevel, boolean locationTracking ) |
| { |
| final ModelBuildingRequest request = new DefaultModelBuildingRequest().setValidationLevel( validationLevel ) |
| .setLocationTracking( locationTracking ) |
| .setModelSource( new FileModelSource( pomFile ) ); |
| final DefaultModelProblemCollector collector = |
| new DefaultModelProblemCollector( new DefaultModelBuildingResult() ); |
| try |
| { |
| return newResult( readFileModel( request, collector ), collector.getProblems() ); |
| } |
| catch ( ModelBuildingException e ) |
| { |
| return error( collector.getProblems() ); |
| } |
| } |
| |
| private Model readFileModel( ModelBuildingRequest request, |
| DefaultModelProblemCollector problems ) |
| throws ModelBuildingException |
| { |
| ModelSource modelSource = request.getModelSource(); |
| Model model = fromCache( request.getModelCache(), modelSource, ModelCacheTag.FILE ); |
| if ( model == null ) |
| { |
| model = doReadFileModel( modelSource, request, problems ); |
| |
| intoCache( request.getModelCache(), modelSource, ModelCacheTag.FILE, model ); |
| } |
| |
| if ( modelSource instanceof FileModelSource ) |
| { |
| if ( request.getTransformerContextBuilder() instanceof DefaultTransformerContextBuilder ) |
| { |
| DefaultTransformerContextBuilder contextBuilder = |
| (DefaultTransformerContextBuilder) request.getTransformerContextBuilder(); |
| contextBuilder.putSource( getGroupId( model ), model.getArtifactId(), modelSource ); |
| } |
| } |
| |
| return model; |
| } |
| |
| @SuppressWarnings( "checkstyle:methodlength" ) |
| private Model doReadFileModel( ModelSource modelSource, ModelBuildingRequest request, |
| DefaultModelProblemCollector problems ) |
| throws ModelBuildingException |
| { |
| Model model; |
| problems.setSource( modelSource.getLocation() ); |
| try |
| { |
| boolean strict = request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0; |
| |
| Map<String, Object> options = new HashMap<>( 3 ); |
| options.put( ModelProcessor.IS_STRICT, strict ); |
| options.put( ModelProcessor.SOURCE, modelSource ); |
| |
| InputSource source; |
| if ( request.isLocationTracking() ) |
| { |
| source = (InputSource) options.computeIfAbsent( ModelProcessor.INPUT_SOURCE, k -> new InputSource() ); |
| } |
| else |
| { |
| source = null; |
| } |
| |
| try |
| { |
| model = modelProcessor.read( modelSource.getInputStream(), options ); |
| } |
| catch ( ModelParseException e ) |
| { |
| if ( !strict ) |
| { |
| throw e; |
| } |
| |
| options.put( ModelProcessor.IS_STRICT, Boolean.FALSE ); |
| |
| try |
| { |
| model = modelProcessor.read( modelSource.getInputStream(), options ); |
| } |
| catch ( ModelParseException ne ) |
| { |
| // still unreadable even in non-strict mode, rethrow original error |
| throw e; |
| } |
| |
| Severity severity = ( modelSource instanceof FileModelSource ) ? Severity.ERROR : Severity.WARNING; |
| problems.add( new ModelProblemCollectorRequest( severity, Version.V20 ) |
| .setMessage( "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage() ) |
| .setException( e ) ); |
| } |
| |
| if ( source != null ) |
| { |
| source.setModelId( ModelProblemUtils.toId( model ) ); |
| source.setLocation( modelSource.getLocation() ); |
| } |
| } |
| catch ( ModelParseException e ) |
| { |
| problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) |
| .setMessage( "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage() ) |
| .setException( e ) ); |
| throw problems.newModelBuildingException(); |
| } |
| catch ( IOException e ) |
| { |
| String msg = e.getMessage(); |
| if ( msg == null || msg.length() <= 0 ) |
| { |
| // NOTE: There's java.nio.charset.MalformedInputException and sun.io.MalformedInputException |
| if ( e.getClass().getName().endsWith( "MalformedInputException" ) ) |
| { |
| msg = "Some input bytes do not match the file encoding."; |
| } |
| else |
| { |
| msg = e.getClass().getSimpleName(); |
| } |
| } |
| problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) |
| .setMessage( "Non-readable POM " + modelSource.getLocation() + ": " + msg ).setException( e ) ); |
| throw problems.newModelBuildingException(); |
| } |
| |
| if ( modelSource instanceof FileModelSource ) |
| { |
| model.setPomFile( ( (FileModelSource) modelSource ).getFile() ); |
| } |
| problems.setSource( model ); |
| |
| modelValidator.validateFileModel( model, request, problems ); |
| |
| if ( hasFatalErrors( problems ) ) |
| { |
| throw problems.newModelBuildingException(); |
| } |
| |
| return model; |
| } |
| |
| private Model readRawModel( ModelBuildingRequest request, DefaultModelProblemCollector problems ) |
| throws ModelBuildingException |
| { |
| ModelSource modelSource = request.getModelSource(); |
| |
| ModelData cachedData = fromCache( request.getModelCache(), modelSource, ModelCacheTag.RAW ); |
| if ( cachedData != null ) |
| { |
| return cachedData.getModel(); |
| } |
| |
| Model rawModel; |
| if ( Features.buildConsumer( request.getUserProperties() ).isActive() |
| && modelSource instanceof FileModelSource ) |
| { |
| rawModel = readFileModel( request, problems ); |
| File pomFile = ( (FileModelSource) modelSource ).getFile(); |
| |
| TransformerContext context = null; |
| if ( request.getTransformerContextBuilder() != null ) |
| { |
| context = request.getTransformerContextBuilder().initialize( request, problems ); |
| } |
| |
| try |
| { |
| // must implement TransformContext, but should use request to access system properties/modelcache |
| Model transformedFileModel = modelProcessor.read( pomFile, |
| Collections.singletonMap( ModelReader.TRANSFORMER_CONTEXT, context ) ); |
| |
| // rawModel with locationTrackers, required for proper feedback during validations |
| |
| // Apply enriched data |
| modelMerger.merge( rawModel, transformedFileModel, false, null ); |
| } |
| catch ( IOException e ) |
| { |
| problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V40 ).setException( e ) ); |
| } |
| } |
| else if ( request.getFileModel() == null ) |
| { |
| rawModel = readFileModel( request, problems ); |
| } |
| else |
| { |
| rawModel = request.getFileModel().clone(); |
| } |
| |
| modelValidator.validateRawModel( rawModel, request, problems ); |
| |
| if ( hasFatalErrors( problems ) ) |
| { |
| throw problems.newModelBuildingException(); |
| } |
| |
| String groupId = getGroupId( rawModel ); |
| String artifactId = rawModel.getArtifactId(); |
| String version = getVersion( rawModel ); |
| |
| ModelData modelData = new ModelData( modelSource, rawModel, groupId, artifactId, version ); |
| intoCache( request.getModelCache(), modelSource, ModelCacheTag.RAW, modelData ); |
| |
| return rawModel; |
| } |
| |
| private String getGroupId( Model model ) |
| { |
| String groupId = model.getGroupId(); |
| if ( groupId == null && model.getParent() != null ) |
| { |
| groupId = model.getParent().getGroupId(); |
| } |
| return groupId; |
| } |
| |
| private String getVersion( Model model ) |
| { |
| String version = model.getVersion(); |
| if ( version == null && model.getParent() != null ) |
| { |
| version = model.getParent().getVersion(); |
| } |
| return version; |
| } |
| |
| private DefaultProfileActivationContext getProfileActivationContext( ModelBuildingRequest request ) |
| { |
| DefaultProfileActivationContext context = new DefaultProfileActivationContext(); |
| |
| context.setActiveProfileIds( request.getActiveProfileIds() ); |
| context.setInactiveProfileIds( request.getInactiveProfileIds() ); |
| context.setSystemProperties( request.getSystemProperties() ); |
| context.setUserProperties( request.getUserProperties() ); |
| context.setProjectDirectory( ( request.getPomFile() != null ) ? request.getPomFile().getParentFile() : null ); |
| |
| return context; |
| } |
| |
| private void configureResolver( ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems ) |
| { |
| configureResolver( modelResolver, model, problems, false ); |
| } |
| |
| private void configureResolver( ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems, |
| boolean replaceRepositories ) |
| { |
| if ( modelResolver == null ) |
| { |
| return; |
| } |
| |
| problems.setSource( model ); |
| |
| List<Repository> repositories = model.getRepositories(); |
| |
| for ( Repository repository : repositories ) |
| { |
| try |
| { |
| modelResolver.addRepository( repository, replaceRepositories ); |
| } |
| catch ( InvalidRepositoryException e ) |
| { |
| problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) |
| .setMessage( "Invalid repository " + repository.getId() + ": " + e.getMessage() ) |
| .setLocation( repository.getLocation( "" ) ).setException( e ) ); |
| } |
| } |
| } |
| |
| private void checkPluginVersions( List<Model> lineage, ModelBuildingRequest request, |
| ModelProblemCollector problems ) |
| { |
| if ( request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 ) |
| { |
| return; |
| } |
| |
| Map<String, Plugin> plugins = new HashMap<>(); |
| Map<String, String> versions = new HashMap<>(); |
| Map<String, String> managedVersions = new HashMap<>(); |
| |
| for ( int i = lineage.size() - 1; i >= 0; i-- ) |
| { |
| Model model = lineage.get( i ); |
| Build build = model.getBuild(); |
| if ( build != null ) |
| { |
| for ( Plugin plugin : build.getPlugins() ) |
| { |
| String key = plugin.getKey(); |
| if ( versions.get( key ) == null ) |
| { |
| versions.put( key, plugin.getVersion() ); |
| plugins.put( key, plugin ); |
| } |
| } |
| PluginManagement mgmt = build.getPluginManagement(); |
| if ( mgmt != null ) |
| { |
| for ( Plugin plugin : mgmt.getPlugins() ) |
| { |
| String key = plugin.getKey(); |
| managedVersions.computeIfAbsent( key, k -> plugin.getVersion() ); |
| } |
| } |
| } |
| } |
| |
| for ( String key : versions.keySet() ) |
| { |
| if ( versions.get( key ) == null && managedVersions.get( key ) == null ) |
| { |
| InputLocation location = plugins.get( key ).getLocation( "" ); |
| problems |
| .add( new ModelProblemCollectorRequest( Severity.WARNING, Version.V20 ) |
| .setMessage( "'build.plugins.plugin.version' for " + key + " is missing." ) |
| .setLocation( location ) ); |
| } |
| } |
| } |
| |
| private void assembleInheritance( List<Model> lineage, ModelBuildingRequest request, |
| ModelProblemCollector problems ) |
| { |
| for ( int i = lineage.size() - 2; i >= 0; i-- ) |
| { |
| Model parent = lineage.get( i + 1 ); |
| Model child = lineage.get( i ); |
| inheritanceAssembler.assembleModelInheritance( child, parent, request, problems ); |
| } |
| } |
| |
| private Map<String, Activation> getProfileActivations( Model model, boolean clone ) |
| { |
| Map<String, Activation> activations = new HashMap<>(); |
| for ( Profile profile : model.getProfiles() ) |
| { |
| Activation activation = profile.getActivation(); |
| |
| if ( activation == null ) |
| { |
| continue; |
| } |
| |
| if ( clone ) |
| { |
| activation = activation.clone(); |
| } |
| |
| activations.put( profile.getId(), activation ); |
| } |
| |
| return activations; |
| } |
| |
| private void injectProfileActivations( Model model, Map<String, Activation> activations ) |
| { |
| for ( Profile profile : model.getProfiles() ) |
| { |
| Activation activation = profile.getActivation(); |
| |
| if ( activation == null ) |
| { |
| continue; |
| } |
| |
| // restore activation |
| profile.setActivation( activations.get( profile.getId() ) ); |
| } |
| } |
| |
| private Model interpolateModel( Model model, ModelBuildingRequest request, ModelProblemCollector problems ) |
| { |
| // save profile activations before interpolation, since they are evaluated with limited scope |
| Map<String, Activation> originalActivations = getProfileActivations( model, true ); |
| |
| Model interpolatedModel = |
| modelInterpolator.interpolateModel( model, model.getProjectDirectory(), request, problems ); |
| if ( interpolatedModel.getParent() != null ) |
| { |
| StringSearchInterpolator ssi = new StringSearchInterpolator(); |
| ssi.addValueSource( new MapBasedValueSource( request.getUserProperties() ) ); |
| |
| ssi.addValueSource( new MapBasedValueSource( model.getProperties() ) ); |
| |
| ssi.addValueSource( new MapBasedValueSource( request.getSystemProperties() ) ); |
| |
| try |
| { |
| String interpolated = ssi.interpolate( interpolatedModel.getParent().getVersion() ); |
| interpolatedModel.getParent().setVersion( interpolated ); |
| } |
| catch ( Exception e ) |
| { |
| ModelProblemCollectorRequest mpcr = |
| new ModelProblemCollectorRequest( Severity.ERROR, |
| Version.BASE ).setMessage( "Failed to interpolate field: " |
| + interpolatedModel.getParent().getVersion() |
| + " on class: " ).setException( e ); |
| problems.add( mpcr ); |
| } |
| |
| |
| } |
| interpolatedModel.setPomFile( model.getPomFile() ); |
| |
| // restore profiles with file activation to their value before full interpolation |
| injectProfileActivations( model, originalActivations ); |
| |
| return interpolatedModel; |
| } |
| |
| private ModelData readParent( Model childModel, Source childSource, ModelBuildingRequest request, |
| ModelBuildingResult result, DefaultModelProblemCollector problems ) |
| throws ModelBuildingException |
| { |
| ModelData parentData = null; |
| |
| Parent parent = childModel.getParent(); |
| if ( parent != null ) |
| { |
| parentData = readParentLocally( childModel, childSource, request, result, problems ); |
| if ( parentData == null ) |
| { |
| parentData = readParentExternally( childModel, request, result, problems ); |
| } |
| |
| Model parentModel = parentData.getModel(); |
| if ( !"pom".equals( parentModel.getPackaging() ) ) |
| { |
| problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) |
| .setMessage( "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint( parentModel ) |
| + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"" ) |
| .setLocation( parentModel.getLocation( "packaging" ) ) ); |
| } |
| } |
| |
| return parentData; |
| } |
| |
| private ModelData readParentLocally( Model childModel, Source childSource, ModelBuildingRequest request, |
| ModelBuildingResult result, DefaultModelProblemCollector problems ) |
| throws ModelBuildingException |
| { |
| final Parent parent = childModel.getParent(); |
| final ModelSource candidateSource; |
| final Model candidateModel; |
| final WorkspaceModelResolver resolver = request.getWorkspaceModelResolver(); |
| if ( resolver == null ) |
| { |
| candidateSource = getParentPomFile( childModel, childSource ); |
| |
| if ( candidateSource == null ) |
| { |
| return null; |
| } |
| |
| ModelBuildingRequest candidateBuildRequest = new DefaultModelBuildingRequest( request ) |
| .setModelSource( candidateSource ); |
| |
| candidateModel = readRawModel( candidateBuildRequest, problems ); |
| } |
| else |
| { |
| try |
| { |
| candidateModel = |
| resolver.resolveRawModel( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() ); |
| } |
| catch ( UnresolvableModelException e ) |
| { |
| problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) // |
| .setMessage( e.getMessage() ).setLocation( parent.getLocation( "" ) ).setException( e ) ); |
| throw problems.newModelBuildingException(); |
| } |
| if ( candidateModel == null ) |
| { |
| return null; |
| } |
| candidateSource = new FileModelSource( candidateModel.getPomFile() ); |
| } |
| |
| // |
| // TODO jvz Why isn't all this checking the job of the duty of the workspace resolver, we know that we |
| // have a model that is suitable, yet more checks are done here and the one for the version is problematic |
| // before because with parents as ranges it will never work in this scenario. |
| // |
| |
| String groupId = getGroupId( candidateModel ); |
| String artifactId = candidateModel.getArtifactId(); |
| |
| if ( groupId == null || !groupId.equals( parent.getGroupId() ) || artifactId == null |
| || !artifactId.equals( parent.getArtifactId() ) ) |
| { |
| StringBuilder buffer = new StringBuilder( 256 ); |
| buffer.append( "'parent.relativePath'" ); |
| if ( childModel != problems.getRootModel() ) |
| { |
| buffer.append( " of POM " ).append( ModelProblemUtils.toSourceHint( childModel ) ); |
| } |
| buffer.append( " points at " ).append( groupId ).append( ':' ).append( artifactId ); |
| buffer.append( " instead of " ).append( parent.getGroupId() ).append( ':' ); |
| buffer.append( parent.getArtifactId() ).append( ", please verify your project structure" ); |
| |
| problems.setSource( childModel ); |
| problems.add( new ModelProblemCollectorRequest( Severity.WARNING, Version.BASE ) |
| .setMessage( buffer.toString() ).setLocation( parent.getLocation( "" ) ) ); |
| return null; |
| } |
| |
| String version = getVersion( candidateModel ); |
| if ( version != null && parent.getVersion() != null && !version.equals( parent.getVersion() ) ) |
| { |
| try |
| { |
| VersionRange parentRange = VersionRange.createFromVersionSpec( parent.getVersion() ); |
| if ( !parentRange.hasRestrictions() ) |
| { |
| // the parent version is not a range, we have version skew, drop back to resolution from repo |
| return null; |
| } |
| if ( !parentRange.containsVersion( new DefaultArtifactVersion( version ) ) ) |
| { |
| // version skew drop back to resolution from the repository |
| return null; |
| } |
| |
| // Validate versions aren't inherited when using parent ranges the same way as when read externally. |
| String rawChildModelVersion = childModel.getVersion(); |
| if ( rawChildModelVersion == null ) |
| { |
| // Message below is checked for in the MNG-2199 core IT. |
| problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ) |
| .setMessage( "Version must be a constant" ).setLocation( childModel.getLocation( "" ) ) ); |
| |
| } |
| else |
| { |
| if ( rawChildVersionReferencesParent( rawChildModelVersion ) ) |
| { |
| // Message below is checked for in the MNG-2199 core IT. |
| problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ) |
| .setMessage( "Version must be a constant" ) |
| .setLocation( childModel.getLocation( "version" ) ) ); |
| |
| } |
| } |
| |
| // MNG-2199: What else to check here ? |
| } |
| catch ( InvalidVersionSpecificationException e ) |
| { |
| // invalid version range, so drop back to resolution from the repository |
| return null; |
| } |
| } |
| |
| // |
| // Here we just need to know that a version is fine to use but this validation we can do in our workspace |
| // resolver. |
| // |
| |
| /* |
| * if ( version == null || !version.equals( parent.getVersion() ) ) { return null; } |
| */ |
| |
| return new ModelData( candidateSource, candidateModel, groupId, artifactId, version ); |
| } |
| |
| private boolean rawChildVersionReferencesParent( String rawChildModelVersion ) |
| { |
| return rawChildModelVersion.equals( "${project.version}" ) |
| || rawChildModelVersion.equals( "${project.parent.version}" ); |
| } |
| |
| private ModelSource getParentPomFile( Model childModel, Source source ) |
| { |
| if ( !( source instanceof ModelSource2 ) ) |
| { |
| return null; |
| } |
| |
| String parentPath = childModel.getParent().getRelativePath(); |
| |
| if ( parentPath == null || parentPath.length() <= 0 ) |
| { |
| return null; |
| } |
| |
| return ( (ModelSource2) source ).getRelatedSource( parentPath ); |
| } |
| |
| private ModelData readParentExternally( Model childModel, ModelBuildingRequest request, |
| ModelBuildingResult result, DefaultModelProblemCollector problems ) |
| throws ModelBuildingException |
| { |
| problems.setSource( childModel ); |
| |
| Parent parent = childModel.getParent(); |
| |
| String groupId = parent.getGroupId(); |
| String artifactId = parent.getArtifactId(); |
| String version = parent.getVersion(); |
| |
| ModelResolver modelResolver = request.getModelResolver(); |
| Objects.requireNonNull( modelResolver, |
| String.format( "request.modelResolver cannot be null (parent POM %s and POM %s)", |
| ModelProblemUtils.toId( groupId, artifactId, version ), |
| ModelProblemUtils.toSourceHint( childModel ) ) ); |
| |
| ModelSource modelSource; |
| try |
| { |
| modelSource = modelResolver.resolveModel( parent ); |
| } |
| catch ( UnresolvableModelException e ) |
| { |
| // Message below is checked for in the MNG-2199 core IT. |
| StringBuilder buffer = new StringBuilder( 256 ); |
| buffer.append( "Non-resolvable parent POM" ); |
| if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) ) |
| { |
| buffer.append( ' ' ).append( ModelProblemUtils.toId( groupId, artifactId, version ) ); |
| } |
| if ( childModel != problems.getRootModel() ) |
| { |
| buffer.append( " for " ).append( ModelProblemUtils.toId( childModel ) ); |
| } |
| buffer.append( ": " ).append( e.getMessage() ); |
| if ( childModel.getProjectDirectory() != null ) |
| { |
| if ( parent.getRelativePath() == null || parent.getRelativePath().length() <= 0 ) |
| { |
| buffer.append( " and 'parent.relativePath' points at no local POM" ); |
| } |
| else |
| { |
| buffer.append( " and 'parent.relativePath' points at wrong local POM" ); |
| } |
| } |
| |
| problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) |
| .setMessage( buffer.toString() ).setLocation( parent.getLocation( "" ) ).setException( e ) ); |
| throw problems.newModelBuildingException(); |
| } |
| |
| int validationLevel = Math.min( request.getValidationLevel(), ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 ); |
| ModelBuildingRequest lenientRequest = new DefaultModelBuildingRequest( request ) |
| .setValidationLevel( validationLevel ) |
| .setFileModel( null ) |
| .setModelSource( modelSource ); |
| |
| Model parentModel = readRawModel( lenientRequest, problems ); |
| |
| if ( !parent.getVersion().equals( version ) ) |
| { |
| String rawChildModelVersion = childModel.getVersion(); |
| |
| if ( rawChildModelVersion == null ) |
| { |
| // Message below is checked for in the MNG-2199 core IT. |
| problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ) |
| .setMessage( "Version must be a constant" ).setLocation( childModel.getLocation( "" ) ) ); |
| |
| } |
| else |
| { |
| if ( rawChildVersionReferencesParent( rawChildModelVersion ) ) |
| { |
| // Message below is checked for in the MNG-2199 core IT. |
| problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.V31 ) |
| .setMessage( "Version must be a constant" ) |
| .setLocation( childModel.getLocation( "version" ) ) ); |
| |
| } |
| } |
| |
| // MNG-2199: What else to check here ? |
| } |
| |
| return new ModelData( modelSource, parentModel, parent.getGroupId(), parent.getArtifactId(), |
| parent.getVersion() ); |
| } |
| |
| private Model getSuperModel() |
| { |
| return superPomProvider.getSuperModel( "4.0.0" ).clone(); |
| } |
| |
| private void importDependencyManagement( Model model, ModelBuildingRequest request, |
| DefaultModelProblemCollector problems, Collection<String> importIds ) |
| { |
| DependencyManagement depMgmt = model.getDependencyManagement(); |
| |
| if ( depMgmt == null ) |
| { |
| return; |
| } |
| |
| String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion(); |
| |
| importIds.add( importing ); |
| |
| List<DependencyManagement> importMgmts = null; |
| |
| for ( Iterator<Dependency> it = depMgmt.getDependencies().iterator(); it.hasNext(); ) |
| { |
| Dependency dependency = it.next(); |
| |
| if ( !"pom".equals( dependency.getType() ) || !"import".equals( dependency.getScope() ) ) |
| { |
| continue; |
| } |
| |
| it.remove(); |
| |
| DependencyManagement importMgmt = loadDependencyManagement( model, request, problems, |
| dependency, importIds ); |
| |
| if ( importMgmt != null ) |
| { |
| if ( importMgmts == null ) |
| { |
| importMgmts = new ArrayList<>(); |
| } |
| |
| importMgmts.add( importMgmt ); |
| } |
| } |
| |
| importIds.remove( importing ); |
| |
| dependencyManagementImporter.importManagement( model, importMgmts, request, problems ); |
| } |
| |
| private DependencyManagement loadDependencyManagement( Model model, ModelBuildingRequest request, |
| DefaultModelProblemCollector problems, |
| Dependency dependency, |
| Collection<String> importIds ) |
| { |
| String groupId = dependency.getGroupId(); |
| String artifactId = dependency.getArtifactId(); |
| String version = dependency.getVersion(); |
| |
| if ( groupId == null || groupId.length() <= 0 ) |
| { |
| problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) |
| .setMessage( "'dependencyManagement.dependencies.dependency.groupId' for " |
| + dependency.getManagementKey() + " is missing." ) |
| .setLocation( dependency.getLocation( "" ) ) ); |
| return null; |
| } |
| if ( artifactId == null || artifactId.length() <= 0 ) |
| { |
| problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) |
| .setMessage( "'dependencyManagement.dependencies.dependency.artifactId' for " |
| + dependency.getManagementKey() + " is missing." ) |
| .setLocation( dependency.getLocation( "" ) ) ); |
| return null; |
| } |
| if ( version == null || version.length() <= 0 ) |
| { |
| problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) |
| .setMessage( "'dependencyManagement.dependencies.dependency.version' for " |
| + dependency.getManagementKey() + " is missing." ) |
| .setLocation( dependency.getLocation( "" ) ) ); |
| return null; |
| } |
| |
| String imported = groupId + ':' + artifactId + ':' + version; |
| |
| if ( importIds.contains( imported ) ) |
| { |
| String message = "The dependencies of type=pom and with scope=import form a cycle: "; |
| for ( String modelId : importIds ) |
| { |
| message += modelId + " -> "; |
| } |
| message += imported; |
| problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage( message ) ); |
| |
| return null; |
| } |
| |
| DependencyManagement importMgmt = fromCache( request.getModelCache(), groupId, artifactId, version, |
| ModelCacheTag.IMPORT ); |
| if ( importMgmt == null ) |
| { |
| importMgmt = doLoadDependencyManagement( model, request, problems, dependency, |
| groupId, artifactId, version, importIds ); |
| if ( importMgmt != null ) |
| { |
| intoCache( request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, importMgmt ); |
| } |
| } |
| |
| return importMgmt; |
| } |
| |
| @SuppressWarnings( "checkstyle:parameternumber" ) |
| private DependencyManagement doLoadDependencyManagement( Model model, ModelBuildingRequest request, |
| DefaultModelProblemCollector problems, |
| Dependency dependency, |
| String groupId, |
| String artifactId, |
| String version, |
| Collection<String> importIds ) |
| { |
| DependencyManagement importMgmt; |
| final WorkspaceModelResolver workspaceResolver = request.getWorkspaceModelResolver(); |
| final ModelResolver modelResolver = request.getModelResolver(); |
| if ( workspaceResolver == null && modelResolver == null ) |
| { |
| throw new NullPointerException( String.format( |
| "request.workspaceModelResolver and request.modelResolver cannot be null (parent POM %s and POM %s)", |
| ModelProblemUtils.toId( groupId, artifactId, version ), |
| ModelProblemUtils.toSourceHint( model ) ) ); |
| } |
| |
| Model importModel = null; |
| if ( workspaceResolver != null ) |
| { |
| try |
| { |
| importModel = workspaceResolver.resolveEffectiveModel( groupId, artifactId, version ); |
| } |
| catch ( UnresolvableModelException e ) |
| { |
| problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) |
| .setMessage( e.getMessage() ).setException( e ) ); |
| return null; |
| } |
| } |
| |
| // no workspace resolver or workspace resolver returned null (i.e. model not in workspace) |
| if ( importModel == null ) |
| { |
| final ModelSource importSource; |
| try |
| { |
| importSource = modelResolver.resolveModel( dependency ); |
| } |
| catch ( UnresolvableModelException e ) |
| { |
| StringBuilder buffer = new StringBuilder( 256 ); |
| buffer.append( "Non-resolvable import POM" ); |
| if ( !containsCoordinates( e.getMessage(), groupId, artifactId, version ) ) |
| { |
| buffer.append( ' ' ).append( ModelProblemUtils.toId( groupId, artifactId, version ) ); |
| } |
| buffer.append( ": " ).append( e.getMessage() ); |
| |
| problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) |
| .setMessage( buffer.toString() ).setLocation( dependency.getLocation( "" ) ) |
| .setException( e ) ); |
| return null; |
| } |
| |
| final ModelBuildingResult importResult; |
| try |
| { |
| ModelBuildingRequest importRequest = new DefaultModelBuildingRequest(); |
| importRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL ); |
| importRequest.setModelCache( request.getModelCache() ); |
| importRequest.setSystemProperties( request.getSystemProperties() ); |
| importRequest.setUserProperties( request.getUserProperties() ); |
| importRequest.setLocationTracking( request.isLocationTracking() ); |
| |
| importRequest.setModelSource( importSource ); |
| importRequest.setModelResolver( modelResolver.newCopy() ); |
| |
| importResult = build( importRequest, importIds ); |
| } |
| catch ( ModelBuildingException e ) |
| { |
| problems.addAll( e.getProblems() ); |
| return null; |
| } |
| |
| problems.addAll( importResult.getProblems() ); |
| |
| importModel = importResult.getEffectiveModel(); |
| } |
| |
| importMgmt = importModel.getDependencyManagement(); |
| |
| if ( importMgmt == null ) |
| { |
| importMgmt = new DependencyManagement(); |
| } |
| return importMgmt; |
| } |
| |
| private <T> void intoCache( ModelCache modelCache, String groupId, String artifactId, String version, |
| ModelCacheTag<T> tag, T data ) |
| { |
| if ( modelCache != null ) |
| { |
| modelCache.put( groupId, artifactId, version, tag, data ); |
| } |
| } |
| |
| private <T> void intoCache( ModelCache modelCache, Source source, ModelCacheTag<T> tag, T data ) |
| { |
| if ( modelCache != null ) |
| { |
| modelCache.put( source, tag, data ); |
| } |
| } |
| |
| private static <T> T fromCache( ModelCache modelCache, String groupId, String artifactId, String version, |
| ModelCacheTag<T> tag ) |
| { |
| if ( modelCache != null ) |
| { |
| return modelCache.get( groupId, artifactId, version, tag ); |
| } |
| return null; |
| } |
| |
| private static <T> T fromCache( ModelCache modelCache, Source source, ModelCacheTag<T> tag ) |
| { |
| if ( modelCache != null ) |
| { |
| return modelCache.get( source, tag ); |
| } |
| return null; |
| } |
| |
| private void fireEvent( Model model, ModelBuildingRequest request, ModelProblemCollector problems, |
| ModelBuildingEventCatapult catapult ) |
| throws ModelBuildingException |
| { |
| ModelBuildingListener listener = request.getModelBuildingListener(); |
| |
| if ( listener != null ) |
| { |
| ModelBuildingEvent event = new DefaultModelBuildingEvent( model, request, problems ); |
| |
| catapult.fire( listener, event ); |
| } |
| } |
| |
| private boolean containsCoordinates( String message, String groupId, String artifactId, String version ) |
| { |
| return message != null && ( groupId == null || message.contains( groupId ) ) |
| && ( artifactId == null || message.contains( artifactId ) ) |
| && ( version == null || message.contains( version ) ); |
| } |
| |
| protected boolean hasModelErrors( ModelProblemCollectorExt problems ) |
| { |
| if ( problems instanceof DefaultModelProblemCollector ) |
| { |
| return ( (DefaultModelProblemCollector) problems ).hasErrors(); |
| } |
| else |
| { |
| // the default execution path only knows the DefaultModelProblemCollector, |
| // only reason it's not in signature is because it's package private |
| throw new IllegalStateException(); |
| } |
| } |
| |
| protected boolean hasFatalErrors( ModelProblemCollectorExt problems ) |
| { |
| if ( problems instanceof DefaultModelProblemCollector ) |
| { |
| return ( (DefaultModelProblemCollector) problems ).hasFatalErrors(); |
| } |
| else |
| { |
| // the default execution path only knows the DefaultModelProblemCollector, |
| // only reason it's not in signature is because it's package private |
| throw new IllegalStateException(); |
| } |
| } |
| |
| /** |
| * Builds up the transformer context. |
| * After the buildplan is ready, the build()-method returns the immutable context useful during distribution. |
| * This is an inner class, as it must be able to call readRawModel() |
| * |
| * @author Robert Scholte |
| * @since 4.0.0 |
| */ |
| private class DefaultTransformerContextBuilder implements TransformerContextBuilder |
| { |
| private final DefaultTransformerContext context = new DefaultTransformerContext(); |
| |
| private final Map<DefaultTransformerContext.GAKey, Set<Source>> mappedSources |
| = new ConcurrentHashMap<>( 64 ); |
| |
| /** |
| * If an interface could be extracted, DefaultModelProblemCollector should be ModelProblemCollectorExt. |
| * |
| * @param request the request |
| * @param collector the collector |
| * @return the transformer context |
| */ |
| @Override |
| public TransformerContext initialize( ModelBuildingRequest request, ModelProblemCollector collector ) |
| { |
| // We must assume the TransformerContext was created using this.newTransformerContextBuilder() |
| DefaultModelProblemCollector problems = (DefaultModelProblemCollector) collector; |
| return new TransformerContext() |
| { |
| @Override |
| public String getUserProperty( String key ) |
| { |
| return context.userProperties.computeIfAbsent( key, |
| k -> request.getUserProperties().getProperty( key ) ); |
| } |
| |
| @Override |
| public Model getRawModel( String gId, String aId ) |
| { |
| return context.modelByGA.computeIfAbsent( new DefaultTransformerContext.GAKey( gId, aId ), |
| k -> new DefaultTransformerContext.Holder() ) |
| .computeIfAbsent( () -> findRawModel( gId, aId ) ); |
| } |
| |
| @Override |
| public Model getRawModel( Path path ) |
| { |
| return context.modelByPath.computeIfAbsent( path, |
| k -> new DefaultTransformerContext.Holder() ) |
| .computeIfAbsent( () -> findRawModel( path ) ); |
| } |
| |
| private Model findRawModel( String groupId, String artifactId ) |
| { |
| Source source = getSource( groupId, artifactId ); |
| if ( source != null ) |
| { |
| try |
| { |
| ModelBuildingRequest gaBuildingRequest = new DefaultModelBuildingRequest( request ) |
| .setModelSource( (ModelSource) source ); |
| return readRawModel( gaBuildingRequest, problems ); |
| } |
| catch ( ModelBuildingException e ) |
| { |
| // gathered with problem collector |
| } |
| } |
| return null; |
| } |
| |
| private Model findRawModel( Path p ) |
| { |
| if ( !Files.isRegularFile( p ) ) |
| { |
| throw new IllegalArgumentException( "Not a regular file: " + p ); |
| } |
| |
| DefaultModelBuildingRequest req = new DefaultModelBuildingRequest( request ) |
| .setPomFile( p.toFile() ) |
| .setModelSource( new FileModelSource( p.toFile() ) ); |
| |
| try |
| { |
| return readRawModel( req, problems ); |
| } |
| catch ( ModelBuildingException e ) |
| { |
| // gathered with problem collector |
| } |
| return null; |
| } |
| }; |
| } |
| |
| @Override |
| public TransformerContext build() |
| { |
| return context; |
| } |
| |
| public Source getSource( String groupId, String artifactId ) |
| { |
| Set<Source> sources = mappedSources.get( new DefaultTransformerContext.GAKey( groupId, artifactId ) ); |
| if ( sources == null ) |
| { |
| return null; |
| } |
| return sources.stream().reduce( ( a, b ) -> |
| { |
| throw new IllegalStateException( String.format( "No unique Source for %s:%s: %s and %s", |
| groupId, artifactId, |
| a.getLocation(), b.getLocation() ) ); |
| } ).orElse( null ); |
| } |
| |
| public void putSource( String groupId, String artifactId, Source source ) |
| { |
| mappedSources.computeIfAbsent( new DefaultTransformerContext.GAKey( groupId, artifactId ), |
| k -> new HashSet<>() ).add( source ); |
| } |
| |
| } |
| } |