Skip to content

Commit

Permalink
Merge pull request #5758 from kenfinnigan/config-serialization
Browse files Browse the repository at this point in the history
Fixes #4516
  • Loading branch information
dmlloyd authored Nov 26, 2019
2 parents 970d210 + 7450751 commit 1457e12
Show file tree
Hide file tree
Showing 17 changed files with 170 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.runtime.configuration;

import java.io.Serializable;
import java.util.Map;
import java.util.Set;

Expand All @@ -12,7 +13,8 @@
/**
* 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<String, String> propertiesMap;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.runtime.configuration;

import java.io.Serializable;
import java.util.Collections;
import java.util.Map;

Expand All @@ -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() {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<CidrAddress> {
public class CidrAddressConverter implements Converter<CidrAddress>, Serializable {

private static final long serialVersionUID = 2023552088048952902L;

@Override
public CidrAddress convert(String value) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand All @@ -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<String> getPropertyNames() {
Set<String> propertyNames = delegate.getPropertyNames();
//if a key is only present in a profile we still want the unprofiled key name to show up
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -14,7 +15,8 @@
* A converter for a {@link Duration} interface.
*/
@Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY)
public class DurationConverter implements Converter<Duration> {
public class DurationConverter implements Converter<Duration>, 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+.*");
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -13,6 +15,7 @@
*/
public class ExpandingConfigSource extends AbstractDelegatingConfigSource {

private static final long serialVersionUID = 1075000015425893741L;
private static final ThreadLocal<Boolean> NO_EXPAND = new ThreadLocal<>();

public static UnaryOperator<ConfigSource> wrapper(Cache cache) {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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<String, Expression> exprCache = new ConcurrentHashMap<>();

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -12,9 +13,10 @@
/**
* A converter for hyphenated enums.
*/
public final class HyphenateEnumConverter<E extends Enum<E>> implements Converter<E> {
public final class HyphenateEnumConverter<E extends Enum<E>> implements Converter<E>, Serializable {
private static final String HYPHEN = "-";
private static final Pattern PATTERN = Pattern.compile("([-_]+)");
private static final long serialVersionUID = 5675903245398498741L;

private final Class<E> enumType;
private final Map<String, E> values = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -14,7 +15,9 @@
* A converter which produces values of type {@link InetAddress}.
*/
@Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY)
public class InetAddressConverter implements Converter<InetAddress> {
public class InetAddressConverter implements Converter<InetAddress>, Serializable {

private static final long serialVersionUID = 4539214213710330204L;

@Override
public InetAddress convert(String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -16,7 +17,9 @@
* an unresolved instance is returned.
*/
@Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY)
public class InetSocketAddressConverter implements Converter<InetSocketAddress> {
public class InetSocketAddressConverter implements Converter<InetSocketAddress>, Serializable {

private static final long serialVersionUID = 1928336763333858343L;

@Override
public InetSocketAddress convert(String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -16,10 +17,11 @@
* A converter to support data sizes.
*/
@Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY)
public class MemorySizeConverter implements Converter<MemorySize> {
public class MemorySizeConverter implements Converter<MemorySize>, Serializable {
private static final Pattern MEMORY_SIZE_PATTERN = Pattern.compile("^(\\d+)([BbKkMmGgTtPpEeZzYy]?)$");
static final BigInteger KILO_BYTES = BigInteger.valueOf(1024);
private static final Map<String, BigInteger> MEMORY_SIZE_MULTIPLIERS;
private static final long serialVersionUID = -1988485929047973068L;

static {
MEMORY_SIZE_MULTIPLIERS = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -13,7 +14,9 @@
* A converter for a {@link Path} interface.
*/
@Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY)
public class PathConverter implements Converter<Path> {
public class PathConverter implements Converter<Path>, Serializable {

private static final long serialVersionUID = 4452863383998867844L;

@Override
public Path convert(String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -12,7 +13,9 @@
* A converter to support regular expressions.
*/
@Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY)
public class RegexConverter implements Converter<Pattern> {
public class RegexConverter implements Converter<Pattern>, Serializable {

private static final long serialVersionUID = -2627801624423530576L;

/**
* Construct a new instance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -13,7 +14,9 @@
* A simple converter for logging levels.
*/
@Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY)
public final class LevelConverter implements Converter<Level> {
public final class LevelConverter implements Converter<Level>, Serializable {

private static final long serialVersionUID = 704275577610445233L;

public Level convert(final String value) {
if (value == null || value.isEmpty()) {
Expand Down
3 changes: 1 addition & 2 deletions tcks/microprofile-config/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@
<!-- TCK and spec dispute: https://github.com/eclipse/microprofile-config/pull/407 -->
<exclude>org.eclipse.microprofile.config.tck.EmptyValuesTest</exclude>
<!-- "testEnvironmentConfigSource" fails because of the above TCK/spec dispute -->
<!-- "testInjectedConfigSerializable" fails because not all ConfigSource, Converters, etc are serializable -->
<!-- We have a custom version of this TCK class that excludes "testEnvironmentConfigSource" and "testInjectedConfigSerializable", allowing other tests to run -->
<!-- We have a custom version of this TCK class that excludes "testEnvironmentConfigSource", allowing other tests to run -->
<exclude>org.eclipse.microprofile.config.tck.ConfigProviderTest</exclude>
</excludes>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit 1457e12

Please sign in to comment.