From c76ecfc0bb34112de0c7c6e3737056dc843e4b17 Mon Sep 17 00:00:00 2001 From: Ken Finnigan Date: Wed, 13 Nov 2019 09:36:26 -0500 Subject: [PATCH 1/2] Fixes #4516 - Ensures Config is serializable - Passes Config serialization TCK test --- .../AbstractDelegatingConfigSource.java | 6 ++- .../AbstractRawDefaultConfigSource.java | 5 ++- .../configuration/CidrAddressConverter.java | 6 ++- .../DeploymentProfileConfigSource.java | 23 +++++++++++ .../configuration/DurationConverter.java | 4 +- .../configuration/ExpandingConfigSource.java | 25 +++++++++++- .../configuration/HyphenateEnumConverter.java | 4 +- .../configuration/InetAddressConverter.java | 5 ++- .../InetSocketAddressConverter.java | 5 ++- .../configuration/MemorySizeConverter.java | 4 +- .../runtime/configuration/PathConverter.java | 5 ++- .../runtime/configuration/RegexConverter.java | 5 ++- .../runtime/logging/LevelConverter.java | 5 ++- tcks/microprofile-config/pom.xml | 3 +- .../tck/config/CustomConfigProviderTest.java | 35 ++++++++++++++++- .../tck/config/CustomObjectInputStream.java | 25 ++++++++++++ .../http/TestHTTPConfigSourceProvider.java | 39 +++++++++++-------- 17 files changed, 171 insertions(+), 33 deletions(-) create mode 100644 tcks/microprofile-config/src/test/java/io/quarkus/tck/config/CustomObjectInputStream.java diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractDelegatingConfigSource.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractDelegatingConfigSource.java index 99bb69b3c0af0..bf7672e3eb2ee 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractDelegatingConfigSource.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractDelegatingConfigSource.java @@ -1,5 +1,6 @@ package io.quarkus.runtime.configuration; +import java.io.Serializable; import java.util.Map; import java.util.Set; @@ -12,9 +13,10 @@ /** * A base class for configuration sources which delegate to other configuration sources. */ -public abstract class AbstractDelegatingConfigSource implements ConfigSource { +public abstract class AbstractDelegatingConfigSource implements ConfigSource, Serializable { + private static final long serialVersionUID = -6636734120743034580L; protected final ConfigSource delegate; - private Map propertiesMap; + protected Map propertiesMap; /** * Construct a new instance. diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractRawDefaultConfigSource.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractRawDefaultConfigSource.java index 3e2f4915cd5ff..3b1d418c9a72b 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractRawDefaultConfigSource.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractRawDefaultConfigSource.java @@ -1,5 +1,6 @@ package io.quarkus.runtime.configuration; +import java.io.Serializable; import java.util.Collections; import java.util.Map; @@ -8,7 +9,9 @@ /** * The base class for the config source that yields the 'raw' default values. */ -public abstract class AbstractRawDefaultConfigSource implements ConfigSource { +public abstract class AbstractRawDefaultConfigSource implements ConfigSource, Serializable { + private static final long serialVersionUID = 2524612253582530249L; + protected AbstractRawDefaultConfigSource() { } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/CidrAddressConverter.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/CidrAddressConverter.java index 708693b26cccb..2443f5cec5e42 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/CidrAddressConverter.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/CidrAddressConverter.java @@ -2,6 +2,8 @@ import static io.quarkus.runtime.configuration.ConverterSupport.DEFAULT_QUARKUS_CONVERTER_PRIORITY; +import java.io.Serializable; + import javax.annotation.Priority; import org.eclipse.microprofile.config.spi.Converter; @@ -12,7 +14,9 @@ * A converter which converts a CIDR address into an instance of {@link CidrAddress}. */ @Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY) -public class CidrAddressConverter implements Converter { +public class CidrAddressConverter implements Converter, Serializable { + + private static final long serialVersionUID = 2023552088048952902L; @Override public CidrAddress convert(String value) { diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/DeploymentProfileConfigSource.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/DeploymentProfileConfigSource.java index 236286f6a4533..c7da037b9d170 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/DeploymentProfileConfigSource.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/DeploymentProfileConfigSource.java @@ -1,5 +1,7 @@ package io.quarkus.runtime.configuration; +import java.io.ObjectStreamException; +import java.io.Serializable; import java.util.HashSet; import java.util.Set; import java.util.function.UnaryOperator; @@ -11,6 +13,7 @@ * A configuration source which supports deployment profiles. */ public class DeploymentProfileConfigSource extends AbstractDelegatingConfigSource { + private static final long serialVersionUID = -8001338475089294128L; private final String profilePrefix; @@ -35,6 +38,26 @@ public DeploymentProfileConfigSource(final ConfigSource delegate, final String p profilePrefix = "%" + profileName + "."; } + Object writeReplace() throws ObjectStreamException { + return new Ser(delegate, profilePrefix); + } + + static final class Ser implements Serializable { + private static final long serialVersionUID = -4618790131794331510L; + + final ConfigSource d; + final String p; + + Ser(final ConfigSource d, String p) { + this.d = d; + this.p = p; + } + + Object readResolve() { + return new DeploymentProfileConfigSource(d, p); + } + } + public Set getPropertyNames() { Set propertyNames = delegate.getPropertyNames(); //if a key is only present in a profile we still want the unprofiled key name to show up diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/DurationConverter.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/DurationConverter.java index 6e1406aedc601..8c04d55c5e4aa 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/DurationConverter.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/DurationConverter.java @@ -2,6 +2,7 @@ import static io.quarkus.runtime.configuration.ConverterSupport.DEFAULT_QUARKUS_CONVERTER_PRIORITY; +import java.io.Serializable; import java.time.Duration; import java.time.format.DateTimeParseException; import java.util.regex.Pattern; @@ -14,7 +15,8 @@ * A converter for a {@link Duration} interface. */ @Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY) -public class DurationConverter implements Converter { +public class DurationConverter implements Converter, Serializable { + private static final long serialVersionUID = 7499347081928776532L; private static final String PERIOD_OF_TIME = "PT"; private static final Pattern DIGITS = Pattern.compile("^[-+]?\\d+$"); private static final Pattern START_WITH_DIGITS = Pattern.compile("^[-+]?\\d+.*"); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ExpandingConfigSource.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ExpandingConfigSource.java index 86c28d94a8a22..b5aeb7e9c8460 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ExpandingConfigSource.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ExpandingConfigSource.java @@ -1,5 +1,7 @@ package io.quarkus.runtime.configuration; +import java.io.ObjectStreamException; +import java.io.Serializable; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.UnaryOperator; @@ -13,6 +15,7 @@ */ public class ExpandingConfigSource extends AbstractDelegatingConfigSource { + private static final long serialVersionUID = 1075000015425893741L; private static final ThreadLocal NO_EXPAND = new ThreadLocal<>(); public static UnaryOperator wrapper(Cache cache) { @@ -44,6 +47,24 @@ public String getValue(final String propertyName) { return isExpanding() ? expand(delegateValue) : delegateValue; } + Object writeReplace() throws ObjectStreamException { + return new Ser(delegate); + } + + static final class Ser implements Serializable { + private static final long serialVersionUID = 3633535720479375279L; + + final ConfigSource d; + + Ser(final ConfigSource d) { + this.d = d; + } + + Object readResolve() { + return new ExpandingConfigSource(d, new Cache()); + } + } + String expand(final String value) { return expandValue(value, cache); } @@ -83,7 +104,9 @@ public static String expandValue(String value, Cache cache) { /** * An expression cache to use with {@link ExpandingConfigSource}. */ - public static final class Cache { + public static final class Cache implements Serializable { + private static final long serialVersionUID = 6111143168103886992L; + // this is a cache of compiled expressions, NOT a cache of expanded values final ConcurrentHashMap exprCache = new ConcurrentHashMap<>(); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/HyphenateEnumConverter.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/HyphenateEnumConverter.java index 20032f9b714f6..81e7edcb9c174 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/HyphenateEnumConverter.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/HyphenateEnumConverter.java @@ -1,5 +1,6 @@ package io.quarkus.runtime.configuration; +import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; @@ -12,9 +13,10 @@ /** * A converter for hyphenated enums. */ -public final class HyphenateEnumConverter> implements Converter { +public final class HyphenateEnumConverter> implements Converter, Serializable { private static final String HYPHEN = "-"; private static final Pattern PATTERN = Pattern.compile("([-_]+)"); + private static final long serialVersionUID = 5675903245398498741L; private final Class enumType; private final Map values = new HashMap<>(); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/InetAddressConverter.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/InetAddressConverter.java index b7adf02c8ad6e..a1bbbcd1575cf 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/InetAddressConverter.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/InetAddressConverter.java @@ -2,6 +2,7 @@ import static io.quarkus.runtime.configuration.ConverterSupport.DEFAULT_QUARKUS_CONVERTER_PRIORITY; +import java.io.Serializable; import java.net.InetAddress; import java.net.UnknownHostException; @@ -14,7 +15,9 @@ * A converter which produces values of type {@link InetAddress}. */ @Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY) -public class InetAddressConverter implements Converter { +public class InetAddressConverter implements Converter, Serializable { + + private static final long serialVersionUID = 4539214213710330204L; @Override public InetAddress convert(String value) { diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/InetSocketAddressConverter.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/InetSocketAddressConverter.java index ed3a3785e3512..d6dad37ef5e7e 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/InetSocketAddressConverter.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/InetSocketAddressConverter.java @@ -2,6 +2,7 @@ import static io.quarkus.runtime.configuration.ConverterSupport.DEFAULT_QUARKUS_CONVERTER_PRIORITY; +import java.io.Serializable; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -16,7 +17,9 @@ * an unresolved instance is returned. */ @Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY) -public class InetSocketAddressConverter implements Converter { +public class InetSocketAddressConverter implements Converter, Serializable { + + private static final long serialVersionUID = 1928336763333858343L; @Override public InetSocketAddress convert(String value) { diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/MemorySizeConverter.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/MemorySizeConverter.java index e4361653cfc75..f88cc13f87798 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/MemorySizeConverter.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/MemorySizeConverter.java @@ -2,6 +2,7 @@ import static io.quarkus.runtime.configuration.ConverterSupport.DEFAULT_QUARKUS_CONVERTER_PRIORITY; +import java.io.Serializable; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; @@ -16,10 +17,11 @@ * A converter to support data sizes. */ @Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY) -public class MemorySizeConverter implements Converter { +public class MemorySizeConverter implements Converter, Serializable { private static final Pattern MEMORY_SIZE_PATTERN = Pattern.compile("^(\\d+)([BbKkMmGgTtPpEeZzYy]?)$"); static final BigInteger KILO_BYTES = BigInteger.valueOf(1024); private static final Map MEMORY_SIZE_MULTIPLIERS; + private static final long serialVersionUID = -1988485929047973068L; static { MEMORY_SIZE_MULTIPLIERS = new HashMap<>(); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/PathConverter.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/PathConverter.java index a783840069ad8..e113a49813308 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/PathConverter.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/PathConverter.java @@ -2,6 +2,7 @@ import static io.quarkus.runtime.configuration.ConverterSupport.DEFAULT_QUARKUS_CONVERTER_PRIORITY; +import java.io.Serializable; import java.nio.file.Path; import java.nio.file.Paths; @@ -13,7 +14,9 @@ * A converter for a {@link Path} interface. */ @Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY) -public class PathConverter implements Converter { +public class PathConverter implements Converter, Serializable { + + private static final long serialVersionUID = 4452863383998867844L; @Override public Path convert(String value) { diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/RegexConverter.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/RegexConverter.java index ad6e72093c48b..0f8babbc4b906 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/RegexConverter.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/RegexConverter.java @@ -2,6 +2,7 @@ import static io.quarkus.runtime.configuration.ConverterSupport.DEFAULT_QUARKUS_CONVERTER_PRIORITY; +import java.io.Serializable; import java.util.regex.Pattern; import javax.annotation.Priority; @@ -12,7 +13,9 @@ * A converter to support regular expressions. */ @Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY) -public class RegexConverter implements Converter { +public class RegexConverter implements Converter, Serializable { + + private static final long serialVersionUID = -2627801624423530576L; /** * Construct a new instance. diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/LevelConverter.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/LevelConverter.java index b87e84404739c..8669ff5e50a86 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/LevelConverter.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/LevelConverter.java @@ -2,6 +2,7 @@ import static io.quarkus.runtime.configuration.ConverterSupport.DEFAULT_QUARKUS_CONVERTER_PRIORITY; +import java.io.Serializable; import java.util.logging.Level; import javax.annotation.Priority; @@ -13,7 +14,9 @@ * A simple converter for logging levels. */ @Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY) -public final class LevelConverter implements Converter { +public final class LevelConverter implements Converter, Serializable { + + private static final long serialVersionUID = 704275577610445233L; public Level convert(final String value) { if (value == null || value.isEmpty()) { diff --git a/tcks/microprofile-config/pom.xml b/tcks/microprofile-config/pom.xml index d97f5b4c70982..c8be3cfc68e74 100644 --- a/tcks/microprofile-config/pom.xml +++ b/tcks/microprofile-config/pom.xml @@ -40,8 +40,7 @@ org.eclipse.microprofile.config.tck.EmptyValuesTest - - + org.eclipse.microprofile.config.tck.ConfigProviderTest diff --git a/tcks/microprofile-config/src/test/java/io/quarkus/tck/config/CustomConfigProviderTest.java b/tcks/microprofile-config/src/test/java/io/quarkus/tck/config/CustomConfigProviderTest.java index d08a201848e53..d5f07b821e05d 100644 --- a/tcks/microprofile-config/src/test/java/io/quarkus/tck/config/CustomConfigProviderTest.java +++ b/tcks/microprofile-config/src/test/java/io/quarkus/tck/config/CustomConfigProviderTest.java @@ -1,11 +1,27 @@ package io.quarkus.tck.config; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import javax.inject.Inject; + +import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.tck.ConfigProviderTest; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; +import org.testng.Assert; import org.testng.annotations.Test; import io.quarkus.runtime.configuration.ExpandingConfigSource; public class CustomConfigProviderTest extends ConfigProviderTest { + + @Inject + private Config config; + @Test public void testEnvironmentConfigSource() { // this test fails when there is a expression-like thing in an env prop @@ -17,8 +33,25 @@ public void testEnvironmentConfigSource() { } } - @Test(enabled = false) + @Test public void testInjectedConfigSerializable() { + // Needed a custom ObjectInputStream.resolveClass() to use a ClassLoader with Quarkus generated classes in it + // Everything else is identical to the test class it overwrites + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try (ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream)) { + out.writeObject(config); + } catch (IOException ex) { + Assert.fail("Injected config should be serializable, but could not serialize it", ex); + } + Object readObject = null; + try (ObjectInputStream in = new CustomObjectInputStream( + new ByteArrayInputStream(byteArrayOutputStream.toByteArray()))) { + readObject = in.readObject(); + } catch (IOException | ClassNotFoundException ex) { + Assert.fail("Injected config should be serializable, but could not deserialize a previously serialized instance", + ex); + } + MatcherAssert.assertThat("Deserialized object", readObject, CoreMatchers.instanceOf(Config.class)); } @Test diff --git a/tcks/microprofile-config/src/test/java/io/quarkus/tck/config/CustomObjectInputStream.java b/tcks/microprofile-config/src/test/java/io/quarkus/tck/config/CustomObjectInputStream.java new file mode 100644 index 0000000000000..090dc586661af --- /dev/null +++ b/tcks/microprofile-config/src/test/java/io/quarkus/tck/config/CustomObjectInputStream.java @@ -0,0 +1,25 @@ +package io.quarkus.tck.config; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; + +public class CustomObjectInputStream extends ObjectInputStream { + + public CustomObjectInputStream(InputStream in) throws IOException { + super(in); + } + + @Override + protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { + String name = desc.getName(); + try { + return Class.forName(name, false, Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException ex) { + // Do nothing + } + // Fallback to parent implementation if we can't find the class + return super.resolveClass(desc); + } +} diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPConfigSourceProvider.java b/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPConfigSourceProvider.java index 887e880605cc4..d56e3ce725c7d 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPConfigSourceProvider.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPConfigSourceProvider.java @@ -1,5 +1,6 @@ package io.quarkus.test.common.http; +import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -28,22 +29,26 @@ public class TestHTTPConfigSourceProvider implements ConfigSourceProvider { } public Iterable getConfigSources(final ClassLoader forClassLoader) { - return Collections.singletonList(new ConfigSource() { - public Map getProperties() { - return entries; - } - - public String getValue(final String propertyName) { - return entries.get(propertyName); - } - - public String getName() { - return "test URL provider"; - } - - public int getOrdinal() { - return Integer.MIN_VALUE + 1000; - } - }); + return Collections.singletonList(new TestURLConfigSource()); + } + + static class TestURLConfigSource implements ConfigSource, Serializable { + private static final long serialVersionUID = 4841094273900625000L; + + public Map getProperties() { + return entries; + } + + public String getValue(final String propertyName) { + return entries.get(propertyName); + } + + public String getName() { + return "test URL provider"; + } + + public int getOrdinal() { + return Integer.MIN_VALUE + 1000; + } } } From 74507518009c44f9a22a5ae0b647662c6c78b453 Mon Sep 17 00:00:00 2001 From: Ken Finnigan Date: Tue, 26 Nov 2019 09:28:06 -0500 Subject: [PATCH 2/2] Switch field back to private as it was made protected by mistake --- .../runtime/configuration/AbstractDelegatingConfigSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractDelegatingConfigSource.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractDelegatingConfigSource.java index bf7672e3eb2ee..95d3feddfef55 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractDelegatingConfigSource.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractDelegatingConfigSource.java @@ -16,7 +16,7 @@ public abstract class AbstractDelegatingConfigSource implements ConfigSource, Serializable { private static final long serialVersionUID = -6636734120743034580L; protected final ConfigSource delegate; - protected Map propertiesMap; + private Map propertiesMap; /** * Construct a new instance.