From 6f3dfe9462cea000aef0616413476c6117a6fa7c Mon Sep 17 00:00:00 2001 From: Matt Benson Date: Mon, 18 Mar 2024 17:21:38 -0500 Subject: [PATCH] [MNG-5235] interpolate available properties during default profile selection --- .../building/DefaultModelBuilderFactory.java | 2 +- .../model/profile/DefaultProfileSelector.java | 100 +++++++++++++++++- .../profile/DefaultProfileSelectorTest.java | 89 ++++++++++++++++ 3 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 maven-model-builder/src/test/java/org/apache/maven/model/profile/DefaultProfileSelectorTest.java diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java index 363b9b67a3af..4aa6fa80814d 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java @@ -237,7 +237,7 @@ protected ModelReader newModelReader() { } protected ProfileSelector newProfileSelector() { - return new DefaultProfileSelector(Arrays.asList(newProfileActivators())); + return new DefaultProfileSelector(Arrays.asList(newProfileActivators()), newModelInterpolator()); } protected ProfileActivator[] newProfileActivators() { diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java index b2d0e9a8015c..e2e81bde12b5 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileSelector.java @@ -22,18 +22,28 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; import java.util.stream.Collectors; import org.apache.maven.model.Activation; +import org.apache.maven.model.Model; import org.apache.maven.model.Profile; +import org.apache.maven.model.building.DefaultModelBuildingRequest; +import org.apache.maven.model.building.ModelBuildingRequest; import org.apache.maven.model.building.ModelProblem.Severity; import org.apache.maven.model.building.ModelProblem.Version; import org.apache.maven.model.building.ModelProblemCollector; import org.apache.maven.model.building.ModelProblemCollectorRequest; +import org.apache.maven.model.interpolation.ModelInterpolator; import org.apache.maven.model.profile.activation.ProfileActivator; /** @@ -44,15 +54,53 @@ @Singleton public class DefaultProfileSelector implements ProfileSelector { + private static Properties asProperties(Map m) { + return m.entrySet().stream() + .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue(), (l, r) -> r, Properties::new)); + } + private final List activators; + private ModelInterpolator interpolator; public DefaultProfileSelector() { - this.activators = new ArrayList<>(); + this(new ArrayList<>(), new ModelInterpolator() { + + @Override + public Model interpolateModel( + Model model, File projectDir, ModelBuildingRequest request, ModelProblemCollector problems) { + return model; + } + + @Override + public org.apache.maven.api.model.Model interpolateModel( + org.apache.maven.api.model.Model model, + File projectDir, + ModelBuildingRequest request, + ModelProblemCollector problems) { + return model; + } + + @Override + public Model interpolateModel( + Model model, Path projectDir, ModelBuildingRequest request, ModelProblemCollector problems) { + return model; + } + + @Override + public org.apache.maven.api.model.Model interpolateModel( + org.apache.maven.api.model.Model model, + Path projectDir, + ModelBuildingRequest request, + ModelProblemCollector problems) { + return model; + } + }); } @Inject - public DefaultProfileSelector(List activators) { + public DefaultProfileSelector(List activators, ModelInterpolator interpolator) { this.activators = new ArrayList<>(activators); + this.interpolator = interpolator; } public DefaultProfileSelector addProfileActivator(ProfileActivator profileActivator) { @@ -62,6 +110,10 @@ public DefaultProfileSelector addProfileActivator(ProfileActivator profileActiva return this; } + public void setInterpolator(ModelInterpolator interpolator) { + this.interpolator = interpolator; + } + @Override public List getActiveProfilesV4( Collection profiles, @@ -76,6 +128,11 @@ public List getActiveProfilesV4( @Override public List getActiveProfiles( Collection profiles, ProfileActivationContext context, ModelProblemCollector problems) { + + if (profiles.stream().map(Profile::getId).distinct().count() < profiles.size()) { + // invalid profile specification + return Collections.emptyList(); + } Collection activatedIds = new HashSet<>(context.getActiveProfileIds()); Collection deactivatedIds = new HashSet<>(context.getInactiveProfileIds()); @@ -83,9 +140,12 @@ public List getActiveProfiles( List activePomProfilesByDefault = new ArrayList<>(); boolean activatedPomProfileNotByDefault = false; + Map activation = earlyInterpolateProfileActivations(profiles, context); + for (Profile profile : profiles) { if (!deactivatedIds.contains(profile.getId())) { - if (activatedIds.contains(profile.getId()) || isActive(profile, context, problems)) { + if (activatedIds.contains(profile.getId()) + || isActive(activation.get(profile.getId()), context, problems)) { activeProfiles.add(profile); if (Profile.SOURCE_POM.equals(profile.getSource())) { @@ -108,6 +168,40 @@ public List getActiveProfiles( return activeProfiles; } + private Map earlyInterpolateProfileActivations( + Collection original, ProfileActivationContext context) { + + org.apache.maven.api.model.Model model = org.apache.maven.api.model.Model.newBuilder() + .profiles(original.stream() + .map(p -> org.apache.maven.api.model.Profile.newBuilder() + .id(p.getId()) + .activation(Optional.ofNullable(p.getActivation()) + .map(Activation::getDelegate) + .orElse(null)) + .build()) + .collect(Collectors.toList())) + .build(); + + ModelBuildingRequest mbr = new DefaultModelBuildingRequest() + .setActiveProfileIds(context.getActiveProfileIds()) + .setInactiveProfileIds(context.getInactiveProfileIds()) + .setSystemProperties(asProperties(context.getSystemProperties())) + .setUserProperties(asProperties(context.getUserProperties())) + .setTwoPhaseBuilding(true) + .setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL); + + model = interpolator.interpolateModel( + model, + Optional.ofNullable(context.getProjectDirectory()) + .map(File::toPath) + .orElse(null), + mbr, + problem -> {}); + + return model.getProfiles().stream() + .collect(Collectors.toMap(org.apache.maven.api.model.Profile::getId, Profile::new)); + } + private boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) { boolean isActive = false; for (ProfileActivator activator : activators) { diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/profile/DefaultProfileSelectorTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/profile/DefaultProfileSelectorTest.java new file mode 100644 index 000000000000..873be3f645f7 --- /dev/null +++ b/maven-model-builder/src/test/java/org/apache/maven/model/profile/DefaultProfileSelectorTest.java @@ -0,0 +1,89 @@ +/* + * 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. + */ +package org.apache.maven.model.profile; + +import java.nio.file.Path; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.apache.maven.api.model.Activation; +import org.apache.maven.api.model.ActivationProperty; +import org.apache.maven.api.model.Model; +import org.apache.maven.api.model.Profile; +import org.apache.maven.model.interpolation.ModelInterpolator; +import org.apache.maven.model.profile.activation.PropertyProfileActivator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DefaultProfileSelectorTest { + private DefaultProfileSelector selector; + private ModelInterpolator interpolator; + + @BeforeEach + public void setup() { + interpolator = Mockito.mock(ModelInterpolator.class); + selector = new DefaultProfileSelector(Collections.singletonList(new PropertyProfileActivator()), interpolator); + } + + @Test + public void testProfileActivationInterpolation() { + Map userProperties = Collections.singletonMap("foo", "bar"); + + Mockito.when(interpolator.interpolateModel( + Mockito.any(org.apache.maven.api.model.Model.class), + Mockito.any(), + Mockito.any(), + Mockito.any())) + .thenAnswer(invocation -> { + Model m = invocation.getArgument(0); + + return Optional.ofNullable(m.getProfiles()) + .map(pz -> pz.stream() + .map(p -> Optional.ofNullable(p.getActivation()) + .flatMap(a -> Optional.ofNullable(a.getProperty()) + .flatMap(ap -> Optional.ofNullable(ap.getName()) + .map(userProperties::get) + .map(v -> ap.withValue(v))) + .map(a::withProperty)) + .map(p::withActivation) + .orElse(p)) + .collect(Collectors.toList())) + .map(m::withProfiles) + .orElse(m); + }); + + org.apache.maven.model.Profile profile = new org.apache.maven.model.Profile(Profile.newBuilder() + .id("foo") + .activation(Activation.newBuilder() + .property(ActivationProperty.newBuilder().name("foo").build()) + .build()) + .build()); + + assertThat(selector.getActiveProfiles( + Collections.singleton(profile), + new DefaultProfileActivationContext().setUserProperties(userProperties), + p -> {})) + .containsExactly(profile); + } +}