Polish profile expression support

Issue: SPR-12458
This commit is contained in:
Sam Brannen 2018-06-16 21:40:22 +03:00
parent 38f9a7b072
commit 4184ebe799
6 changed files with 44 additions and 37 deletions

View File

@ -49,7 +49,7 @@ import org.springframework.core.env.Profiles;
* <p>If a {@code @Configuration} class is marked with {@code @Profile}, all of the * <p>If a {@code @Configuration} class is marked with {@code @Profile}, all of the
* {@code @Bean} methods and {@link Import @Import} annotations associated with that class * {@code @Bean} methods and {@link Import @Import} annotations associated with that class
* will be bypassed unless one or more of the specified profiles are active. A profile * will be bypassed unless one or more of the specified profiles are active. A profile
* string may contains a simple profile name (for example {@code "p1"}) or a profile * string may contain a simple profile name (for example {@code "p1"}) or a profile
* expression. A profile expression allows for more complicated profile logic to be * expression. A profile expression allows for more complicated profile logic to be
* expressed, for example {@code "p1 & p2"}. See {@link Profiles#of(String...)} for more * expressed, for example {@code "p1 & p2"}. See {@link Profiles#of(String...)} for more
* details about supported formats. * details about supported formats.

View File

@ -98,11 +98,11 @@ public interface Environment extends PropertyResolver {
* Return whether one or more of the given profiles is active or, in the case of no * Return whether one or more of the given profiles is active or, in the case of no
* explicit active profiles, whether one or more of the given profiles is included in * explicit active profiles, whether one or more of the given profiles is included in
* the set of default profiles. If a profile begins with '!' the logic is inverted, * the set of default profiles. If a profile begins with '!' the logic is inverted,
* i.e. the method will return true if the given profile is <em>not</em> active. * i.e. the method will return {@code true} if the given profile is <em>not</em> active.
* For example, <pre class="code">env.acceptsProfiles("p1", "!p2")</pre> will * For example, {@code env.acceptsProfiles("p1", "!p2")} will return {@code true} if
* return {@code true} if profile 'p1' is active or 'p2' is not active. * profile 'p1' is active or 'p2' is not active.
* @throws IllegalArgumentException if called with zero arguments * @throws IllegalArgumentException if called with zero arguments
* or if any profile is {@code null}, empty or whitespace-only * or if any profile is {@code null}, empty, or whitespace only
* @see #getActiveProfiles * @see #getActiveProfiles
* @see #getDefaultProfiles * @see #getDefaultProfiles
* @see #acceptsProfiles(Profiles) * @see #acceptsProfiles(Profiles)
@ -112,7 +112,8 @@ public interface Environment extends PropertyResolver {
boolean acceptsProfiles(String... profiles); boolean acceptsProfiles(String... profiles);
/** /**
* Return whether the active profiles match the given {@link Profiles} predicate. * Return whether the {@linkplain #getActiveProfiles() active profiles}
* match the given {@link Profiles} predicate.
*/ */
boolean acceptsProfiles(Profiles profiles); boolean acceptsProfiles(Profiles profiles);

View File

@ -19,11 +19,11 @@ package org.springframework.core.env;
import java.util.function.Predicate; import java.util.function.Predicate;
/** /**
* Profile predicate that may be {@link Environment#acceptsProfiles(Profiles) accepted} by * Profile predicate that may be {@linkplain Environment#acceptsProfiles(Profiles)
* an {@link Environment}. * accepted} by an {@link Environment}.
* <p> *
* May be implemented directly or, more usually, created using the {@link #of(String...) * <p>May be implemented directly or, more usually, created using the
* of(...)} factory method. * {@link #of(String...) of(...)} factory method.
* *
* @author Phillip Webb * @author Phillip Webb
* @since 5.1 * @since 5.1
@ -32,31 +32,36 @@ import java.util.function.Predicate;
public interface Profiles { public interface Profiles {
/** /**
* Test if this profile predicate matches against given active profiles. * Test if this profile predicate matches against the given active profiles
* @param activeProfiles test whether a given profile is currently active * predicate.
* @param activeProfiles predicate that tests whether a given profile is
* currently active
*/ */
boolean matches(Predicate<String> activeProfiles); boolean matches(Predicate<String> activeProfiles);
/** /**
* Return a new {@link Profiles} instance that checks for matches against the given * Return a new {@link Profiles} instance that checks for matches against the given
* profile strings. The returned instance will * profile strings.
* {@link Profiles#matches(Predicate)} match} if any one of the given profile strings *
* match. * <p>The returned instance will {@linkplain Profiles#matches(Predicate) match}
* <p> * if any one of the given profile strings matches.
* A profile string may contains a simple profile name (for example *
* {@code "production"}) or a profile expression. A profile expression allows for more * <p>A profile string may contain a simple profile name (for example
* complicated profile logic to be expressed, for example * {@code "production"}) or a profile expression. A profile expression allows
* for more complicated profile logic to be expressed, for example
* {@code "production & cloud"}. * {@code "production & cloud"}.
* <p> *
* The following operators are supported in profile expressions: * <p>The following operators are supported in profile expressions:
* <ul> * <ul>
* <li>{@code !} - A logical <em>not</em> of the profile</li> * <li>{@code !} - A logical <em>not</em> of the profile</li>
* <li>{@code &} - A logical <em>and</em> of the profiles</li> * <li>{@code &} - A logical <em>and</em> of the profiles</li>
* <li>{@code |} - A logical <em>or</em> of the profiles</li></li> * <li>{@code |} - A logical <em>or</em> of the profiles</li>
* <p> * </ul>
* Please note that the {@code &} and {@code |} operators may not be mixed without *
* using parentheses. For example {@code "a & b | c"} is not a valid expression, it * <p>Please note that the {@code &} and {@code |} operators may not be mixed
* must be expressed as {@code "(a & b) | c"}. * without using parentheses. For example {@code "a & b | c"} is not a valid
* expression; it must be expressed as {@code "(a & b) | c"} or
* {@code "a & (b | c)"}.
* *
* @param profiles the profiles to include * @param profiles the profiles to include
* @return a new {@link Profiles} instance * @return a new {@link Profiles} instance

View File

@ -19,7 +19,6 @@ package org.springframework.core.env;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -30,6 +29,7 @@ import org.springframework.util.StringUtils;
* Internal parser used by {@link Profiles#of}. * Internal parser used by {@link Profiles#of}.
* *
* @author Phillip Webb * @author Phillip Webb
* @since 5.1
*/ */
class ProfilesParser { class ProfilesParser {
@ -43,7 +43,7 @@ class ProfilesParser {
} }
private static Profiles parseExpression(String expression) { private static Profiles parseExpression(String expression) {
Assert.hasText(expression, Assert.hasText(expression, () ->
"Invalid profile expression [" + expression + "]: must contain text"); "Invalid profile expression [" + expression + "]: must contain text");
StringTokenizer tokens = new StringTokenizer(expression, "()&|!", true); StringTokenizer tokens = new StringTokenizer(expression, "()&|!", true);
return parseTokens(expression, tokens); return parseTokens(expression, tokens);
@ -97,7 +97,7 @@ class ProfilesParser {
private static void assertWellFormed(String expression, boolean wellFormed) { private static void assertWellFormed(String expression, boolean wellFormed) {
Assert.isTrue(wellFormed, Assert.isTrue(wellFormed,
() -> "Malformed profile expression '" + expression + "'"); () -> "Malformed profile expression [" + expression + "]");
} }
private static Profiles or(Profiles... profiles) { private static Profiles or(Profiles... profiles) {
@ -122,7 +122,7 @@ class ProfilesParser {
return (profiles) -> profiles.matches(activeProfile); return (profiles) -> profiles.matches(activeProfile);
} }
enum Operator { private enum Operator {
AND, AND,
OR OR
} }

View File

@ -37,6 +37,7 @@ import static org.junit.Assert.*;
* *
* @author Phillip Webb * @author Phillip Webb
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 5.1
*/ */
public class ProfilesTests { public class ProfilesTests {

View File

@ -7664,19 +7664,19 @@ straight JNDI `InitialContext` usage shown above, but not the `JndiObjectFactory
variant which would force you to declare the return type as the `FactoryBean` type. variant which would force you to declare the return type as the `FactoryBean` type.
==== ====
The profile string may contains a simple profile name (for example `production`) or a The profile string may contain a simple profile name (for example `production`) or a
profile expression. A profile expression allows for more complicated profile logic to be profile expression. A profile expression allows for more complicated profile logic to be
expressed, for example `production & us-east`. The following operators are supported in expressed, for example `production & us-east`. The following operators are supported in
profile expressions: profile expressions:
* `!` - A logical not of the profile * `!` - A logical _not_ of the profile
* `&` - A logical and of the profiles * `&` - A logical _and_ of the profiles
* `|` - A logical or of the profiles * `|` - A logical _or_ of the profiles
[NOTE] [NOTE]
==== ====
The `&` and `|` operators may not be mixed without using parentheses. For example The `&` and `|` operators may not be mixed without using parentheses. For example
`production & us-east | eu-central` is not a valid expression, it must be expressed as `production & us-east | eu-central` is not a valid expression; it must be expressed as
`production & (us-east | eu-central)`. `production & (us-east | eu-central)`.
==== ====