From e2623b7d35fe1d9b8ab984349678072a533ff61a Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 12 Jun 2018 21:17:50 -0700 Subject: [PATCH] Add profile expression support Allow the `Environment` to accept a generic `Profiles` interface which can support more complex matching rules. The previous `acceptsProfiles(String...)` method now uses `Profiles.of` which supports basic profile expressions such as "(a | b) & !c" Issue: SPR-12458 --- .../core/env/AbstractEnvironment.java | 18 +- .../springframework/core/env/Environment.java | 20 +- .../springframework/core/env/Profiles.java | 76 ++++++ .../core/env/ProfilesParser.java | 155 ++++++++++++ .../core/env/DummyEnvironment.java | 5 +- .../core/env/ProfilesTests.java | 222 ++++++++++++++++++ .../core/env/StandardEnvironmentTests.java | 12 +- 7 files changed, 481 insertions(+), 27 deletions(-) create mode 100644 spring-core/src/main/java/org/springframework/core/env/Profiles.java create mode 100644 spring-core/src/main/java/org/springframework/core/env/ProfilesParser.java create mode 100644 spring-core/src/test/java/org/springframework/core/env/ProfilesTests.java diff --git a/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java b/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java index b50b8b8242..94d82e2db7 100644 --- a/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java +++ b/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -323,19 +323,9 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { } @Override - public boolean acceptsProfiles(String... profiles) { - Assert.notEmpty(profiles, "Must specify at least one profile"); - for (String profile : profiles) { - if (StringUtils.hasLength(profile) && profile.charAt(0) == '!') { - if (!isProfileActive(profile.substring(1))) { - return true; - } - } - else if (isProfileActive(profile)) { - return true; - } - } - return false; + public boolean acceptsProfiles(Profiles profiles) { + Assert.notNull(profiles, "Profiles must not be null"); + return profiles.matches(this::isProfileActive); } /** 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 52c8717e94..d335aa9db5 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,15 +97,19 @@ public interface Environment extends PropertyResolver { /** * 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 - * 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 not active. - * For example,
env.acceptsProfiles("p1", "!p2")
will - * return {@code true} if profile 'p1' is active or 'p2' is not active. - * @throws IllegalArgumentException if called with zero arguments - * or if any profile is {@code null}, empty or whitespace-only + * the set of default profiles. Profiles can simple indicators ('{@code p1}', + * {@code !p1}) or more complex boolean expressions. See {@link Profiles#of(String...)} + * for syntax details. * @see #getActiveProfiles * @see #getDefaultProfiles */ - boolean acceptsProfiles(String... profiles); + default boolean acceptsProfiles(String... profiles) { + return acceptsProfiles(Profiles.of(profiles)); + } + + /** + * Returns whether the active profiles match the given {@link Profiles} set. + */ + boolean acceptsProfiles(Profiles profiles); } diff --git a/spring-core/src/main/java/org/springframework/core/env/Profiles.java b/spring-core/src/main/java/org/springframework/core/env/Profiles.java new file mode 100644 index 0000000000..dd09d7b504 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/env/Profiles.java @@ -0,0 +1,76 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed 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.springframework.core.env; + +/** + * A set of profiles that may be {@link Environment#acceptsProfiles(Profiles) accepted} by + * an {@link Environment}. + *

+ * May be implemented directly or, more usually, created using the {@link #of(String...) + * of(...)} factory method. + * + * @author Phillip Webb + * @since 5.0 + * @see #of(String...) + */ +@FunctionalInterface +public interface Profiles { + + /** + * Test if this profile set matches against given active profiles. + */ + boolean matches(ActiveProfiles activeProfiles); + + /** + * Return a new {@link Profiles} instance that checks for matches against the given + * profile strings. The returned instance will + * {@link Profiles#matches(ActiveProfiles) matches} if any one of the given profile + * strings match. + *

+ * A profile string may contains a simple profile name (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"}. + *

+ * The following operators are supported in profile expressions: + *