From 4eab8d9819d919a6b5fd7ed8c1cd352df8f0ec9d Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 17 Mar 2022 16:40:15 +0100 Subject: [PATCH 1/2] Cache all interpolated models The idea is to avoid having to do the interpolation for all the ancestors. Once a model has been interpolated with the profiles and merged with its parent, it is cached and reused as the only ancestor when computing the effective models for this children. --- .../model/building/DefaultModelBuilder.java | 259 +++++++++++++----- 1 file changed, 190 insertions(+), 69 deletions(-) diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java index de84b90de03f..3b11f3161a3a 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java @@ -103,6 +103,36 @@ public class DefaultModelBuilder implements ModelBuilder { + /** + * The tag used for the file model without profile activation + * @since 4.0.0 + */ + private static final ModelCacheTag INTERPOLATED = new ModelCacheTag() + { + @Override + public String getName() + { + return "interpolated"; + } + + @Override + public Class getType() + { + return InterpolatedModel.class; + } + + @Override + public InterpolatedModel intoCache( InterpolatedModel data ) + { + return data; + } + + @Override + public InterpolatedModel fromCache( InterpolatedModel data ) + { + return data; + } + }; private final ModelMerger modelMerger = new FileToRawModelMerger(); private final ModelProcessor modelProcessor; @@ -517,13 +547,11 @@ private Model readEffectiveModel( final ModelBuildingRequest request, final Defa DefaultModelProblemCollector problems ) throws ModelBuildingException { - Model inputModel = - readRawModel( request, problems ); + 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 ); @@ -545,110 +573,166 @@ private Model readEffectiveModel( final ModelBuildingRequest request, final Defa List lineage = new ArrayList<>(); - for ( ModelData currentData = resultData; ; ) - { - String modelId = currentData.getId(); - result.addModelId( modelId ); + getModelData( request, result, problems, profileActivationContext, + activeExternalProfiles, parentIds, lineage, resultData ); - Model rawModel = currentData.getModel(); - result.setRawModel( modelId, rawModel ); + problems.setSource( result.getRawModel() ); - profileActivationContext.setProjectProperties( rawModel.getProperties() ); - problems.setSource( rawModel ); - List activePomProfiles = profileSelector.getActiveProfiles( rawModel.getProfiles(), - profileActivationContext, problems ); - result.setActivePomProfiles( modelId, activePomProfiles ); + Model resultModel = lineage.get( 0 ); - Model tmpModel = rawModel.clone(); + // consider caching inherited model - problems.setSource( tmpModel ); + problems.setSource( resultModel ); + problems.setRootModel( resultModel ); - // model normalization - modelNormalizer.mergeDuplicates( tmpModel, request, problems ); + // model interpolation + resultModel = interpolateModel( resultModel, request, problems ); - profileActivationContext.setProjectProperties( tmpModel.getProperties() ); + // url normalization + modelUrlNormalizer.normalize( resultModel, request ); - Map interpolatedActivations = getInterpolatedActivations( rawModel, - profileActivationContext, - problems ); - injectProfileActivations( tmpModel, interpolatedActivations ); + result.setEffectiveModel( resultModel ); - // profile injection - for ( Profile activeProfile : result.getActivePomProfiles( modelId ) ) - { - profileInjector.injectProfile( tmpModel, activeProfile, request, problems ); - } + // Now the fully interpolated model is available: reconfigure the resolver + configureResolver( request.getModelResolver(), resultModel, problems, true ); - if ( currentData == resultData ) + return resultModel; + } + + private void getModelData( ModelBuildingRequest request, + DefaultModelBuildingResult result, + DefaultModelProblemCollector problems, + DefaultProfileActivationContext profileActivationContext, + List activeExternalProfiles, + Collection parentIds, + List lineage, + ModelData currentData ) throws ModelBuildingException + { + ModelCache cache = request.getModelCache(); + if ( cache != null ) + { + InterpolatedModel interpolated = + cache.get( currentData.getSource(), INTERPOLATED ); + if ( interpolated != null ) { - for ( Profile activeProfile : activeExternalProfiles ) + Model model = interpolated.getModel(); + if ( activeExternalProfiles != null ) + { + result.setEffectiveModel( model ); + } + lineage.add( model ); + for ( InterpolatedModel m = interpolated; m != null; m = m.getParent() ) { - profileInjector.injectProfile( tmpModel, activeProfile, request, problems ); + result.addModelId( m.getModelId() ); + result.setRawModel( m.getModelId(), m.getModel() ); + result.setActivePomProfiles( m.getModelId(), m.getProfiles() ); } - result.setEffectiveModel( tmpModel ); + return; } + } + + String modelId = currentData.getId(); + result.addModelId( modelId ); + + Model rawModel = currentData.getModel(); + result.setRawModel( modelId, rawModel ); + + profileActivationContext.setProjectProperties( rawModel.getProperties() ); + problems.setSource( rawModel ); + List activePomProfiles = profileSelector.getActiveProfiles( rawModel.getProfiles(), + profileActivationContext, problems ); + result.setActivePomProfiles( modelId, activePomProfiles ); - lineage.add( tmpModel ); + Model tmpModel = rawModel.clone(); + + problems.setSource( tmpModel ); + + // model normalization + modelNormalizer.mergeDuplicates( tmpModel, request, problems ); + + profileActivationContext.setProjectProperties( tmpModel.getProperties() ); + + Map interpolatedActivations = getInterpolatedActivations( rawModel, + profileActivationContext, + problems ); + injectProfileActivations( tmpModel, interpolatedActivations ); + + // profile injection + for ( Profile activeProfile : activePomProfiles ) + { + profileInjector.injectProfile( tmpModel, activeProfile, request, problems ); + } - if ( currentData == superData ) + if ( activeExternalProfiles != null ) + { + for ( Profile activeProfile : activeExternalProfiles ) { - break; + profileInjector.injectProfile( tmpModel, activeProfile, request, problems ); } + result.setEffectiveModel( tmpModel ); + } + List tmpLineage = new ArrayList<>(); + + tmpLineage.add( tmpModel ); + + InterpolatedModel parentInterpolatedModel = null; + if ( currentData.getSource() != null ) + { configureResolver( request.getModelResolver(), tmpModel, problems ); ModelData parentData = - readParent( currentData.getModel(), currentData.getSource(), request, result, problems ); + readParent( currentData.getModel(), currentData.getSource(), request, result, problems ); if ( parentData == null ) { - currentData = superData; + parentData = new ModelData( null, getSuperModel() ); } - else if ( !parentIds.add( parentData.getId() ) ) + 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(); + throw newCycleException( problems, parentIds, parentData ); } else { - currentData = parentData; + getModelData( request, result, problems, profileActivationContext, + null, parentIds, tmpLineage, parentData ); + if ( cache != null ) + { + parentInterpolatedModel = cache.get( parentData.getSource(), INTERPOLATED ); + } } } - problems.setSource( result.getRawModel() ); - checkPluginVersions( lineage, request, problems ); + checkPluginVersions( tmpLineage, 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 ); + assembleInheritance( tmpLineage, request, problems ); - // url normalization - modelUrlNormalizer.normalize( resultModel, request ); + Model interpolated = tmpLineage.get( 0 ); + lineage.add( interpolated ); + if ( cache != null ) + { + cache.put( currentData.getSource(), INTERPOLATED, + new InterpolatedModel( modelId, interpolated, + activePomProfiles, parentInterpolatedModel ) ); + } + } - result.setEffectiveModel( resultModel ); + private ModelBuildingException newCycleException( DefaultModelProblemCollector problems, + Collection parentIds, + ModelData parentData ) + { + StringBuilder message = new StringBuilder( "The parents form a cycle: " ); + for ( String parentId : parentIds ) + { + message.append( parentId ).append( " -> " ); + } + message.append( parentData.getId() ); - // Now the fully interpolated model is available: reconfigure the resolver - configureResolver( request.getModelResolver(), resultModel, problems, true ); + problems.add( new ModelProblemCollectorRequest( Severity.FATAL, Version.BASE ) + .setMessage( message.toString() ) ); - return resultModel; + return problems.newModelBuildingException(); } private Map getInterpolatedActivations( Model rawModel, @@ -1775,6 +1859,43 @@ protected boolean hasFatalErrors( ModelProblemCollectorExt problems ) } } + private static class InterpolatedModel + { + private final String modelId; + private final Model model; + private final List profiles; + private final InterpolatedModel parent; + + InterpolatedModel( String modelId, Model model, List profiles, + InterpolatedModel parent ) + { + this.modelId = modelId; + this.model = model; + this.profiles = profiles; + this.parent = parent; + } + + public String getModelId() + { + return modelId; + } + + public Model getModel() + { + return model; + } + + public List getProfiles() + { + return profiles; + } + + public InterpolatedModel getParent() + { + return parent; + } + } + /** * Builds up the transformer context. * After the buildplan is ready, the build()-method returns the immutable context useful during distribution. From b92207a2db764ee6526d36a3dd85a7e1f85789f8 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 17 Mar 2022 20:12:38 +0100 Subject: [PATCH 2/2] Fix some ITs --- .../org/apache/maven/model/building/DefaultModelBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java index 3b11f3161a3a..0e83bf8cb8b8 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java @@ -586,7 +586,7 @@ private Model readEffectiveModel( final ModelBuildingRequest request, final Defa problems.setRootModel( resultModel ); // model interpolation - resultModel = interpolateModel( resultModel, request, problems ); + resultModel = interpolateModel( resultModel.clone(), request, problems ); // url normalization modelUrlNormalizer.normalize( resultModel, request );