-
Notifications
You must be signed in to change notification settings - Fork 132
Description
Mapped configurations are very good in terms of brevity, clean API, and usability. However, they are not usable unless you're using SmallRye Config to read a configuration and create them indirectly in that way.
As a configuration user, I would like to have the ability to use mapped configurations to configure my application using my application's API, but have the usage of the actual configuration file be optional. This way I can configure my application using a file, or programmatically, all using the same API.
To allow this, we should have an API which allows configuration interfaces to be instantiated directly using a builder API. The API could look something like this:
@ConfigMapping(prefix = "hello-world")
public interface HelloWorldConfig {
@WithDefault("hello")
String helloMessage();
@WithDefault("5")
int numberOfTimes();
Optional<Path> outputPath();
} public static void main(String[] args) {
ConfigMappingBuilderFactory factory = ...;
// manually create a config
ConfigMappingBuilder<HelloWorldConfig> configBuilder = factory.builder(HelloWorldConfig.class);
configBuilder.set(HelloWorldConfig::helloMessage, "good morning!");
// convenience for optional properties
configBuilder.setOptional(HelloWorldConfig::outputPath, Path.of("/tmp/good-morning.txt"));
// primitives without boxing
configBuilder.set(HelloWorldConfig::numberOfTimes, 10);
// oops, I made a mistake, let's revert to default
configBuilder.unset(HelloWorldConfig::numberOfTimes);
HelloWorldConfig config = configBuilder.build();
// now do something with config
}An API with this style is easier to use than source code generation based approaches. The builder factory would track generated classes for each configuration interface, which ideally could be reused by the existing config mapping implementation (though that implementation would not use the builder API itself per se). When running on Java 16 and later, these implementation classes should likely be implemented as records.
The generated record would be able to enforce constraints like non-nullity and validation in its canonical constructor (ideally using smallrye-common-constraint to validate nullity and smallrye-config-validator for the user validation rules as expected), ensuring that the consuming code will always receive a correct object. Optional properties would default to Optional.empty() and properties with explicit defaults would have those defaults set if no value is given.
Note that by waiting for the baseline JDK to move from 11 to 17, in addition to using records on the back end, we could possibly use my backport of the JDK classfile API to make class generation easiest.