mirror of
https://github.com/jlengrand/helidon.git
synced 2026-03-10 08:21:17 +00:00
Support for mutable config sources to remove regression (even though … (#1667)
* Support for mutable MP config sources to remove regression (even though the original behavior was not intended). * Fixed unit test to be independent on ordering.
This commit is contained in:
@@ -16,13 +16,18 @@
|
||||
|
||||
package io.helidon.config;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import io.helidon.config.spi.ConfigNode;
|
||||
|
||||
import org.eclipse.microprofile.config.spi.ConfigSource;
|
||||
|
||||
import static io.helidon.config.AbstractConfigImpl.LOGGER;
|
||||
|
||||
class ConfigSourceMpRuntimeImpl extends ConfigSourceRuntimeBase {
|
||||
private final ConfigSource source;
|
||||
|
||||
@@ -38,7 +43,28 @@ class ConfigSourceMpRuntimeImpl extends ConfigSourceRuntimeBase {
|
||||
|
||||
@Override
|
||||
public void onChange(BiConsumer<String, ConfigNode> change) {
|
||||
// this is a no-op - MP config sources do not support changes
|
||||
try {
|
||||
// this is not a documented feature
|
||||
// it is to enable MP config sources to be "mutable" in Helidon
|
||||
// this requires some design decisions (and clarification of the MP Config Specification), as this
|
||||
// is open to different interpretations for now
|
||||
Method method = source.getClass().getMethod("registerChangeListener", BiConsumer.class);
|
||||
BiConsumer<String, String> mpListener = (key, value) -> change.accept(key, ConfigNode.ValueNode.create(value));
|
||||
|
||||
method.invoke(source, mpListener);
|
||||
} catch (NoSuchMethodException e) {
|
||||
LOGGER.finest("No registerChangeListener(BiConsumer) method found on " + source.getClass() + ", change"
|
||||
+ " support not enabled for this config source (" + source.getName() + ")");
|
||||
} catch (IllegalAccessException e) {
|
||||
LOGGER.log(Level.WARNING, "Cannot invoke registerChangeListener(BiConsumer) method on " + source.getClass() + ", "
|
||||
+ "change support not enabled for this config source ("
|
||||
+ source.getName() + ")", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
LOGGER.log(Level.WARNING, "Invocation of registerChangeListener(BiConsumer) method on " + source.getClass()
|
||||
+ " failed with an exception, "
|
||||
+ "change support not enabled for this config source ("
|
||||
+ source.getName() + ")", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,4 +92,10 @@ class ConfigSourceMpRuntimeImpl extends ConfigSourceRuntimeBase {
|
||||
public String description() {
|
||||
return source.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean changesSupported() {
|
||||
// supported through a known method signature
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,25 @@ package io.helidon.microprofile.config;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.enterprise.context.Dependent;
|
||||
import javax.enterprise.inject.se.SeContainer;
|
||||
import javax.enterprise.inject.se.SeContainerInitializer;
|
||||
import javax.enterprise.inject.spi.CDI;
|
||||
import javax.enterprise.util.AnnotationLiteral;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Qualifier;
|
||||
|
||||
import io.helidon.config.test.infra.RestoreSystemPropertiesExt;
|
||||
import io.helidon.microprofile.cdi.HelidonContainer;
|
||||
import io.helidon.microprofile.config.Converters.Ctor;
|
||||
import io.helidon.microprofile.config.Converters.Of;
|
||||
import io.helidon.microprofile.config.Converters.Parse;
|
||||
import io.helidon.microprofile.config.Converters.ValueOf;
|
||||
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
|
||||
import org.eclipse.microprofile.config.spi.ConfigSource;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -49,27 +53,31 @@ import static org.junit.jupiter.api.Assertions.assertAll;
|
||||
*/
|
||||
@ExtendWith(RestoreSystemPropertiesExt.class)
|
||||
class MpConfigInjectionTest {
|
||||
private static HelidonContainer container;
|
||||
private static SeContainer container;
|
||||
|
||||
@BeforeAll
|
||||
static void initClass() {
|
||||
|
||||
// System properties for injection
|
||||
// Removed use of system properties, as those stay around after test is finished
|
||||
ConfigProviderResolver configProvider = ConfigProviderResolver.instance();
|
||||
|
||||
configProvider.registerConfig(configProvider.getBuilder()
|
||||
.addDefaultSources()
|
||||
.withSources(new TestSource())
|
||||
.build(),
|
||||
Thread.currentThread().getContextClassLoader());
|
||||
|
||||
System.setProperty("inject.of", "of");
|
||||
System.setProperty("inject.valueOf", "valueOf");
|
||||
System.setProperty("inject.parse", "parse");
|
||||
System.setProperty("inject.ctor", "ctor");
|
||||
|
||||
// CDI container
|
||||
container = HelidonContainer.instance();
|
||||
container.start();
|
||||
container = SeContainerInitializer.newInstance()
|
||||
.addBeanClasses(Bean.class, SubBean.class)
|
||||
.initialize();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void destroyClass() {
|
||||
if (null != container) {
|
||||
container.shutdown();
|
||||
container.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,4 +139,28 @@ class MpConfigInjectionTest {
|
||||
@Specific
|
||||
public static class SubBean extends Bean {
|
||||
}
|
||||
|
||||
private static class TestSource implements ConfigSource {
|
||||
private final Map<String, String> properties = Map.of(
|
||||
"inject.of", "of",
|
||||
"inject.valueOf", "valueOf",
|
||||
"inject.parse", "parse",
|
||||
"inject.ctor", "ctor"
|
||||
);
|
||||
|
||||
@Override
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(String propertyName) {
|
||||
return properties.get(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getClass().getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.microprofile.config;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import javax.enterprise.context.Dependent;
|
||||
import javax.enterprise.inject.se.SeContainer;
|
||||
import javax.enterprise.inject.se.SeContainerInitializer;
|
||||
import javax.enterprise.inject.spi.CDI;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
|
||||
import org.eclipse.microprofile.config.spi.ConfigSource;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
public class MutableMpTest {
|
||||
private static SeContainer container;
|
||||
private static MutableSource source;
|
||||
|
||||
@BeforeAll
|
||||
static void initClass() {
|
||||
source = new MutableSource("initial");
|
||||
ConfigProviderResolver configProvider = ConfigProviderResolver.instance();
|
||||
|
||||
configProvider.registerConfig(configProvider.getBuilder()
|
||||
.addDefaultSources()
|
||||
.withSources(source)
|
||||
.build(),
|
||||
Thread.currentThread().getContextClassLoader());
|
||||
|
||||
// CDI container
|
||||
container = SeContainerInitializer.newInstance()
|
||||
.addBeanClasses(Bean.class)
|
||||
.initialize();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void destroyClass() {
|
||||
if (null != container) {
|
||||
container.close();
|
||||
}
|
||||
source = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMutable() {
|
||||
Bean bean = CDI.current().select(Bean.class).get();
|
||||
|
||||
assertThat(bean.value, is("initial"));
|
||||
|
||||
source.setValue("updated");
|
||||
|
||||
bean = CDI.current().select(Bean.class).get();
|
||||
|
||||
assertThat(bean.value, is("updated"));
|
||||
}
|
||||
|
||||
@Dependent
|
||||
public static class Bean {
|
||||
@Inject
|
||||
@ConfigProperty(name = "value")
|
||||
public String value;
|
||||
}
|
||||
|
||||
// class must be public so helidon can see it and invoke methods through reflection
|
||||
public static class MutableSource implements ConfigSource {
|
||||
private AtomicReference<String> value = new AtomicReference<>();
|
||||
private BiConsumer<String, String> listener;
|
||||
|
||||
public MutableSource(String initial) {
|
||||
value.set(initial);
|
||||
}
|
||||
|
||||
public void registerChangeListener(BiConsumer<String, String> listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getProperties() {
|
||||
return Map.of("value", value.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(String propertyName) {
|
||||
if ("value".equals(propertyName)) {
|
||||
return value.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "mutable-unit-test";
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value.set(value);
|
||||
listener.accept("value", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
|
||||
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
|
||||
http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
|
||||
version="2.0"
|
||||
bean-discovery-mode="annotated">
|
||||
</beans>
|
||||
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
# needed to run unit tests independently (without beans.xml)
|
||||
mp.initializer.allow=true
|
||||
mp.initializer.warn=false
|
||||
Reference in New Issue
Block a user