Fixes for MP quickstart in native image. (#1564)

* Fixes for MP quickstart in native image.
* Mp Quicstart update - fix injection and documentation.

Signed-off-by: Tomas Langer <tomas.langer@oracle.com>
This commit is contained in:
Tomas Langer
2020-03-24 12:54:13 +01:00
committed by GitHub
parent 631c8a3ac6
commit e9774cdb9f
27 changed files with 735 additions and 205 deletions

View File

@@ -18,7 +18,6 @@ package io.helidon.config;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -455,12 +454,7 @@ class BuilderImpl implements Config.Builder {
hasEnvVarSource = true;
hasSystemPropertiesSource = true;
prioritizedSources.add(new HelidonSourceWithPriority(ConfigSources.systemProperties()
.pollingStrategy(PollingStrategies
.regular(Duration.ofSeconds(2))
.build())
.build(),
100));
prioritizedSources.add(new HelidonSourceWithPriority(ConfigSources.systemProperties().build(), 100));
prioritizedSources.add(new HelidonSourceWithPriority(ConfigSources.environmentVariables(), 100));
prioritizedSources.add(new HelidonSourceWithPriority(ConfigSources.classpath("application.yaml")
.optional(true)

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2020 Oracle and/or its affiliates.
*
* 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.objectmapping;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
/**
* A replacement for {@link java.lang.invoke.MethodHandle} that we cannot use for the time being, due to
* limitations of GraalVM native image.
*/
interface HelidonMethodHandle {
static HelidonMethodHandle create(Class<?> type, Constructor<?> constructor) {
return new ReflectionUtil.ConstructorMethodHandle(type, constructor);
}
static HelidonMethodHandle create(Class<?> type, Method method) {
if (Modifier.isStatic(method.getModifiers())) {
return new ReflectionUtil.StaticMethodHandle(method);
} else {
return new ReflectionUtil.InstanceMethodHandle(type, method);
}
}
static HelidonMethodHandle create(Class<?> type, Field field) {
return new ReflectionUtil.FieldMethodHandle(type, field);
}
/**
* Invoke the method or constructor with params.
* @param params parameters
* @return response
*/
Object invoke(List<Object> params);
/**
* Type of this handle, see {@link java.lang.invoke.MethodHandle#type()}.
*
* @return type of this handle
*/
MethodType type();
/**
* Invoke with varargs, delegates to {@link #invoke(java.util.List)}.
*
* @param params parameters
* @return result of the operation, or null (for setters)
*/
default Object invoke(Object... params) {
return invoke(Arrays.asList(params));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,13 +15,13 @@
*/
package io.helidon.config.objectmapping;
import java.lang.invoke.MethodHandle;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import javax.annotation.Priority;
import io.helidon.common.HelidonFeatures;
import io.helidon.config.Config;
import io.helidon.config.objectmapping.ObjectConfigMappers.BuilderConfigMapper;
import io.helidon.config.objectmapping.ObjectConfigMappers.ConfigMethodHandleConfigMapper;
@@ -50,6 +50,10 @@ public class ObjectConfigMapperProvider implements ConfigMapperProvider {
private static final String METHOD_PARSE = "parse";
private static final String METHOD_CREATE = "create";
static {
HelidonFeatures.register("Config", "Object Mapping");
}
@Override
public Map<Class<?>, Function<Config, ?>> mappers() {
return Map.of();
@@ -110,7 +114,7 @@ public class ObjectConfigMapperProvider implements ConfigMapperProvider {
private static <T> Optional<Function<Config, T>> findStaticStringMethodMapper(Class<T> type,
String methodName) {
Optional<MethodHandle> method = findStaticMethod(type,
Optional<HelidonMethodHandle> method = findStaticMethod(type,
methodName,
String.class);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,10 @@
*/
package io.helidon.config.objectmapping;
import java.lang.invoke.MethodHandle;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import io.helidon.common.HelidonFeatures;
import io.helidon.config.Config;
import io.helidon.config.ConfigMappingException;
import io.helidon.config.MissingValueException;
@@ -31,16 +30,12 @@ import io.helidon.config.objectmapping.ReflectionUtil.PropertyAccessor;
*/
class ObjectConfigMappers {
static {
HelidonFeatures.register("Config", "Object Mapping");
}
abstract static class MethodHandleConfigMapper<T, P> implements Function<Config, T> {
private final Class<T> type;
private final String methodName;
private final MethodHandle methodHandle;
private final HelidonMethodHandle methodHandle;
MethodHandleConfigMapper(Class<T> type, String methodName, MethodHandle methodHandle) {
MethodHandleConfigMapper(Class<T> type, String methodName, HelidonMethodHandle methodHandle) {
this.type = type;
this.methodName = methodName;
this.methodHandle = methodHandle;
@@ -51,7 +46,7 @@ class ObjectConfigMappers {
@Override
public T apply(Config config) throws ConfigMappingException, MissingValueException {
try {
return type.cast(methodHandle.invoke(invokeParameter(config)));
return type.cast(methodHandle.invoke(List.of(invokeParameter(config))));
} catch (ConfigMappingException ex) {
throw ex;
} catch (Throwable ex) {
@@ -62,7 +57,7 @@ class ObjectConfigMappers {
}
static class ConfigMethodHandleConfigMapper<T> extends MethodHandleConfigMapper<T, Config> {
ConfigMethodHandleConfigMapper(Class<T> type, String methodName, MethodHandle methodHandle) {
ConfigMethodHandleConfigMapper(Class<T> type, String methodName, HelidonMethodHandle methodHandle) {
super(type, methodName, methodHandle);
}
@@ -73,7 +68,7 @@ class ObjectConfigMappers {
}
static class StringMethodHandleConfigMapper<T> extends MethodHandleConfigMapper<T, String> {
StringMethodHandleConfigMapper(Class<T> type, String methodName, MethodHandle methodHandle) {
StringMethodHandleConfigMapper(Class<T> type, String methodName, HelidonMethodHandle methodHandle) {
super(type, methodName, methodHandle);
}
@@ -176,10 +171,10 @@ class ObjectConfigMappers {
static class GenericConfigMapper<T> implements Function<Config, T> {
private final Class<T> type;
private final MethodHandle constructorHandle;
private final HelidonMethodHandle constructorHandle;
private final Collection<PropertyAccessor<?>> propertyAccessors;
GenericConfigMapper(Class<T> type, MethodHandle constructorHandle) {
GenericConfigMapper(Class<T> type, HelidonMethodHandle constructorHandle) {
this.type = type;
this.constructorHandle = constructorHandle;
@@ -192,7 +187,7 @@ class ObjectConfigMappers {
@Override
public T apply(Config config) throws ConfigMappingException, MissingValueException {
try {
T instance = type.cast(constructorHandle.invoke());
T instance = type.cast(constructorHandle.invoke(List.of()));
for (PropertyAccessor<?> propertyAccessor : propertyAccessors) {
propertyAccessor.set(instance, config.get(propertyAccessor.name()));

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,8 +15,6 @@
*/
package io.helidon.config.objectmapping;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
@@ -76,11 +74,11 @@ final class ReflectionUtil {
return (Class<T>) REPLACED_TYPES.getOrDefault(type, type);
}
static Optional<MethodHandle> findStaticMethod(Class<?> type, String methodName, Class<?>... parameterTypes) {
static Optional<HelidonMethodHandle> findStaticMethod(Class<?> type, String methodName, Class<?>... parameterTypes) {
try {
Method method = type.getMethod(methodName, parameterTypes);
if (checkMethod(method, true, type, methodName, parameterTypes.length > 0)) {
return unreflect(method);
return Optional.of(HelidonMethodHandle.create(type, method));
} else {
LOGGER.log(Level.FINEST,
() -> "Class " + type.getName() + " method '" + methodName
@@ -95,11 +93,11 @@ final class ReflectionUtil {
return Optional.empty();
}
static Optional<MethodHandle> findConstructor(Class<?> type, Class<?>... parameterTypes) {
static Optional<HelidonMethodHandle> findConstructor(Class<?> type, Class<?>... parameterTypes) {
try {
Constructor<?> constructor = type.getConstructor(parameterTypes);
if (checkConstructor(constructor, parameterTypes.length > 0)) {
return Optional.of(MethodHandles.publicLookup().unreflectConstructor(constructor));
return Optional.of(HelidonMethodHandle.create(type, constructor));
} else {
LOGGER.log(Level.FINEST,
() -> "Class " + type.getName() + " constructor with parameters "
@@ -111,11 +109,6 @@ final class ReflectionUtil {
ex,
() -> "Class " + type.getName() + " does not have a constructor with parameters "
+ Arrays.toString(parameterTypes) + ".");
} catch (IllegalAccessException ex) {
LOGGER.log(Level.FINER,
ex,
() -> "Access checking fails on " + type.getName()
+ " class, constructor with parameters " + Arrays.toString(parameterTypes) + ".");
}
return Optional.empty();
}
@@ -125,14 +118,16 @@ final class ReflectionUtil {
* e.g. Type t = Type.builder().build();
*/
static <T> Optional<BuilderAccessor<T>> findBuilderMethod(Class<T> type) {
return findMethod(type, METHOD_BUILDER, true, null).flatMap(
builderMethod -> unreflect(builderMethod).flatMap(
builderHandler -> findBuilderBuildHandler(type, builderMethod.getReturnType()).map(
buildHandler ->
new BuilderAccessor<>(builderMethod.getReturnType(),
builderHandler,
type,
buildHandler))));
return findMethod(type, METHOD_BUILDER, true, null)
.flatMap(builderMethod -> {
HelidonMethodHandle builderHandler = HelidonMethodHandle.create(type, builderMethod);
return findBuilderBuildHandler(type, builderMethod.getReturnType()).map(
buildHandler ->
new BuilderAccessor<>(builderMethod.getReturnType(),
builderHandler,
type,
buildHandler));
});
}
/**
@@ -203,9 +198,9 @@ final class ReflectionUtil {
}
}
static <T> Optional<MethodHandle> findBuilderBuildHandler(Class<T> type, Class<?> builderType) {
static <T> Optional<HelidonMethodHandle> findBuilderBuildHandler(Class<T> type, Class<?> builderType) {
return findMethod(builderType, METHOD_BUILD, false, type)
.flatMap(ReflectionUtil::unreflect);
.map(it -> HelidonMethodHandle.create(type, it));
}
/**
@@ -249,18 +244,6 @@ final class ReflectionUtil {
return Optional.empty();
}
private static Optional<MethodHandle> unreflect(Method method) {
try {
return Optional.of(MethodHandles.publicLookup().unreflect(method));
} catch (IllegalAccessException ex) {
LOGGER.log(Level.FINER,
ex,
() -> "Access checking fails on " + method.getDeclaringClass() + " class, method '" + method.getName()
+ "' with parameters " + Arrays.asList(method.getParameters()) + ".");
}
return Optional.empty();
}
/**
* The class covers work with factory method.
*
@@ -268,11 +251,11 @@ final class ReflectionUtil {
*/
static class FactoryAccessor<T> {
private final Class<T> type;
private final MethodHandle handle;
private final HelidonMethodHandle handle;
private final LinkedHashMap<String, PropertyWrapper<?>> parameterValueProviders;
FactoryAccessor(Class<T> type,
MethodHandle handle,
HelidonMethodHandle handle,
Parameter[] parameters) {
this.type = type;
this.handle = handle;
@@ -284,7 +267,7 @@ final class ReflectionUtil {
List<Object> args = createArguments(configNode);
try {
Object obj = handle.invokeWithArguments(args);
Object obj = handle.invoke(args);
return type.cast(obj);
} catch (ConfigException ex) {
throw ex;
@@ -350,15 +333,15 @@ final class ReflectionUtil {
*/
static class BuilderAccessor<T> {
private final Class<?> builderType;
private final MethodHandle builderHandler;
private final HelidonMethodHandle builderHandler;
private final Class<T> buildType;
private final MethodHandle buildHandler;
private final HelidonMethodHandle buildHandler;
private final Collection<PropertyAccessor<?>> builderAccessors;
BuilderAccessor(Class<?> builderType,
MethodHandle builderHandler,
HelidonMethodHandle builderHandler,
Class<T> buildType,
MethodHandle buildHandler) {
HelidonMethodHandle buildHandler) {
this.builderType = builderType;
this.builderHandler = builderHandler;
this.buildType = buildType;
@@ -369,13 +352,13 @@ final class ReflectionUtil {
public T create(Config config) {
try {
Object builder = builderType.cast(builderHandler.invoke());
Object builder = builderType.cast(builderHandler.invoke(List.of()));
for (PropertyAccessor<?> builderAccessor : builderAccessors) {
builderAccessor.set(builder, config.get(builderAccessor.name()));
}
return buildType.cast(buildHandler.invoke(builder));
return buildType.cast(buildHandler.invoke(List.of(builder)));
} catch (ConfigMappingException ex) {
throw ex;
} catch (Throwable ex) {
@@ -393,14 +376,14 @@ final class ReflectionUtil {
*/
static class PropertyAccessor<T> {
private final String name;
private final MethodHandle handle;
private final HelidonMethodHandle handle;
private final boolean hasValueAnnotation;
private final PropertyWrapper<T> propertyWrapper;
PropertyAccessor(String name, Class<T> propertyType,
Class<?> configAsType,
boolean list,
MethodHandle handle,
HelidonMethodHandle handle,
Value value) {
this.name = name;
this.handle = handle;
@@ -432,7 +415,7 @@ final class ReflectionUtil {
}
}
MethodHandle handle() {
HelidonMethodHandle handle() {
return handle;
}
@@ -555,30 +538,26 @@ final class ReflectionUtil {
static <T> PropertyAccessor<T> createPropertyAccessor(Class<T> type,
String name,
Method method) {
try {
final Class<T> propertyType = (Class<T>) method.getParameterTypes()[0];
Class<?> configAsType = propertyType;
final Class<T> propertyType = (Class<T>) method.getParameterTypes()[0];
Class<?> configAsType = propertyType;
boolean list = List.class.isAssignableFrom(configAsType);
if (list) {
Type genType = method.getGenericParameterTypes()[0];
if (genType instanceof ParameterizedType) {
configAsType = (Class<?>) ((ParameterizedType) genType).getActualTypeArguments()[0];
} else {
throw new ConfigException("Unable to find generic type of List on setter parameter: " + method);
}
boolean list = List.class.isAssignableFrom(configAsType);
if (list) {
Type genType = method.getGenericParameterTypes()[0];
if (genType instanceof ParameterizedType) {
configAsType = (Class<?>) ((ParameterizedType) genType).getActualTypeArguments()[0];
} else {
throw new ConfigException("Unable to find generic type of List on setter parameter: " + method);
}
MethodHandle handle = MethodHandles.publicLookup()
.findVirtual(type,
method.getName(),
MethodType.methodType(method.getReturnType(), method.getParameterTypes()));
return new PropertyAccessor<>(name, propertyType, configAsType, list, handle,
method.getAnnotation(Value.class));
} catch (NoSuchMethodException | IllegalAccessException | ClassCastException ex) {
throw new ConfigException("Cannot access setter: " + method, ex);
}
return new PropertyAccessor<>(name,
propertyType,
configAsType,
list,
HelidonMethodHandle.create(type, method),
method.getAnnotation(Value.class));
}
@SuppressWarnings("unchecked")
@@ -599,12 +578,13 @@ final class ReflectionUtil {
}
}
MethodHandle handle = MethodHandles.publicLookup()
.findSetter(type, field.getName(), field.getType());
return new PropertyAccessor<>(name, propertyType, configAsType, list, handle,
return new PropertyAccessor<>(name,
propertyType,
configAsType,
list,
HelidonMethodHandle.create(type, field),
field.getAnnotation(Value.class));
} catch (NoSuchFieldException | IllegalAccessException | ClassCastException ex) {
} catch (ClassCastException ex) {
throw new ConfigException("Cannot access field: " + field, ex);
}
}
@@ -728,4 +708,116 @@ final class ReflectionUtil {
this.defaultSupplier = defaultSupplier;
}
}
static class FieldMethodHandle implements HelidonMethodHandle {
private final Field field;
private final Class<?> type;
FieldMethodHandle(Class<?> type, Field field) {
this.type = type;
this.field = field;
}
@Override
public Object invoke(List<Object> params) {
try {
field.set(params.get(0), params.get(1));
return null;
} catch (IllegalAccessException e) {
throw new ConfigException("Field " + field + " is not accessible. Cannot set value", e);
}
}
@Override
public MethodType type() {
return MethodType.methodType(Void.class, type, field.getType());
}
}
static class StaticMethodHandle implements HelidonMethodHandle {
private final Method method;
StaticMethodHandle(Method method) {
this.method = method;
}
@Override
public Object invoke(List<Object> params) {
try {
return method.invoke(null, params.toArray(new Object[0]));
} catch (IllegalAccessException e) {
throw new ConfigException("Method " + method + " is not accessible. Cannot invoke", e);
} catch (InvocationTargetException e) {
throw new ConfigException("Failed to invoke method " + method, e);
}
}
@Override
public MethodType type() {
return MethodType.methodType(method.getReturnType(), method.getParameterTypes());
}
}
static class ConstructorMethodHandle implements HelidonMethodHandle {
private final Class<?> type;
private final Constructor<?> constructor;
ConstructorMethodHandle(Class<?> type, Constructor<?> constructor) {
this.type = type;
this.constructor = constructor;
}
@Override
public Object invoke(List<Object> params) {
try {
return constructor.newInstance(params.toArray(new Object[0]));
} catch (IllegalAccessException e) {
throw new ConfigException("Constructor " + constructor + " is not accessible. Cannot invoke", e);
} catch (InvocationTargetException e) {
throw new ConfigException("Failed to invoke constructor " + constructor, e);
} catch (InstantiationException e) {
throw new ConfigException("Failed to instantiate class using constructor " + constructor, e);
} catch (IllegalArgumentException e) {
throw new ConfigException("Parameters mismatch for constructor " + constructor, e);
}
}
@Override
public MethodType type() {
return MethodType.methodType(type);
}
}
public static class InstanceMethodHandle implements HelidonMethodHandle {
private Class<?> type;
private final Method method;
InstanceMethodHandle(Class<?> type, Method method) {
this.type = type;
this.method = method;
}
@Override
public Object invoke(List<Object> params) {
try {
// first is instance
Object instance = params.get(0);
List<Object> mutableParams = new ArrayList<>(params);
mutableParams.remove(0);
return method.invoke(params.get(0), mutableParams.toArray(new Object[0]));
} catch (IllegalAccessException e) {
throw new ConfigException("Method " + method + " is not accessible. Cannot invoke", e);
} catch (InvocationTargetException e) {
throw new ConfigException("Failed to invoke method " + method, e);
}
}
@Override
public MethodType type() {
List<Class<?>> paramTypes = new ArrayList<>();
paramTypes.add(type);
paramTypes.addAll(Arrays.asList(method.getParameterTypes()));
return MethodType.methodType(method.getReturnType(), paramTypes);
}
}
}