forked from helidon-io/helidon
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request helidon-io#509 from batsatt/se-map-env-vars_446
Make SE and MP share config env var mapping
- Loading branch information
Showing
10 changed files
with
660 additions
and
133 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
config/config/src/main/java/io/helidon/config/EnvironmentVariables.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/* | ||
* Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. | ||
* | ||
* 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 io.helidon.config; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* Provides environment variables that include mapped variants enabling setting or overriding configuration with keys that are | ||
* unlikely to be legal as environment variables. | ||
* <p> | ||
* The <a href="https://github.com/eclipse/microprofile-config/blob/master/spec/src/main/asciidoc/configsources.asciidoc"> | ||
* MP config specification</a> describes the environment variables {@code ConfigSource} as follows: | ||
* <pre> | ||
* Some operating systems allow only alphabetic characters or an underscore, _, in environment variables. Other | ||
* characters such as ., /, etc may be disallowed. In order to set a value for a config property that has a name | ||
* containing such disallowed characters from an environment variable, the following rules are used. | ||
* | ||
* This ConfigSource searches 3 environment variables for a given property name (e.g. com.ACME.size): | ||
* | ||
* 1. Exact match (i.e. com.ACME.size) | ||
* 2. Replace the character that is neither alphanumeric nor _ with _ (i.e. com_ACME_size) | ||
* 3. Replace the character that is neither alphanumeric nor _ with _ and convert to upper case (i.e. COM_ACME_SIZE) | ||
* | ||
* The first environment variable that is found is returned by this ConfigSource. | ||
* </pre> | ||
* <p> | ||
* The spec assumes the mapping takes place during search, where the desired key is known, but Helidon merges | ||
* {@code ConfigSource}s instead; therefore this implementation produces <em>additional</em> KV pairs with variant keys | ||
* for any variable that can meaningfully be mapped. See {@link #shouldMap(String)} for the mapping criteria. | ||
* <p> | ||
* Since Helidon supports many configuration keys that contain {@code '-'} (e.g. {@code "server.executor-service.max-pool-size"}), | ||
* an additional mapping is required to produce a matching variant. Given that it must map from legal environment variable names | ||
* and reduce the chances of inadvertent mappings, a verbose mapping is used: {@code "_dash_"} substrings (lowercase only) are | ||
* first replaced by {@code '-'}. See {@link #expand()} for the variants produced. | ||
* <p> | ||
*/ | ||
public class EnvironmentVariables { | ||
private static final Pattern DASH_PATTERN = Pattern.compile("_dash_", Pattern.LITERAL); | ||
private static final String UNDERSCORE = "_"; | ||
private static final String DOUBLE_UNDERSCORE = "__"; | ||
private static final String DASH = "-"; | ||
private static final char UNDERSCORE_CHAR = '_'; | ||
private static final char DOT_CHAR = '.'; | ||
|
||
/** | ||
* Tests whether mappings should be created for the given environment variable name. | ||
* <p> | ||
* To provide a meaningful mapping, the name must meet <em>all</em> of the following criteria: | ||
* <ol> | ||
* <li>does not begin or end with a {@code '_'} character</li> | ||
* <li>does not contain {@code "__"}</li> | ||
* <li>contains one or more {@code '_'} characters</li> | ||
* </ol> | ||
* | ||
* @param name The environment variable name. | ||
* @return {@code true} if mappings should be created. | ||
*/ | ||
public static boolean shouldMap(final String name) { | ||
final int length = name.length(); | ||
return length > 2 | ||
&& name.charAt(0) != UNDERSCORE_CHAR | ||
&& name.charAt(length - 1) != UNDERSCORE_CHAR | ||
&& name.contains(UNDERSCORE) | ||
&& !name.contains(DOUBLE_UNDERSCORE); | ||
} | ||
|
||
/** | ||
* Returns the environment variables and their mapped variants. | ||
* <p> | ||
* The following mappings are applied to any environment variable name for which {@link #shouldMap(String)} returns | ||
* {@code true}: | ||
* <ol> | ||
* <li>Replace {@code "_dash_"} by {@code '-'}, e.g. {@code "SERVER_EXECUTOR_dash_SERVICE_MAX_dash_POOL_dash_SIZE"} becomes | ||
* {@code "SERVER_EXECUTOR-SERVICE_MAX-POOL-SIZE"}.</li> | ||
* <li>Replace {@code '_'} by {@code '.'} and add as a variant, e.g. {@code "com_ACME_size"} becomes {@code "com.ACME.size"} | ||
* and {@code "SERVER_EXECUTOR-SERVICE_MAX-POOL-SIZE"} becomes {@code "SERVER.EXECUTOR-SERVICE.MAX-POOL-SIZE"}. This mapping | ||
* is added primarily to support mixed case config keys such as {@code "app.someCamelCaseKey"}.</li> | ||
* <li>Convert the result of step 2 to lowercase and add as a variant, e.g. {@code "com.ACME.size"} becomes | ||
* {@code "com.acme.size"} and {@code "SERVER.EXECUTOR-SERVICE.MAX-POOL-SIZE"} becomes | ||
* {@code "server.executor-service.max-pool-size"}. | ||
* </li> | ||
* </ol> | ||
* | ||
* @return The map. | ||
*/ | ||
public static Map<String, String> expand() { | ||
return expand(System.getenv()); | ||
} | ||
|
||
/** | ||
* Returns the environment variables and their mapped variants. | ||
* <p> | ||
* The following mappings are applied to any environment variable name for which {@link #shouldMap(String)} returns | ||
* {@code true}: | ||
* <ol> | ||
* <li>Replace {@code "_dash_"} by {@code '-'}, e.g. {@code "SERVER_EXECUTOR_dash_SERVICE_MAX_dash_POOL_dash_SIZE"} becomes | ||
* {@code "SERVER_EXECUTOR-SERVICE_MAX-POOL-SIZE"}.</li> | ||
* <li>Replace {@code '_'} by {@code '.'} and add as a variant, e.g. {@code "com_ACME_size"} becomes {@code "com.ACME.size"} | ||
* and {@code "SERVER_EXECUTOR-SERVICE_MAX-POOL-SIZE"} becomes {@code "SERVER.EXECUTOR-SERVICE.MAX-POOL-SIZE"}. This mapping | ||
* is added primarily to support mixed case config keys such as {@code "app.someCamelCaseKey"}.</li> | ||
* <li>Convert the result of step 2 to lowercase and add as a variant, e.g. {@code "com.ACME.size"} becomes | ||
* {@code "com.acme.size"} and {@code "SERVER.EXECUTOR-SERVICE.MAX-POOL-SIZE"} becomes | ||
* {@code "server.executor-service.max-pool-size"}. | ||
* </li> | ||
* </ol> | ||
* | ||
* @param env The environment variables. | ||
* @return A copy of {@code env} with variants added. | ||
*/ | ||
public static Map<String, String> expand(final Map<String, String> env) { | ||
final Map<String, String> result = new HashMap<>(env.size()); | ||
env.forEach((name, value) -> { | ||
result.put(name, value); | ||
if (shouldMap(name)) { | ||
String alternateName = DASH_PATTERN.matcher(name).replaceAll(DASH); | ||
alternateName = alternateName.replace(UNDERSCORE_CHAR, DOT_CHAR); | ||
result.put(alternateName, value); | ||
result.put(alternateName.toLowerCase(), value); | ||
} | ||
}); | ||
|
||
return result; | ||
} | ||
|
||
private EnvironmentVariables() { | ||
} | ||
} |
Oops, something went wrong.