Skip to content

Commit

Permalink
Add Configuration.Builder for manual construction
Browse files Browse the repository at this point in the history
- Adds Configuration.Builder for manually creating a Configuration
- Refactor extract the internal CoreConfiguration.postLoad() logic
  • Loading branch information
rob-bygrave committed Sep 28, 2023
1 parent 65cac70 commit e91594a
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 8 deletions.
50 changes: 50 additions & 0 deletions avaje-config/src/main/java/io/avaje/config/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -632,4 +632,54 @@ interface SetValue {
*/
<T> Set<T> ofType(String key, Function<String, T> mappingFunction);
}

/**
* Return a Builder for Configuration that is loaded manually (not via the normal resource loading).
*/
static Builder builder() {
return new CoreConfigurationBuilder();
}

/**
* Build Configuration manually explicitly loading all the configuration as key value pairs.
* <p>
* Building configuration this way does NOT automatically load resources like application.properties
* and also does NOT load ConfigurationSource. ALL configuration is explicitly loaded via calls
* to {@link Builder#put(String, String)}, {@link Builder#putAll(Map)}.
*/
interface Builder {

/**
* Put an entry into the configuration.
*/
Builder put(String key, String value);

/**
* Put entries into the configuration.
*/
Builder putAll(Map<String, ?> sourceMap);

/**
* Put entries into the configuration from properties.
*/
Builder putAll(Properties source);

/**
* Optionally set the event runner to use . If not specified a foreground runner will be used.
*/
Builder eventRunner(ModificationEventRunner eventRunner);

/**
* Optionally set the log to use. If not specified then a logger using System.Logger will be used.
*/
Builder log(ConfigurationLog log);

/**
* Build and return the Configuration.
* <p>
* Performs evaluation of property values that contain expressions (e.g. {@code ${user.home}})
* and returns the configuration.
*/
Configuration build();
}
}
23 changes: 17 additions & 6 deletions avaje-config/src/main/java/io/avaje/config/CoreConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,24 @@ static Configuration initialise() {
log.preInitialisation();
final var resourceLoader = ServiceLoader.load(ResourceLoader.class).findFirst().orElseGet(DefaultResourceLoader::new);
final var loader = new InitialLoader(log, resourceLoader);
final CoreConfiguration configuration = new CoreConfiguration(runner, log, loader.load());
configuration.loadSources(loader.loadedFrom());
loader.initWatcher(configuration);
configuration.initSystemProperties();
configuration.logMessage(loader);
return new CoreConfiguration(runner, log, loader.load()).postLoad(loader);
}

CoreConfiguration postLoad() {
return postLoad(null);
}

private CoreConfiguration postLoad(@Nullable InitialLoader loader) {
if (loader != null) {
loadSources(loader.loadedFrom());
loader.initWatcher(this);
}
initSystemProperties();
if (loader != null) {
logMessage(loader);
}
log.postInitialisation();
return configuration;
return this;
}

ConfigurationLog log() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package io.avaje.config;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;

import static java.util.Objects.requireNonNull;

final class CoreConfigurationBuilder implements Configuration.Builder {

private final Map<String, String> sourceMap = new LinkedHashMap<>();
private ModificationEventRunner eventRunner;
private ConfigurationLog configurationLog;

@Override
public Configuration.Builder eventRunner(ModificationEventRunner eventRunner) {
this.eventRunner = eventRunner;
return this;
}

@Override
public Configuration.Builder log(ConfigurationLog configurationLog) {
this.configurationLog = configurationLog;
return this;
}

@Override
public Configuration.Builder put(String key, String value) {
requireNonNull(key);
requireNonNull(value);
sourceMap.put(key, value);
return this;
}

@Override
public Configuration.Builder putAll(Map<String, ?> source) {
requireNonNull(source);
source.forEach((key, value) -> {
if (key != null && value != null) {
put(key, value.toString());
}
});
return this;
}

@Override
public Configuration.Builder putAll(Properties source) {
requireNonNull(source);
source.forEach((key, value) -> {
if (key != null && value != null) {
put(key.toString(), value.toString());
}
});
return this;
}

@Override
public Configuration build() {
return new CoreConfiguration(initRunner(), initLog(), initEntries())
.postLoad();
}

private CoreEntry.CoreMap initEntries() {
final var entries = CoreEntry.newMap();
sourceMap.forEach((key, value) -> entries.put(key, value, "initial"));
return CoreExpressionEval.evalFor(entries);
}

private ConfigurationLog initLog() {
if (configurationLog != null) {
return configurationLog;
} else {
return ServiceLoader.load(ConfigurationLog.class)
.findFirst()
.orElseGet(DefaultConfigurationLog::new);
}
}

private ModificationEventRunner initRunner() {
if (eventRunner != null) {
return eventRunner;
} else {
return ServiceLoader.load(ModificationEventRunner.class)
.findFirst()
.orElseGet(CoreConfiguration.ForegroundEventRunner::new);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class CoreConfigurationTest {
private final CoreConfiguration data = createSample();

private CoreMap basicProperties() {
return CoreEntry.newMap(properties(), "test");
}

private static Properties properties() {
Properties properties = new Properties();
properties.setProperty("a", "1");
properties.setProperty("foo.bar", "42");
Expand All @@ -27,8 +31,8 @@ private CoreMap basicProperties() {
properties.setProperty("someValues", "13,42,55");
properties.setProperty("1someValues", "13,42,55");
properties.setProperty("myEnum", "TWO");

return CoreEntry.newMap(properties, "test");
properties.setProperty("myHome", "my/${user.home}/home");
return properties;
}

private CoreConfiguration createSample() {
Expand Down Expand Up @@ -122,6 +126,24 @@ void toEnvKey() {
assertThat(CoreConfiguration.toEnvKey("BAR")).isEqualTo("BAR");
}

@Test
void builder() {
var conf = Configuration.builder()
.putAll(properties())
.putAll(Map.of("myExtraMap", "foo", "myExtraMap.b", "bar"))
.put("myExtraOne", "baz")
.build();

assertEquals(conf.get("a", "something"), "1");
assertEquals(conf.get("doesNotExist", "something"), "something");
assertEquals(conf.get("myExtraMap"), "foo");
assertEquals(conf.get("myExtraMap.b"), "bar");
assertEquals(conf.get("myExtraOne"), "baz");

String userHome = System.getProperty("user.home");
assertEquals(conf.get("myHome"), "my/" + userHome + "/home");
}

@Test
void get() {
assertEquals(data.get("a", "something"), "1");
Expand Down Expand Up @@ -255,6 +277,7 @@ void onChangePutAll() {
@Test
void onChangeNew() {
// we will remove this entry
System.clearProperty("foo.bar");
assertThat(data.getOptional("foo.bar")).contains("42");

final List<ModificationEvent> capturedEvents = new ArrayList<>();
Expand Down

0 comments on commit e91594a

Please sign in to comment.