diff --git a/maven-model-builder/pom.xml b/maven-model-builder/pom.xml index f315f64b75..4ff0becbc5 100644 --- a/maven-model-builder/pom.xml +++ b/maven-model-builder/pom.xml @@ -167,6 +167,9 @@ under the License. org.apache.maven.model.profile.activation.FileProfileActivator#setProfileActivationFilePathInterpolator(org.apache.maven.model.path.ProfileActivationFilePathInterpolator):METHOD_REMOVED org.apache.maven.model.profile.activation.FileProfileActivator#FileProfileActivator():CONSTRUCTOR_REMOVED org.apache.maven.model.profile.DefaultProfileInjector + org.apache.maven.model.profile.ProfileInjector#injectProfile(org.apache.maven.api.model.Model,org.apache.maven.api.model.Profile,org.apache.maven.model.building.ModelBuildingRequest,org.apache.maven.model.building.ModelProblemCollector):METHOD_NEW_DEFAULT + org.apache.maven.model.profile.ProfileInjector#injectProfiles(org.apache.maven.api.model.Model,java.util.List,org.apache.maven.model.building.ModelBuildingRequest,org.apache.maven.model.building.ModelProblemCollector):METHOD_NEW_DEFAULT + org.apache.maven.model.profile.ProfileSelector#getActiveProfilesV4(java.util.Collection,org.apache.maven.model.profile.ProfileActivationContext,org.apache.maven.model.building.ModelProblemCollector):METHOD_NEW_DEFAULT org.apache.maven.model.resolution.InvalidRepositoryException#getRepository():METHOD_RETURN_TYPE_CHANGED org.apache.maven.model.resolution.InvalidRepositoryException#InvalidRepositoryException(java.lang.String,org.apache.maven.model.Repository,java.lang.Throwable):CONSTRUCTOR_REMOVED org.apache.maven.model.resolution.InvalidRepositoryException#InvalidRepositoryException(java.lang.String,org.apache.maven.model.Repository):CONSTRUCTOR_REMOVED diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java index f67ca6275c..a435002837 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/DefaultProfileInjector.java @@ -26,6 +26,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; import org.apache.maven.api.model.Build; import org.apache.maven.api.model.BuildBase; @@ -34,6 +36,7 @@ import org.apache.maven.api.model.Plugin; import org.apache.maven.api.model.PluginContainer; import org.apache.maven.api.model.PluginExecution; +import org.apache.maven.api.model.Profile; import org.apache.maven.api.model.ReportPlugin; import org.apache.maven.api.model.ReportSet; import org.apache.maven.api.model.Reporting; @@ -50,6 +53,13 @@ @SuppressWarnings({"checkstyle:methodname"}) public class DefaultProfileInjector implements ProfileInjector { + private static final Map, Model>> CACHE = Collections.synchronizedMap(new WeakHashMap<>()); + + // In order for the weak hash map to work correctly, we must not hold any reference to + // the model used as the key. So we use a dummy model as a placeholder to indicate that + // we want to store the model used as they key. + private static final Model KEY = Model.newInstance(); + private ProfileModelMerger merger = new ProfileModelMerger(); @Override @@ -58,19 +68,42 @@ public void injectProfile( org.apache.maven.model.Profile profile, ModelBuildingRequest request, ModelProblemCollector problems) { - if (profile != null) { - Model.Builder builder = Model.newBuilder(model.getDelegate()); - merger.mergeModelBase(builder, model.getDelegate(), profile.getDelegate()); + model.update( + injectProfile(model.getDelegate(), profile != null ? profile.getDelegate() : null, request, problems)); + } - if (profile.getBuild() != null) { - Build build = model.getBuild() != null ? model.getBuild().getDelegate() : Build.newInstance(); - Build.Builder bbuilder = Build.newBuilder(build); - merger.mergeBuildBase(bbuilder, build, profile.getBuild().getDelegate()); - builder.build(bbuilder.build()); + @Override + public Model injectProfile( + Model model, Profile profile, ModelBuildingRequest request, ModelProblemCollector problems) { + return injectProfiles(model, Collections.singletonList(profile), request, problems); + } + + @Override + public Model injectProfiles( + Model model, List profiles, ModelBuildingRequest request, ModelProblemCollector problems) { + Model result = CACHE.computeIfAbsent(model, k -> new ConcurrentHashMap<>()) + .computeIfAbsent(profiles, l -> doInjectProfiles(model, profiles)); + return result == KEY ? model : result; + } + + private Model doInjectProfiles(Model model, List profiles) { + Model orgModel = model; + for (Profile profile : profiles) { + if (profile != null) { + Model.Builder builder = Model.newBuilder(model); + merger.mergeModelBase(builder, model, profile); + + if (profile.getBuild() != null) { + Build build = model.getBuild() != null ? model.getBuild() : Build.newInstance(); + Build.Builder bbuilder = Build.newBuilder(build); + merger.mergeBuildBase(bbuilder, build, profile.getBuild()); + builder.build(bbuilder.build()); + } + + model = builder.build(); } - - model.update(builder.build()); } + return model == orgModel ? KEY : model; } /** 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 45959f2ed1..b2d0e9a801 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 @@ -26,6 +26,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.stream.Collectors; import org.apache.maven.model.Activation; import org.apache.maven.model.Profile; @@ -61,6 +62,17 @@ public DefaultProfileSelector addProfileActivator(ProfileActivator profileActiva return this; } + @Override + public List getActiveProfilesV4( + Collection profiles, + ProfileActivationContext context, + ModelProblemCollector problems) { + return getActiveProfiles(profiles.stream().map(Profile::new).collect(Collectors.toList()), context, problems) + .stream() + .map(Profile::getDelegate) + .collect(Collectors.toList()); + } + @Override public List getActiveProfiles( Collection profiles, ProfileActivationContext context, ModelProblemCollector problems) { diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java index ed62c86221..e4ab40015b 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileInjector.java @@ -18,6 +18,8 @@ */ package org.apache.maven.model.profile; +import java.util.List; + import org.apache.maven.model.Model; import org.apache.maven.model.Profile; import org.apache.maven.model.building.ModelBuildingRequest; @@ -39,4 +41,45 @@ public interface ProfileInjector { * @param problems The container used to collect problems that were encountered, must not be {@code null}. */ void injectProfile(Model model, Profile profile, ModelBuildingRequest request, ModelProblemCollector problems); + + /** + * Merges values from the specified profile into the given model. Implementations are expected to keep the profile + * and model completely decoupled by injecting deep copies rather than the original objects from the profile. + * + * @param model The model into which to merge the values defined by the profile, must not be null. + * @param profile The (read-only) profile whose values should be injected, may be null. + * @param request The model building request that holds further settings, must not be {@code null}. + * @param problems The container used to collect problems that were encountered, must not be {@code null}. + */ + default org.apache.maven.api.model.Model injectProfile( + org.apache.maven.api.model.Model model, + org.apache.maven.api.model.Profile profile, + ModelBuildingRequest request, + ModelProblemCollector problems) { + Model m = new Model(model); + injectProfile(m, profile != null ? new Profile(profile) : null, request, problems); + return m.getDelegate(); + } + + /** + * Merges values from the specified profile into the given model. Implementations are expected to keep the profile + * and model completely decoupled by injecting deep copies rather than the original objects from the profile. + * + * @param model The model into which to merge the values defined by the profile, must not be null. + * @param profiles The (read-only) list of profiles whose values should be injected, must not be null. + * @param request The model building request that holds further settings, must not be {@code null}. + * @param problems The container used to collect problems that were encountered, must not be {@code null}. + */ + default org.apache.maven.api.model.Model injectProfiles( + org.apache.maven.api.model.Model model, + List profiles, + ModelBuildingRequest request, + ModelProblemCollector problems) { + for (org.apache.maven.api.model.Profile profile : profiles) { + if (profile != null) { + model = injectProfile(model, profile, request, problems); + } + } + return model; + } } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java index f0412961c6..a87d695e5c 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/profile/ProfileSelector.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; import org.apache.maven.model.Profile; import org.apache.maven.model.building.ModelProblemCollector; @@ -42,4 +43,24 @@ public interface ProfileSelector { */ List getActiveProfiles( Collection profiles, ProfileActivationContext context, ModelProblemCollector problems); + + /** + * Determines the profiles which are active in the specified activation context. Active profiles will eventually be + * injected into the model. + * + * @param profiles The profiles whose activation status should be determined, must not be {@code null}. + * @param context The environmental context used to determine the activation status of a profile, must not be + * {@code null}. + * @param problems The container used to collect problems that were encountered, must not be {@code null}. + * @return The profiles that have been activated, never {@code null}. + */ + default List getActiveProfilesV4( + Collection profiles, + ProfileActivationContext context, + ModelProblemCollector problems) { + return getActiveProfiles(profiles.stream().map(Profile::new).collect(Collectors.toList()), context, problems) + .stream() + .map(Profile::getDelegate) + .collect(Collectors.toList()); + } }