Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion config-magic/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- Don't run JUnit tests -->
<groups />
<groups/>
</configuration>
</plugin>
</plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2020-2025 Equinix, Inc
* Copyright 2014-2025 The Billing Project, LLC
*
* The Billing Project licenses this file to you 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 org.skife.config;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Augmented version of {@link ConfigurationObjectFactory} that collects resolved config properties
* at runtime and registers them in {@link RuntimeConfigRegistry}.
*/
public class AugmentedConfigurationObjectFactory extends ConfigurationObjectFactory {

private static final Logger log = LoggerFactory.getLogger(AugmentedConfigurationObjectFactory.class);

public AugmentedConfigurationObjectFactory(final Properties props) {
super(new SimplePropertyConfigSource(props));
}

public AugmentedConfigurationObjectFactory(final ConfigSource configSource) {
super(configSource);
}

@Override
public <T> T build(final Class<T> configClass) {
final T instance = super.build(configClass);

collectConfigValues(configClass, instance);

return instance;
}

private <T> void collectConfigValues(final Class<T> configClass, final T instance) {
final String configSource = configClass.getSimpleName();

for (final Method method : configClass.getMethods()) {
final Config configAnnotation = method.getAnnotation(Config.class);

if (configAnnotation != null && method.getParameterCount() == 0) {
try {
final Object value = method.invoke(instance);
final String[] keys = configAnnotation.value();
Arrays.stream(keys)
.forEach(key -> RuntimeConfigRegistry.put(key, value));

Arrays.stream(keys)
.forEach(key -> RuntimeConfigRegistry.putWithSource(configSource, key, value));
} catch (final IllegalAccessException | InvocationTargetException e) {
log.warn("Failed to resolve config method: {}", method.getName(), e);
}
} else if (configAnnotation != null) {
log.debug("Skipping config method {} due to parameters", method.getName());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/**
* If a configuration bean is created with mapped replacement values via
* {@link ConfigurationObjectFactory#buildWithReplacements(Class, java.util.Map)},
* {@link AugmentedConfigurationObjectFactory#buildWithReplacements(Class, java.util.Map)},
* this annotation designates a method which should present the provided Map.
* The map may not be changed and is not necessarily the same instance as the original.
* If a key is provided, the return is instead the value for that key.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -44,6 +47,8 @@ public class ConfigurationObjectFactory {

private static final Logger logger = LoggerFactory.getLogger(ConfigurationObjectFactory.class);

//private static final Map<String, String> RUNTIME_CONFIGS = new ConcurrentHashMap<>();

private final ConfigSource config;
private final Bully bully;

Expand All @@ -66,6 +71,11 @@ public <T> T buildWithReplacements(final Class<T> configClass, final Map<String,

public <T> T build(final Class<T> configClass) {
return internalBuild(configClass, null);

/*logger.info("Calling collectConfigValues");
collectConfigValues(configClass, t);*/


}

private <T> T internalBuild(final Class<T> configClass, @Nullable final Map<String, String> mappedReplacements) {
Expand Down Expand Up @@ -112,6 +122,33 @@ private <T> T internalBuild(final Class<T> configClass, @Nullable final Map<Stri
}
}

/*private <T> void collectConfigValues(final Class<T> configClass, final T instance) {
//final String configSource = configClass.getSimpleName();

for (final Method method : configClass.getMethods()) {
final Config configAnnotation = method.getAnnotation(Config.class);

if (configAnnotation != null && method.getParameterCount() == 0) {
try {
final Object value = method.invoke(instance);
final String[] keys = configAnnotation.value();
Arrays.stream(keys)
.forEach(key -> RuntimeConfigRegistry.put(key, value == null ? "" : value.toString()));

*//*Arrays.stream(keys)
.forEach(key -> RuntimeConfigRegistry.putWithSource(configSource, key, value));*//*
} catch (final IllegalAccessException | InvocationTargetException e) {
logger.warn("Failed to resolve config method: {}", method.getName(), e);
}
} else if (configAnnotation != null) {
logger.debug("Skipping config method {} due to parameters", method.getName());
}
}

logger.info("RUNTIME_CONFIGS updated successfully");
//RuntimeConfigRegistry.getAll().forEach((s, s2) -> System.out.println(s + ": " + s2));
}*/

private <T> Builder<T> buildSimple(final Builder<T> bbBuilder,
final Method method,
final Config annotation,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2020-2025 Equinix, Inc
* Copyright 2014-2025 The Billing Project, LLC
*
* The Billing Project licenses this file to you 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 org.skife.config;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
* Registry to capture and expose runtime configuration values.
*/
public class RuntimeConfigRegistry {

private static final Map<String, String> RUNTIME_CONFIGS = new ConcurrentHashMap<>();

private static final Map<String, Map<String, String>> RUNTIME_CONFIGS_BY_SOURCE = new ConcurrentHashMap<>();

public static void put(final String key, final Object value) {
RUNTIME_CONFIGS.put(key, value == null ? "" : value.toString());
}

public static void putWithSource(final String configSource, final String key, final Object value) {
RUNTIME_CONFIGS_BY_SOURCE
.computeIfAbsent(configSource, k -> new ConcurrentHashMap<>())
.put(key, value == null ? "" : value.toString());
}

public static void putAllWithSource(final String configSource, final Map<String, ?> values) {
if (values == null || values.isEmpty()) {
return;
}

RUNTIME_CONFIGS_BY_SOURCE
.computeIfAbsent(configSource, k -> new ConcurrentHashMap<>())
.putAll(values.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey,
e -> e.getValue() == null ? "" : e.getValue().toString())));
}

public static String get(final String key) {
return RUNTIME_CONFIGS.getOrDefault(key, "");
}

public static Map<String, String> getBySource(final String source) {
return Collections.unmodifiableMap(RUNTIME_CONFIGS_BY_SOURCE.getOrDefault(source, Map.of()));
}

public static Map<String, String> getAll() {
return Collections.unmodifiableMap(RUNTIME_CONFIGS);
}

public static Map<String, Map<String, String>> getAllBySource() {
return Collections.unmodifiableMap(RUNTIME_CONFIGS_BY_SOURCE);
}

public static void clear() {
RUNTIME_CONFIGS.clear();
RUNTIME_CONFIGS_BY_SOURCE.clear();
}
}
4 changes: 2 additions & 2 deletions config-magic/src/test/java/org/skife/config/TestArrays.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@
@Category(ConfigMagicTests.class)
public class TestArrays {

private ConfigurationObjectFactory cof;
private AugmentedConfigurationObjectFactory cof;

@Before
public void setUp() {
cof = new ConfigurationObjectFactory(new Properties());
cof = new AugmentedConfigurationObjectFactory(new Properties());
}

@After
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
@Category(ConfigMagicTests.class)
public class TestBadConfig {

ConfigurationObjectFactory c = null;
AugmentedConfigurationObjectFactory c = null;

@Before
public void setUp() {
this.c = new ConfigurationObjectFactory(new Properties());
this.c = new AugmentedConfigurationObjectFactory(new Properties());
}

@After
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,23 @@ public class TestCaseInsensitiveEnumCoercible {

@Test
public void testHappyPath() throws Exception {
final ConfigurationObjectFactory cof = new ConfigurationObjectFactory(Props.of("creamer", "half_and_half"));
final AugmentedConfigurationObjectFactory cof = new AugmentedConfigurationObjectFactory(Props.of("creamer", "half_and_half"));

final Coffee coffee = cof.build(Coffee.class);
assertThat(coffee.getCreamer(), equalTo(Creamer.HALF_AND_HALF));
}

@Test(expected = IllegalStateException.class)
public void testNoMatch() throws Exception {
final ConfigurationObjectFactory cof = new ConfigurationObjectFactory(Props.of("creamer", "goat_milk"));
final AugmentedConfigurationObjectFactory cof = new AugmentedConfigurationObjectFactory(Props.of("creamer", "goat_milk"));

final Coffee coffee = cof.build(Coffee.class);
fail("should have raised an illegal state exception");
}

@Test(expected = IllegalArgumentException.class)
public void testExactMatch() throws Exception {
final ConfigurationObjectFactory cof = new ConfigurationObjectFactory(Props.of("creamer", "whole_milk"));
final ConfigurationObjectFactory cof = new AugmentedConfigurationObjectFactory(Props.of("creamer", "whole_milk"));
cof.addCoercible(new ExactMatchEnumCoercible());

final Coffee coffee = cof.build(Coffee.class);
Expand Down
Loading