ratpack.config.ConfigDataBuilder Maven / Gradle / Ivy
/*
* Copyright 2015 the original author or authors.
*
* 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 ratpack.config;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteSource;
import ratpack.config.internal.source.ArgsConfigSource;
import ratpack.func.Action;
import ratpack.func.Function;
import java.net.URL;
import java.nio.file.Path;
import java.util.Map;
import java.util.Properties;
/**
* Configures how configuration data will be loaded and bound to objects.
*
* Multiple data sources can be specified.
* All specified data sources will be merged together to form the final configuration data.
* If a given value exists in multiple data sources, the value from the last specified source will be used.
*
* By default, if loading a data source fails, the exception will be thrown.
* If desired, this behavior can be adjusted using {@link #onError(ratpack.func.Action)}.
* For example:
*
*
{@code
* import java.nio.file.Files;
* import java.nio.file.Path;
* import ratpack.func.Action;
* import ratpack.server.RatpackServer;
* import ratpack.server.ServerConfig;
* import ratpack.test.ServerBackedApplicationUnderTest;
* import ratpack.test.http.TestHttpClient;
*
* import static org.junit.Assert.*;
*
* public class Example {
* public static void main(String[] args) throws Exception {
* Path jsonFile = Files.createTempFile("optionalConfig", ".json");
* Files.delete(jsonFile);
* Path yamlFile = Files.createTempFile("mandatoryConfig", ".yaml");
* try {
* Files.write(yamlFile, "server:\n threads: 7\n port: 0".getBytes());
* RatpackServer server = RatpackServer.of(spec -> {
* ServerConfig serverConfig = ServerConfig
* .embedded()
* .onError(Action.noop()).json(jsonFile)
* .onError(Action.throwException()).yaml(yamlFile)
* .build();
* spec
* .serverConfig(serverConfig)
* .handler(registry ->
* ctx -> ctx.render("threads:" + ctx.get(ServerConfig.class).getThreads())
* );
* });
* server.start();
*
* TestHttpClient httpClient = ServerBackedApplicationUnderTest.of(server).getHttpClient();
* assertEquals("threads:7", httpClient.getText());
*
* server.stop();
* } finally {
* Files.delete(yamlFile);
* }
* }
* }
* }
*
* @see ConfigData#builder()
*/
public interface ConfigDataBuilder {
String DEFAULT_ENV_PREFIX = "RATPACK_";
String DEFAULT_PROP_PREFIX = "ratpack.";
/**
* Creates the config data, based on the state of this builder.
*
* @return a new config data
*/
ConfigData build();
/**
* Configures the object mapper used for binding configuration data to arbitrary objects.
*
* @param action an action to perform upon the object mapper
* @return {@code this}
*/
ConfigDataBuilder configureObjectMapper(Action action);
/**
* Adds a configuration source.
*
* @param configSource the configuration source to add
* @return {@code this}
*/
ConfigDataBuilder add(ConfigSource configSource);
/**
* Adds a configuration source for environment variables starting with the prefix {@value #DEFAULT_ENV_PREFIX}.
*
* The prefix will be removed before loading the data.
* The environment variable name is split into per-object segments using double underscore as an object boundary.
* Segments are transformed into camel-case field names using a single underscore as a word boundary.
*
* @return this
*/
ConfigDataBuilder env();
/**
* Adds a configuration source for environment variables starting with the specified prefix.
* The prefix will be removed before loading the data.
* The environment variable name is split into per-object segments using double underscore as an object boundary.
* Segments are transformed into camel-case field names using a single underscore as a word boundary.
*
* @param prefix the prefix which should be used to identify relevant environment variables
* @return this
*/
ConfigDataBuilder env(String prefix);
/**
* Adds a configuration source for environment variables starting with the specified prefix.
* The prefix will be removed before loading the data.
* The environment variable name is split into per-object segments using double underscore as an object boundary.
* Segments are transformed into field names using the specified transformation function rather than the default function.
*
* @param prefix the prefix which should be used to identify relevant environment variables
* @param mapFunc the function to transform segments into field names
* @return this
*/
ConfigDataBuilder env(String prefix, Function mapFunc);
/**
* Adds a configuration source for environment variables using custom parsing logic.
*
* @param environmentParser the parser to use to interpret environment variables
* @return {@code this}
*/
ConfigDataBuilder env(EnvironmentParser environmentParser);
/**
* Invokes {@link #args(String, String, String[])}, with no prefix and {@code "="} as the separator.
*
* @param args the argument values
* @return {@code this}
* @since 1.1
*/
default ConfigDataBuilder args(String[] args) {
return args("=", args);
}
/**
* Invokes {@link #args(String, String, String[])}, with no prefix.
*
* @param separator the separator of the key and value in each arg
* @param args the argument values
* @return {@code this}
* @since 1.1
*/
default ConfigDataBuilder args(String separator, String[] args) {
return args("", separator, args);
}
/**
* Adds a configuration source for the given string args.
*
* Args that do not start with the given {@code prefix} are ignored.
* The remaining are each split using the given {@code separator} (as a literal string, not as a regex),
* then trimmed of the prefix.
*
* @param prefix the prefix that each arg must have to be considered (use {@code null} or {@code ""} for no prefix)
* @param separator the separator between the key and the value
* @param args the argument values
* @return {@code this}
* @since 1.1
*/
default ConfigDataBuilder args(String prefix, String separator, String[] args) {
return add(new ArgsConfigSource(prefix, separator, args));
}
/**
* Adds a configuration source for a JSON file.
*
* @param byteSource the source of the JSON data
* @return {@code this}
*/
ConfigDataBuilder json(ByteSource byteSource);
/**
* Adds a configuration source for a JSON file.
*
* @param path the source of the JSON data
* @return {@code this}
*/
ConfigDataBuilder json(Path path);
/**
* Adds the JSON file at the given path as a configuration source.
*
* The default implementation of {@link ConfigDataBuilder} will resolve the given path relative to the actual file system root.
* Alternative implementations, such as {@link ratpack.server.ServerConfigBuilder#json(String)} may resolve the file location differently.
*
* @param path the path to the source of the JSON data
* @return {@code this}
*/
ConfigDataBuilder json(String path);
/**
* Adds a configuration source for a JSON file.
*
* @param url the source of the JSON data
* @return {@code this}
*/
ConfigDataBuilder json(URL url);
/**
* Adds a configuration source for a properties file.
*
* @param byteSource the source of the properties data
* @return {@code this}
*/
ConfigDataBuilder props(ByteSource byteSource);
/**
* Adds a configuration source for a properties file.
*
* @param path the source of the properties data
* @return {@code this}
*/
ConfigDataBuilder props(Path path);
/**
* Adds a configuration source for a properties object.
*
* @param properties the properties object
* @return {@code this}
*/
ConfigDataBuilder props(Properties properties);
/**
* Adds a configuration source for a Map (flat key-value pairs).
*
* This signature is particularly useful for providing default values as shown below:
*
*
{@code
* import com.google.common.collect.ImmutableMap;
* import ratpack.config.ConfigData;
* import ratpack.server.ServerConfig;
* import static org.junit.Assert.*;
*
* public class Example {
* public static void main(String[] args) throws Exception {
* ServerConfig serverConfig = ServerConfig
* .builder()
* .props(ImmutableMap.of("server.port", "5060"))
* .sysProps()
* .build();
* assertEquals(5060, serverConfig.getPort());
* }
* }
* }
*
* @param map the map
* @return {@code this}
*/
ConfigDataBuilder props(Map map);
/**
* Adds the properties file at the given path as a configuration source.
*
* The default implementation of {@link ConfigDataBuilder} will resolve the given path relative to the actual file system root.
* Alternative implementations, such as {@link ratpack.server.ServerConfigBuilder#props(String)} may resolve the file location differently.
*
* @param path the path to the source of the properties data
* @return {@code this}
*/
ConfigDataBuilder props(String path);
/**
* Adds a configuration source for a properties file.
*
* @param url the source of the properties data
* @return {@code this}
*/
ConfigDataBuilder props(URL url);
/**
* Adds a configuration source for system properties starting with the prefix {@value #DEFAULT_PROP_PREFIX}.
*
* @return {@code this}
*/
ConfigDataBuilder sysProps();
/**
* Adds a configuration source for system properties starting with the specified prefix.
*
* @param prefix the prefix which should be used to identify relevant system properties;
* the prefix will be removed before loading the data
* @return {@code this}
*/
ConfigDataBuilder sysProps(String prefix);
/**
* Adds a configuration source for a YAML file.
*
* @param byteSource the source of the YAML data
* @return {@code this}
*/
ConfigDataBuilder yaml(ByteSource byteSource);
/**
* Adds a configuration source for a YAML file.
*
* @param path the source of the YAML data
* @return {@code this}
*/
ConfigDataBuilder yaml(Path path);
/**
* Adds the YAML file at the given path as a configuration source.
*
* The default implementation of {@link ConfigDataBuilder} will resolve the given path relative to the actual file system root.
* Alternative implementations, such as {@link ratpack.server.ServerConfigBuilder#yaml(String)} may resolve the file location differently.
*
* @param path the path to the source of the YAML data
* @return {@code this}
*/
ConfigDataBuilder yaml(String path);
/**
* Adds a configuration source for a YAML file.
*
* @param url the source of the YAML data
* @return {@code this}
*/
ConfigDataBuilder yaml(URL url);
/**
* Adds the object's fields at the given path as a configuration source.
*
* The path is a period separated key string.
* The given object is subject to value merging and overrides as per other config sources.
*
*
{@code
* import ratpack.config.ConfigData;
* import java.util.Collections;
* import static org.junit.Assert.assertEquals;
*
* public class Example {
*
* static class Thing {
* public String f1;
* public String f2;
* public String f3;
* }
*
* public static void main(String... args) throws Exception {
* Thing input = new Thing();
* input.f1 = "1";
* input.f2 = "2";
*
* ConfigData configData = ConfigData.of(c -> c
* .object("thing", input)
* .props(Collections.singletonMap("thing.f1", "changed"))
* .object("thing.f3", "changed")
* );
* Thing thing = configData.get("/thing", Thing.class);
*
* assertEquals("changed", thing.f1);
* assertEquals("2", thing.f2);
* assertEquals("changed", thing.f3);
* }
* }
* }
*
* @param path the configuration path the object's fields should be mapped on to
* @param object the object from which to derive the configuration fields
* @return {@code this}
* @since 1.4
*/
ConfigDataBuilder object(String path, Object object);
/**
* Sets the error all that will be used for added configuration sources.
* The error all only applies to configuration sources added after this method is called; it is not applied retroactively.
*
* @param errorHandler the error all
* @return {@code this}
* @see ratpack.func.Action#noop()
* @see ratpack.func.Action#throwException()
*/
ConfigDataBuilder onError(Action super Throwable> errorHandler);
/**
* Returns the object mapper used for configuration binding.
*
* @return the object mapper
*/
ObjectMapper getObjectMapper();
/**
* Adds {@link Module Jackson modules} to the object mapper.
*
* @param modules the modules to add
* @return this
* @see ObjectMapper#registerModule(Module)
*/
default ConfigDataBuilder jacksonModules(Module... modules) {
getObjectMapper().registerModules(modules);
return this;
}
/**
* Returns the config sources used for configuration binding.
*
* Add sources via {@link #add(ConfigSource)}.
*
* @return the config sources
*/
ImmutableList getConfigSources();
}