From 667a55eecfa39044b64bdab13bfc12e8269b2f83 Mon Sep 17 00:00:00 2001 From: Jakub Pomykala Date: Wed, 7 Feb 2024 13:00:08 +0000 Subject: [PATCH] Updating SmallRye-config to 3.5.2 - Updating SmallRye-config to 3.5.2 - Updating SmallRye-common to 2.2.0 - Overlaying EnvconfigSource from SmallRye-config to add fix for issues #27482 and #27523 - Updating OLSmallRyeConfigBuilder.java to reload EnvVars upon checkpoint restore (part of fix for #27523) --- .../check_this_in_if_it_changes/pom.xml | 16 +- dev/cnf/oss_dependencies.maven | 16 +- .../bnd.bnd | 12 +- .../bnd.bnd | 8 +- .../extension/OLSmallRyeConfigBuilder.java | 17 +- .../io/smallrye/config/EnvConfigSource.java | 401 ++++++++++++++++++ 6 files changed, 441 insertions(+), 29 deletions(-) create mode 100644 dev/io.openliberty.io.smallrye.config3/src/io/smallrye/config/EnvConfigSource.java diff --git a/dev/cnf/dependabot/check_this_in_if_it_changes/pom.xml b/dev/cnf/dependabot/check_this_in_if_it_changes/pom.xml index 4ac0bfcae869..55cd34428bf6 100644 --- a/dev/cnf/dependabot/check_this_in_if_it_changes/pom.xml +++ b/dev/cnf/dependabot/check_this_in_if_it_changes/pom.xml @@ -914,7 +914,7 @@ io.smallrye.common smallrye-common-annotation - 2.1.0 + 2.2.0 io.smallrye.common @@ -924,7 +924,7 @@ io.smallrye.common smallrye-common-classloader - 2.1.0 + 2.2.0 io.smallrye.common @@ -934,7 +934,7 @@ io.smallrye.common smallrye-common-constraint - 2.1.0 + 2.2.0 io.smallrye.common @@ -944,7 +944,7 @@ io.smallrye.common smallrye-common-expression - 2.1.0 + 2.2.0 io.smallrye.common @@ -954,7 +954,7 @@ io.smallrye.common smallrye-common-function - 2.1.0 + 2.2.0 io.smallrye.config @@ -964,7 +964,7 @@ io.smallrye.config smallrye-config-common - 3.3.0 + 3.5.2 io.smallrye.config @@ -974,7 +974,7 @@ io.smallrye.config smallrye-config-core - 3.3.0 + 3.5.2 io.smallrye.config @@ -984,7 +984,7 @@ io.smallrye.config smallrye-config - 3.3.0 + 3.5.2 io.smallrye.reactive diff --git a/dev/cnf/oss_dependencies.maven b/dev/cnf/oss_dependencies.maven index db2881a54ff8..c32e6aed0d18 100644 --- a/dev/cnf/oss_dependencies.maven +++ b/dev/cnf/oss_dependencies.maven @@ -178,21 +178,21 @@ io.prometheus:simpleclient_tracer_otel_agent:0.15.0 io.reactivex.rxjava2:rxjava:2.2.19 io.reactivex:rxjava:1.3.8 io.smallrye.common:smallrye-common-annotation:1.6.0 -io.smallrye.common:smallrye-common-annotation:2.1.0 +io.smallrye.common:smallrye-common-annotation:2.2.0 io.smallrye.common:smallrye-common-classloader:1.6.0 -io.smallrye.common:smallrye-common-classloader:2.1.0 +io.smallrye.common:smallrye-common-classloader:2.2.0 io.smallrye.common:smallrye-common-constraint:1.6.0 -io.smallrye.common:smallrye-common-constraint:2.1.0 +io.smallrye.common:smallrye-common-constraint:2.2.0 io.smallrye.common:smallrye-common-expression:1.6.0 -io.smallrye.common:smallrye-common-expression:2.1.0 +io.smallrye.common:smallrye-common-expression:2.2.0 io.smallrye.common:smallrye-common-function:1.6.0 -io.smallrye.common:smallrye-common-function:2.1.0 +io.smallrye.common:smallrye-common-function:2.2.0 io.smallrye.config:smallrye-config-common:2.5.1 -io.smallrye.config:smallrye-config-common:3.3.0 +io.smallrye.config:smallrye-config-common:3.5.2 io.smallrye.config:smallrye-config-core:2.5.1 -io.smallrye.config:smallrye-config-core:3.3.0 +io.smallrye.config:smallrye-config-core:3.5.2 io.smallrye.config:smallrye-config:2.5.1 -io.smallrye.config:smallrye-config:3.3.0 +io.smallrye.config:smallrye-config:3.5.2 io.smallrye.reactive:mutiny-reactive-streams-operators-jakarta:2.5.6 io.smallrye.reactive:mutiny-zero-flow-adapters:1.0.0 io.smallrye.reactive:mutiny:2.5.6 diff --git a/dev/io.openliberty.io.smallrye.common2/bnd.bnd b/dev/io.openliberty.io.smallrye.common2/bnd.bnd index 7522064e5dd4..092cb8d5d28a 100644 --- a/dev/io.openliberty.io.smallrye.common2/bnd.bnd +++ b/dev/io.openliberty.io.smallrye.common2/bnd.bnd @@ -20,13 +20,13 @@ Bundle-SymbolicName: io.openliberty.io.smallrye.common2; singleton:=true Export-Package: \ - io.smallrye.common.*; version="2.1.0" + io.smallrye.common.*; version="2.2.0" instrument.disabled: true -buildpath: \ - io.smallrye.common:smallrye-common-annotation;version=2.1.0;strategy=exact, \ - io.smallrye.common:smallrye-common-expression;version=2.1.0;strategy=exact, \ - io.smallrye.common:smallrye-common-constraint;version=2.1.0;strategy=exact, \ - io.smallrye.common:smallrye-common-function;version=2.1.0;strategy=exact, \ - io.smallrye.common:smallrye-common-classloader;version=2.1.0;strategy=exact + io.smallrye.common:smallrye-common-annotation;version=2.2.0;strategy=exact, \ + io.smallrye.common:smallrye-common-expression;version=2.2.0;strategy=exact, \ + io.smallrye.common:smallrye-common-constraint;version=2.2.0;strategy=exact, \ + io.smallrye.common:smallrye-common-function;version=2.2.0;strategy=exact, \ + io.smallrye.common:smallrye-common-classloader;version=2.2.0;strategy=exact diff --git a/dev/io.openliberty.io.smallrye.config3/bnd.bnd b/dev/io.openliberty.io.smallrye.config3/bnd.bnd index b28aad77f554..c0a8998fc478 100644 --- a/dev/io.openliberty.io.smallrye.config3/bnd.bnd +++ b/dev/io.openliberty.io.smallrye.config3/bnd.bnd @@ -47,14 +47,14 @@ Import-Package: \ * Export-Package: \ - io.smallrye.config.*;version="3.3.0" + io.smallrye.config.*;version="3.5.2" -buildpath: \ io.openliberty.io.smallrye.common2;version=latest, \ - io.smallrye.config:smallrye-config;version=3.3.0;strategy=exact, \ - io.smallrye.config:smallrye-config-common;version=3.3.0;strategy=exact, \ - io.smallrye.config:smallrye-config-core;version=3.3.0;strategy=exact, \ + io.smallrye.config:smallrye-config;version=3.5.2;strategy=exact, \ + io.smallrye.config:smallrye-config-common;version=3.5.2;strategy=exact, \ + io.smallrye.config:smallrye-config-core;version=3.5.2;strategy=exact, \ io.openliberty.jakarta.cdi.4.0;version=latest, \ io.openliberty.jakarta.annotation.2.1;version=latest, \ io.openliberty.org.eclipse.microprofile.config.3.1;version=latest, \ diff --git a/dev/io.openliberty.io.smallrye.config3/src/io/openliberty/microprofile/config/internal/extension/OLSmallRyeConfigBuilder.java b/dev/io.openliberty.io.smallrye.config3/src/io/openliberty/microprofile/config/internal/extension/OLSmallRyeConfigBuilder.java index 4b467b8aba88..3dd1083f06c2 100644 --- a/dev/io.openliberty.io.smallrye.config3/src/io/openliberty/microprofile/config/internal/extension/OLSmallRyeConfigBuilder.java +++ b/dev/io.openliberty.io.smallrye.config3/src/io/openliberty/microprofile/config/internal/extension/OLSmallRyeConfigBuilder.java @@ -4,7 +4,7 @@ * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -26,6 +26,7 @@ import com.ibm.websphere.ras.annotation.Trivial; import com.ibm.ws.ffdc.annotation.FFDCIgnore; +import io.openliberty.checkpoint.spi.CheckpointHook; import io.openliberty.checkpoint.spi.CheckpointPhase; import io.openliberty.microprofile.config.internal.extension.OLSmallRyeConfigExtension.UnpauseRecording; import io.openliberty.microprofile.config.internal.serverxml.AppPropertyConfigSource; @@ -53,7 +54,17 @@ protected List getDefaultSources() { for (ListIterator iSources = defaultSources.listIterator(); iSources.hasNext();) { ConfigSource source = iSources.next(); if (source instanceof EnvConfigSource) { - iSources.set(new EnvConfigSource(doPrivileged((PrivilegedAction>) System::getenv), source.getOrdinal())); + EnvConfigSource envConfig = new EnvConfigSource(doPrivileged((PrivilegedAction>) System::getenv), source.getOrdinal()); + // If checkpoint has been restored a new checkpointHook will be created on restore to reload the envVars map + if (!CheckpointPhase.getPhase().restored()) { + CheckpointPhase.getPhase().addSingleThreadedHook(new CheckpointHook() { + @Override + public void restore() { + envConfig.reloadEnvironment(); + }; + }); + } + iSources.set(envConfig); } } @@ -92,4 +103,4 @@ private SmallRyeConfig doBuild() { } return config; } -} +} \ No newline at end of file diff --git a/dev/io.openliberty.io.smallrye.config3/src/io/smallrye/config/EnvConfigSource.java b/dev/io.openliberty.io.smallrye.config3/src/io/smallrye/config/EnvConfigSource.java new file mode 100644 index 000000000000..2f22d5bcba54 --- /dev/null +++ b/dev/io.openliberty.io.smallrye.config3/src/io/smallrye/config/EnvConfigSource.java @@ -0,0 +1,401 @@ +/* + * Copyright 2017 Red Hat, Inc. + * + * 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.smallrye.config; + +import static io.smallrye.config.common.utils.ConfigSourceUtil.CONFIG_ORDINAL_KEY; +import static io.smallrye.config.common.utils.StringUtil.isNumeric; +import static io.smallrye.config.common.utils.StringUtil.replaceNonAlphanumericByUnderscores; +import static io.smallrye.config.common.utils.StringUtil.toLowerCaseAndDotted; +import static java.lang.Character.toLowerCase; +import static java.security.AccessController.doPrivileged; + +import java.io.Serializable; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import io.smallrye.config.common.AbstractConfigSource; + +/** + * A {@link org.eclipse.microprofile.config.spi.ConfigSource} to access Environment Variables following the mapping + * rules defined by the MicroProfile Config specification. + *

+ * + * For a given property name foo.bar.baz, is matched to an environment variable with the following rules: + * + *

    + *
  1. Exact match (foo.bar.baz)
  2. + *
  3. Replace each character that is neither alphanumeric nor _ with _ + * (foo_bar_baz)
  4. + *
  5. Replace each character that is neither alphanumeric nor _ with _; then convert the name to + * upper case (FOO_BAR_BAZ)
  6. + *
+ *

+ * + * Additionally, this implementation provides candidate matching dotted property name from the Environment + * Variable name. These are required when a consumer relies on the list of properties to find additional + * configurations. The MicroProfile Config specification defines a set of conversion rules to look up and find + * values from environment variables even when using their dotted version, but it is unclear about property names. + *
+ * Because an environment variable name may only be represented by a subset of characters, it is not possible + * to represent exactly a dotted version name from an environment variable name, so consumers must be aware of such + * limitations. + */ + +/* + * This is the EnvConfigSource from SmallRye-config-3.5.2 + * (https://github.com/smallrye/smallrye-config/blob/3.5.2/implementation/src/main/java/io/smallrye/config/EnvConfigSource.java) + * Also with fix for https://github.com/smallrye/smallrye-config/issues/1099 + */ +public class EnvConfigSource extends AbstractConfigSource { + private static final long serialVersionUID = -4525015934376795496L; + + private static final int DEFAULT_ORDINAL = 300; + + private volatile EnvVars envVars; + + protected EnvConfigSource() { + this(DEFAULT_ORDINAL); + } + + protected EnvConfigSource(final int ordinal) { + this(getEnvProperties(), ordinal); + } + + public EnvConfigSource(final Map properties, final int ordinal) { + super("EnvConfigSource", getEnvOrdinal(properties, ordinal)); + this.envVars = new EnvVars(properties); + } + + @Override + public Map getProperties() { + Map properties = new HashMap<>(); + for (Map.Entry entry : envVars.getEnv().entrySet()) { + EnvEntry entryValue = entry.getValue(); + if (entryValue.getEntries() != null) { + properties.putAll(entryValue.getEntries()); + } else { + properties.put(entryValue.getName(), entryValue.getValue()); + } + } + return properties; + } + + // reload EnvVars on checkpoint restore + public void reloadEnvironment() { + this.envVars = new EnvVars(getEnvProperties()); + + } + + @Override + public Set getPropertyNames() { + return envVars.getNames(); + } + + @Override + public String getValue(final String propertyName) { + return envVars.get(propertyName); + } + + boolean hasPropertyName(final String propertyName) { + return envVars.getEnv().containsKey(new EnvName(propertyName)); + } + + /** + * A new Map with the contents of System.getEnv. In the Windows implementation, the Map is an extension of + * ProcessEnvironment. This causes issues with Graal and native mode, since ProcessEnvironment should not be + * instantiated in the heap. + */ + private static Map getEnvProperties() { + return doPrivileged(new PrivilegedAction>() { + @Override + public Map run() { + return new HashMap<>(System.getenv()); + } + }); + } + + private static int getEnvOrdinal(final Map properties, final int ordinal) { + String value = properties.get(CONFIG_ORDINAL_KEY); + if (value == null) { + value = properties.get(CONFIG_ORDINAL_KEY.toUpperCase()); + } + if (value != null) { + return Converters.INTEGER_CONVERTER.convert(value); + } + return ordinal; + } + + Object writeReplace() { + return new Ser(); + } + + static final class Ser implements Serializable { + private static final long serialVersionUID = 6812312718645271331L; + + Object readResolve() { + return new EnvConfigSource(); + } + } + + static final class EnvVars implements Serializable { + private static final long serialVersionUID = -56318356411229247L; + + private final Map env; + private final Set names; + + public EnvVars(final Map properties) { + this.env = new HashMap<>(properties.size()); + this.names = new HashSet<>(properties.size() * 2); + for (Map.Entry entry : properties.entrySet()) { + EnvName envName = new EnvName(entry.getKey()); + EnvEntry envEntry = env.get(envName); + if (envEntry == null) { + env.put(envName, new EnvEntry(entry.getKey(), entry.getValue())); + } else { + envEntry.add(entry.getKey(), entry.getValue()); + } + this.names.add(entry.getKey()); + this.names.add(toLowerCaseAndDotted(entry.getKey())); + } + } + + public String get(final String propertyName) { + EnvEntry envEntry = env.get(new EnvName(propertyName)); + if (envEntry != null) { + String value = envEntry.get(); + if (value != null) { + return value; + } + + value = envEntry.getEntries().get(propertyName); + if (value != null) { + return value; + } + + String envName = replaceNonAlphanumericByUnderscores(propertyName); + value = envEntry.getEntries().get(envName); + if (value != null) { + return value; + } + + return envEntry.envEntries.get(envName.toUpperCase()); + } + return null; + } + + public Map getEnv() { + return env; + } + + public Set getNames() { + return names; + } + } + + static final class EnvName implements Serializable { + private static final long serialVersionUID = -2679716955093904512L; + + private final String name; + + public EnvName(final String name) { + assert name != null; + this.name = name; + } + + public String getName() { + return name; + } + + // Adding in method from StringUtil from un-released 3.6.0 + public static boolean isAsciiLetterOrDigit(char c) { + return 'a' <= c && c <= 'z' || + 'A' <= c && c <= 'Z' || + '0' <= c && c <= '9'; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final EnvName that = (EnvName) o; + return equals(this.name, that.name); + } + + @Override + public int hashCode() { + int h = 0; + int length = name.length(); + if (length >= 2) { + if (name.charAt(length - 1) == '_' && name.charAt(length - 2) == '_') { + length = length - 1; + } + } + + for (int i = 0; i < length; i++) { + char c = name.charAt(i); + if (i == 0 && length > 1) { + // The first '%' or '_' is meaninful because it represents a profiled property name + if ((c == '%' || c == '_') && isAsciiLetterOrDigit(name.charAt(i + 1))) { + h = 31 * h + 31; + continue; + } + } + + if (isAsciiLetterOrDigit(c)) { + h = 31 * h + toLowerCase(c); + } + // h = 31 * h + c; + } + return h; + } + + @SuppressWarnings("squid:S4973") + static boolean equals(final String name, final String other) { + //noinspection StringEquality + if (name == other) { + return true; + } + + if (name.isEmpty() && other.isEmpty()) { + return true; + } + + if (name.isEmpty() || other.isEmpty()) { + return false; + } + + char n; + char o; + + int matchPosition = name.length() - 1; + for (int i = other.length() - 1; i >= 0; i--) { + if (matchPosition == -1) { + return false; + } + + o = other.charAt(i); + n = name.charAt(matchPosition); + + // profile + if (i == 0 && (o == '%' || o == '_')) { + if (n == '%' || n == '_') { + return true; + } + } + + if (o == '.') { + if (n != '.' && n != '-' && n != '_' && n != '/') { + return false; + } + } else if (o == '-') { + if (n != '.' && n != '-' && n != '_' && n != '/') { + return false; + } + } else if (o == '"') { + if (n != '"' && n != '_') { + return false; + } else if (n == '_' && name.length() - 1 == matchPosition) { + matchPosition = name.lastIndexOf("_", matchPosition - 1); + if (matchPosition == -1) { + return false; + } + } + } else if (o == ']') { + if (n != ']' && n != '_') { + return false; + } + int beginIndexed = other.lastIndexOf('[', i); + if (beginIndexed != -1) { + int range = i - beginIndexed - 1; + if (name.lastIndexOf('_', matchPosition - 1) == matchPosition - range - 1 + || name.lastIndexOf('[', matchPosition - 1) == matchPosition - range - 1) { + if (isNumeric(other, beginIndexed + range, i) + && isNumeric(name, matchPosition - range, matchPosition)) { + matchPosition = matchPosition - range - 2; + i = i - range - 1; + continue; + } + } + } + return false; + } else if (o == '_') { + if (isAsciiLetterOrDigit(n)) { + return false; + } else if (n == '"' && other.length() - 1 == i) { + i = other.lastIndexOf("_", i - 1); + if (i == -1) { + return false; + } + } + } else if (!isAsciiLetterOrDigit(o)) { + if (o != n && n != '_') { + return false; + + } + } else if (toLowerCase(o) != toLowerCase(n)) { + return false; + } + matchPosition--; + } + + return matchPosition <= 0; + } + } + + static final class EnvEntry implements Serializable { + private static final long serialVersionUID = -8786927401082731020L; + + private final String name; + private final String value; + private Map envEntries; + + EnvEntry(final String name, final String value) { + this.name = name; + this.value = value; + } + + String getName() { + return name; + } + + String getValue() { + return value; + } + + String get() { + return envEntries == null ? value : null; + } + + Map getEntries() { + return envEntries; + } + + void add(String name, String value) { + if (envEntries == null) { + envEntries = new HashMap<>(); + envEntries.put(this.name, this.value); + } + envEntries.put(name, value); + } + } +} \ No newline at end of file