From 08fe12393063d1b9ed92ade42a74b2296ceb523e Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 25 Apr 2023 10:24:15 +0200 Subject: [PATCH] Introduce Environment.matchesProfiles() for profile expressions Environment.acceptsProfiles(String...) was deprecated in 5.1 in conjunction with gh-17063 which introduced a new acceptsProfiles(Profiles) method to replace it. The deprecated method only supports OR semantics; whereas, the new method supports profile expressions. Thus, the goal was to encourage people to use the more powerful profile expressions instead of the limited OR support with profile names. However, there are use cases where it is difficult (if not impossible) to provide a Profiles instance, and there are use cases where it is simply preferable to provide profile expressions directly as strings. To address these issues, this commit introduces a new matchesProfiles() method in Environment that accepts a var-args list of profile expressions. See gh-30206 Closes gh-30226 --- .../springframework/core/env/Environment.java | 29 ++++- .../core/env/StandardEnvironmentTests.java | 112 ++++++++++++++++++ 2 files changed, 138 insertions(+), 3 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/env/Environment.java b/spring-core/src/main/java/org/springframework/core/env/Environment.java index f1ffb0a2f8e..157920a61a0 100644 --- a/spring-core/src/main/java/org/springframework/core/env/Environment.java +++ b/spring-core/src/main/java/org/springframework/core/env/Environment.java @@ -58,6 +58,7 @@ package org.springframework.core.env; * * @author Chris Beams * @author Phillip Webb + * @author Sam Brannen * @since 3.1 * @see PropertyResolver * @see EnvironmentCapable @@ -109,20 +110,42 @@ public interface Environment extends PropertyResolver { * whitespace only * @see #getActiveProfiles * @see #getDefaultProfiles + * @see #matchesProfiles(String...) * @see #acceptsProfiles(Profiles) - * @deprecated as of 5.1 in favor of {@link #acceptsProfiles(Profiles)} + * @deprecated as of 5.1 in favor of {@link #acceptsProfiles(Profiles)} or + * {@link #matchesProfiles(String...)} */ @Deprecated boolean acceptsProfiles(String... profiles); + /** + * Determine whether one of the given profile expressions matches the + * {@linkplain #getActiveProfiles() active profiles} — or in the case + * of no explicit active profiles, whether one of the given profile expressions + * matches the {@linkplain #getDefaultProfiles() default profiles}. + *

Profile expressions allow for complex, boolean profile logic to be + * expressed — for example {@code "p1 & p2"}, {@code "(p1 & p2) | p3"}, + * etc. See {@link Profiles#of(String...)} for details on the supported + * expression syntax. + *

This method is a convenient shortcut for + * {@code env.acceptsProfiles(Profiles.of(profileExpressions))}. + * @since 5.3.28 + * @see Profiles#of(String...) + * @see #acceptsProfiles(Profiles) + */ + default boolean matchesProfiles(String... profileExpressions) { + return acceptsProfiles(Profiles.of(profileExpressions)); + } + /** * Determine whether the given {@link Profiles} predicate matches the * {@linkplain #getActiveProfiles() active profiles} — or in the case * of no explicit active profiles, whether the given {@code Profiles} predicate * matches the {@linkplain #getDefaultProfiles() default profiles}. - *

If you wish to check a single profile expression, consider using - * {@link #acceptsProfiles(String)} instead. + *

If you wish provide profile expressions directly as strings, use + * {@link #matchesProfiles(String...)} instead. * @since 5.1 + * @see #matchesProfiles(String...) * @see Profiles#of(String...) */ boolean acceptsProfiles(Profiles profiles); diff --git a/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java b/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java index 71881d3e9d5..80f12bea560 100644 --- a/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java +++ b/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java @@ -541,4 +541,116 @@ class StandardEnvironmentTests { } + @Nested + class MatchesProfilesTests { + + @Test + @SuppressWarnings("deprecation") + void withEmptyArgumentList() { + assertThatIllegalArgumentException().isThrownBy(environment::matchesProfiles); + } + + @Test + @SuppressWarnings("deprecation") + void withNullArgumentList() { + assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles((String[]) null)); + } + + @Test + @SuppressWarnings("deprecation") + void withNullArgument() { + assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles((String) null)); + assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1", null)); + } + + @Test + @SuppressWarnings("deprecation") + void withEmptyArgument() { + assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("")); + assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1", "")); + assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1", " ")); + } + + @Test + @SuppressWarnings("deprecation") + void withInvalidNotOperator() { + assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1", "!")); + } + + @Test + @SuppressWarnings("deprecation") + void withInvalidCompoundExpressionGrouping() { + assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1 | p2 & p3")); + assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1 & p2 | p3")); + assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1 & (p2 | p3) | p4")); + } + + @Test + @SuppressWarnings("deprecation") + void activeProfileSetProgrammatically() { + assertThat(environment.matchesProfiles("p1", "p2")).isFalse(); + + environment.setActiveProfiles("p1"); + assertThat(environment.matchesProfiles("p1", "p2")).isTrue(); + + environment.setActiveProfiles("p2"); + assertThat(environment.matchesProfiles("p1", "p2")).isTrue(); + + environment.setActiveProfiles("p1", "p2"); + assertThat(environment.matchesProfiles("p1", "p2")).isTrue(); + } + + @Test + @SuppressWarnings("deprecation") + void activeProfileSetViaProperty() { + assertThat(environment.matchesProfiles("p1")).isFalse(); + + environment.getPropertySources().addFirst(new MockPropertySource().withProperty(ACTIVE_PROFILES_PROPERTY_NAME, "p1")); + assertThat(environment.matchesProfiles("p1")).isTrue(); + } + + @Test + @SuppressWarnings("deprecation") + void defaultProfile() { + assertThat(environment.matchesProfiles("pd")).isFalse(); + + environment.setDefaultProfiles("pd"); + assertThat(environment.matchesProfiles("pd")).isTrue(); + + environment.setActiveProfiles("p1"); + assertThat(environment.matchesProfiles("pd")).isFalse(); + assertThat(environment.matchesProfiles("p1")).isTrue(); + } + + @Test + @SuppressWarnings("deprecation") + void withNotOperator() { + assertThat(environment.matchesProfiles("p1")).isFalse(); + assertThat(environment.matchesProfiles("!p1")).isTrue(); + + environment.addActiveProfile("p1"); + assertThat(environment.matchesProfiles("p1")).isTrue(); + assertThat(environment.matchesProfiles("!p1")).isFalse(); + } + + @Test + void withProfileExpressions() { + assertThat(environment.matchesProfiles("p1 & p2")).isFalse(); + + environment.addActiveProfile("p1"); + assertThat(environment.matchesProfiles("p1 | p2")).isTrue(); + assertThat(environment.matchesProfiles("p1 & p2")).isFalse(); + + environment.addActiveProfile("p2"); + assertThat(environment.matchesProfiles("p1 & p2")).isTrue(); + assertThat(environment.matchesProfiles("p1 | p2")).isTrue(); + assertThat(environment.matchesProfiles("foo | p1", "p2")).isTrue(); + assertThat(environment.matchesProfiles("foo | p2", "p1")).isTrue(); + assertThat(environment.matchesProfiles("foo | (p2 & p1)")).isTrue(); + assertThat(environment.matchesProfiles("p2 & (foo | p1)")).isTrue(); + assertThat(environment.matchesProfiles("foo", "(p2 & p1)")).isTrue(); + } + + } + }