Skip to content

Commit

Permalink
Changes to Mapping API to improve Quarkus integration. (#408)
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Oct 1, 2020
1 parent 13efd6d commit beae833
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 55 deletions.
Expand Up @@ -28,7 +28,6 @@
* mapping has completed.
*/
public final class ConfigMappingContext {

private final Map<Class<?>, Map<String, Map<Object, Object>>> enclosedThings = new IdentityHashMap<>();
private final Map<Class<?>, Map<String, ConfigMappingObject>> roots = new IdentityHashMap<>();
private final Map<Class<?>, Map<String, Converter<?>>> convertersByTypeAndField = new IdentityHashMap<>();
Expand Down Expand Up @@ -66,7 +65,7 @@ public void registerEnclosedField(Class<?> enclosingType, String key, Object enc
}

public <T> T constructGroup(Class<T> interfaceType) {
final T mappingObject = ConfigMappingObjectLoader.configMappingObject(interfaceType, this);
final T mappingObject = ConfigMappingLoader.configMappingObject(interfaceType, this);
allInstances.add((ConfigMappingObject) mappingObject);
return mappingObject;
}
Expand Down
Expand Up @@ -17,9 +17,11 @@
import java.lang.reflect.WildcardType;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -146,6 +148,33 @@ public String getClassName() {
return className;
}

List<ConfigMappingInterface> getNested() {
ArrayList<ConfigMappingInterface> nested = new ArrayList<>();
getNested(properties, nested);
return nested;
}

static void getNested(final Property[] properties, final List<ConfigMappingInterface> nested) {
for (Property property : properties) {
if (property instanceof GroupProperty) {
GroupProperty groupProperty = (GroupProperty) property;
ConfigMappingInterface group = groupProperty.getGroupType();
nested.add(group);
getNested(group.properties, nested);
}

if (property instanceof OptionalProperty) {
OptionalProperty optionalProperty = (OptionalProperty) property;
if (optionalProperty.getNestedProperty() instanceof GroupProperty) {
GroupProperty groupProperty = (GroupProperty) optionalProperty.getNestedProperty();
ConfigMappingInterface group = groupProperty.getGroupType();
nested.add(group);
getNested(group.properties, nested);
}
}
}
}

public static abstract class Property {
private final Method method;
private final String propertyName;
Expand Down
Expand Up @@ -4,10 +4,12 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.List;

import io.smallrye.common.classloader.ClassDefiner;

public final class ConfigMappingObjectLoader {
public final class ConfigMappingLoader {
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();

private static final ClassValue<ConfigMappingObjectHolder> CACHE = new ClassValue<ConfigMappingObjectHolder>() {
Expand All @@ -17,8 +19,16 @@ protected ConfigMappingObjectHolder computeValue(final Class<?> type) {
}
};

public static ConfigMappingMetadata getConfigMappingMetadata(Class<?> interfaceType) {
return ConfigMappingInterface.getConfigurationInterface(interfaceType);
public static List<ConfigMappingMetadata> getConfigMappingsMetadata(Class<?> type) {
final List<ConfigMappingMetadata> mappings = new ArrayList<>();
final ConfigMappingInterface configurationInterface = ConfigMappingInterface.getConfigurationInterface(type);
mappings.add(configurationInterface);
mappings.addAll(configurationInterface.getNested());
return mappings;
}

static ConfigMappingInterface getConfigMappingInterface(final Class<?> type) {
return ConfigMappingInterface.getConfigurationInterface(type);
}

static <T> T configMappingObject(Class<T> interfaceType, ConfigMappingContext configMappingContext) {
Expand Down Expand Up @@ -46,26 +56,24 @@ static <T> T configMappingObject(Class<T> interfaceType, ConfigMappingContext co
}

@SuppressWarnings("unchecked")
static <T> Class<? extends ConfigMappingObject> getImplementationClass(Class<T> interfaceType) {
final ConfigMappingMetadata mappingMetadata = getConfigMappingMetadata(interfaceType);
static <T> Class<? extends ConfigMappingObject> getImplementationClass(Class<T> type) {
final ConfigMappingMetadata mappingMetadata = ConfigMappingInterface.getConfigurationInterface(type);
return (Class<? extends ConfigMappingObject>) loadClass(type.getClassLoader(),
mappingMetadata.getClassName(),
mappingMetadata.getClassBytes());
}

static Class<?> loadClass(final ClassLoader classLoader, final String className, final byte[] classBytes) {
// Check if the interface implementation was already loaded. If not we will load it.
try {
return (Class<? extends ConfigMappingObject>) interfaceType.getClassLoader()
.loadClass(mappingMetadata.getClassName());
return classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
// Ignore
return loadClass(className, classBytes);
}

return createMappingObjectClass(mappingMetadata.getClassName(),
mappingMetadata.getClassBytes());
}

@SuppressWarnings("unchecked")
static Class<? extends ConfigMappingObject> createMappingObjectClass(final String className,
final byte[] classBytes) {
return (Class<? extends ConfigMappingObject>) ClassDefiner.defineClass(LOOKUP, ConfigMappingObjectLoader.class,
className, classBytes);
private static Class<?> loadClass(final String className, final byte[] classBytes) {
return ClassDefiner.defineClass(LOOKUP, ConfigMappingLoader.class, className, classBytes);
}

private static final class ConfigMappingObjectHolder {
Expand Down
Expand Up @@ -6,7 +6,7 @@
import static io.smallrye.config.ConfigMappingInterface.MayBeOptionalProperty;
import static io.smallrye.config.ConfigMappingInterface.PrimitiveProperty;
import static io.smallrye.config.ConfigMappingInterface.Property;
import static io.smallrye.config.ConfigMappingInterface.getConfigurationInterface;
import static io.smallrye.config.ConfigMappingLoader.getConfigMappingInterface;

import java.io.Serializable;
import java.lang.reflect.Method;
Expand Down Expand Up @@ -43,16 +43,16 @@ final class ConfigMappingProvider implements Serializable {
IGNORE_EVERYTHING = map;
}

private final Map<String, List<ConfigMappingInterface>> roots;
private final Map<String, List<Class<?>>> roots;
private final KeyMap<BiConsumer<ConfigMappingContext, NameIterator>> matchActions;
private final KeyMap<String> defaultValues;

ConfigMappingProvider(final Builder builder) {
roots = new HashMap<>(builder.roots);
this.roots = new HashMap<>(builder.roots);
final ArrayDeque<String> currentPath = new ArrayDeque<>();
KeyMap<BiConsumer<ConfigMappingContext, NameIterator>> matchActions = new KeyMap<>();
KeyMap<String> defaultValues = new KeyMap<>();
for (Map.Entry<String, List<ConfigMappingInterface>> entry : roots.entrySet()) {
for (Map.Entry<String, List<Class<?>>> entry : roots.entrySet()) {
NameIterator rootNi = new NameIterator(entry.getKey());
while (rootNi.hasNext()) {
final String nextSegment = rootNi.getNextSegment();
Expand All @@ -61,11 +61,12 @@ final class ConfigMappingProvider implements Serializable {
}
rootNi.next();
}
List<ConfigMappingInterface> roots = entry.getValue();
for (ConfigMappingInterface root : roots) {
List<Class<?>> roots = entry.getValue();
for (Class<?> root : roots) {
// construct the lazy match actions for each group
BiFunction<ConfigMappingContext, NameIterator, ConfigMappingObject> ef = new GetRootAction(root, entry);
processEagerGroup(currentPath, matchActions, defaultValues, root, ef);
BiFunction<ConfigMappingContext, NameIterator, ConfigMappingObject> ef = new GetRootAction(root,
entry.getKey());
processEagerGroup(currentPath, matchActions, defaultValues, getConfigMappingInterface(root), ef);
}
currentPath.clear();
}
Expand Down Expand Up @@ -235,7 +236,6 @@ private void processEagerGroup(final ArrayDeque<String> currentPath,
matchActions.findOrAdd(currentPath).putRootValue(DO_NOTHING);
}
} else if (property.isGroup()) {

processEagerGroup(currentPath, matchActions, defaultValues, property.asGroup().getGroupType(),
new GetOrCreateEnclosingGroupInGroup(getEnclosingFunction, group, property.asGroup()));
} else if (property.isPrimitive()) {
Expand Down Expand Up @@ -427,17 +427,16 @@ private void processLazyMap(final ArrayDeque<String> currentPath,
}

static class GetRootAction implements BiFunction<ConfigMappingContext, NameIterator, ConfigMappingObject> {
private final ConfigMappingInterface root;
private final Map.Entry<String, List<ConfigMappingInterface>> entry;
private final Class<?> root;
private final String rootPath;

GetRootAction(final ConfigMappingInterface root, final Map.Entry<String, List<ConfigMappingInterface>> entry) {
GetRootAction(final Class<?> root, final String rootPath) {
this.root = root;
this.entry = entry;
this.rootPath = rootPath;
}

public ConfigMappingObject apply(final ConfigMappingContext mc, final NameIterator ni) {
return mc
.getRoot(root.getInterfaceType(), entry.getKey());
return mc.getRoot(root, rootPath);
}
}

Expand Down Expand Up @@ -611,15 +610,14 @@ ConfigMappings mapConfiguration(SmallRyeConfig config, ConfigMappings mappings)
Assert.checkNotNullParam("config", config);
final ConfigMappingContext context = new ConfigMappingContext(config);
// eagerly populate roots
for (Map.Entry<String, List<ConfigMappingInterface>> entry : roots.entrySet()) {
for (Map.Entry<String, List<Class<?>>> entry : roots.entrySet()) {
String path = entry.getKey();
List<ConfigMappingInterface> roots = entry.getValue();
for (ConfigMappingInterface root : roots) {
List<Class<?>> roots = entry.getValue();
for (Class<?> root : roots) {
StringBuilder sb = context.getStringBuilder();
sb.replace(0, sb.length(), path);
Class<?> type = root.getInterfaceType();
ConfigMappingObject group = (ConfigMappingObject) context.constructGroup(type);
context.registerRoot(type, path, group);
ConfigMappingObject group = (ConfigMappingObject) context.constructGroup(root);
context.registerRoot(root, path, group);
}
}
// lazily sweep
Expand Down Expand Up @@ -659,21 +657,16 @@ private boolean isPropertyInRoot(String propertyName) {
}

public static final class Builder {
final Map<String, List<ConfigMappingInterface>> roots = new HashMap<>();
final Map<String, List<Class<?>>> roots = new HashMap<>();
final List<String[]> ignored = new ArrayList<>();

Builder() {
}

public Builder addRoot(String path, Class<?> type) {
Assert.checkNotNullParam("type", type);
return addRoot(path, getConfigurationInterface(type));
}

public Builder addRoot(String path, ConfigMappingInterface info) {
Assert.checkNotNullParam("path", path);
Assert.checkNotNullParam("info", info);
roots.computeIfAbsent(path, k -> new ArrayList<>(4)).add(info);
Assert.checkNotNullParam("type", type);
roots.computeIfAbsent(path, k -> new ArrayList<>(4)).add(type);
return this;
}

Expand Down
Expand Up @@ -32,8 +32,12 @@ public void registerConfigMappings(final SmallRyeConfig config, final Set<Config
for (ConfigMappingWithPrefix mapping : mappings) {
builder.addRoot(mapping.getPrefix(), mapping.getKlass());
}
final ConfigMappingProvider mappingProvider = builder.build();

registerConfigMappings(config, builder.build());
}

public void registerConfigMappings(final SmallRyeConfig config, final ConfigMappingProvider mappingProvider)
throws ConfigValidationException {
for (ConfigSource configSource : config.getConfigSources()) {
if (configSource instanceof DefaultValuesConfigSource) {
final DefaultValuesConfigSource defaultValuesConfigSource = (DefaultValuesConfigSource) configSource;
Expand Down
Expand Up @@ -2,13 +2,15 @@

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.List;

import org.junit.jupiter.api.Test;

class ConfigMappingObjectLoaderTest {
class ConfigMappingLoaderTest {
@Test
void multipleLoads() {
ConfigMappingObjectLoader.getImplementationClass(Server.class);
ConfigMappingObjectLoader.getImplementationClass(Server.class);
ConfigMappingLoader.getImplementationClass(Server.class);
ConfigMappingLoader.getImplementationClass(Server.class);

SmallRyeConfig config = new SmallRyeConfigBuilder().withSources(
KeyValuesConfigSource.config("server.host", "localhost", "server.port", "8080"))
Expand All @@ -22,10 +24,12 @@ void multipleLoads() {

@Test
void loadManually() {
ConfigMappingMetadata mappingMetadata = ConfigMappingObjectLoader.getConfigMappingMetadata(ServerManual.class);
ConfigMappingObjectLoader.createMappingObjectClass(mappingMetadata.getClassName(), mappingMetadata.getClassBytes());
ConfigMappingObjectLoader.getImplementationClass(ServerManual.class);
ConfigMappingObjectLoader.getImplementationClass(ServerManual.class);
List<ConfigMappingMetadata> configMappingsMetadata = ConfigMappingLoader.getConfigMappingsMetadata(ServerManual.class);
configMappingsMetadata.forEach(
mappingMetadata -> ConfigMappingLoader.loadClass(ServerManual.class.getClassLoader(),
mappingMetadata.getClassName(), mappingMetadata.getClassBytes()));
ConfigMappingLoader.getImplementationClass(ServerManual.class);
ConfigMappingLoader.getImplementationClass(ServerManual.class);
}

@ConfigMapping(prefix = "server")
Expand Down

0 comments on commit beae833

Please sign in to comment.