-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
68a5641
commit 4e908a2
Showing
34 changed files
with
785 additions
and
551 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"label": "Annotating - Configurations", | ||
"position": 4, | ||
"link": { | ||
"type": "generated-index", | ||
"description": "5 minutes to learn the most important Gestalt concepts." | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
--- | ||
sidebar_position: 1 | ||
--- | ||
|
||
# Annotations | ||
When decoding a Java Bean style class, a record, an interface or a Kotlin Data Class you can provide a custom annotation to override the path for the field as well as provide a default. | ||
The field annotation `@Config` takes priority if both the field and method are annotated. | ||
The class annotation `@ConfigPrefix` allows the user to define the prefix for the config object as part of the class instead of the `getConfig()` call. If you provide both the resulting prefix is first the path in getConfig then the prefix in the `@ConfigPrefix` annotation. | ||
For example using `@ConfigPrefix(prefix = "connection")` with `DBInfo pool = gestalt.getConfig("db", DBInfo.class);` the resulting path would be `db.connection`. | ||
|
||
```java | ||
@ConfigPrefix(prefix = "db") | ||
public class DBInfo { | ||
@Config(path = "channel.port", defaultVal = "1234") | ||
private int port; | ||
|
||
public int getPort() { | ||
return port; | ||
} | ||
} | ||
|
||
DBInfo pool = gestalt.getConfig("", DBInfo.class); | ||
|
||
|
||
public class DBInfo { | ||
private int port; | ||
|
||
@Config(path = "channel.port", defaultVal = "1234") | ||
public int getPort() { | ||
return port; | ||
} | ||
} | ||
|
||
DBInfo pool = gestalt.getConfig("db.connection", DBInfo.class); | ||
``` | ||
|
||
The path provided in the annotation is used to find the configuration from the base path provided in the call to Gestalt getConfig. | ||
|
||
So if the base path from gestalt.getConfig is `db.connection` and the annotation is `channel.port` the path the configuration will look for is `db.connection.channel.port` | ||
|
||
The default accepts a string type and will be decoded into the property type using the gestalt decoders. For example if the property is an Integer and the default is "100" the integer value will be 100. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
--- | ||
sidebar_position: 1 | ||
--- | ||
|
||
# Getting Started | ||
|
||
## Introduction | ||
|
||
Gestalt is a powerful Java configuration library that merges multiple sources of configuration data into a type-safe and structured format. It supports configuration files, environment variables, in-memory maps, and more. | ||
|
||
### 1. Add the Bintray repository: | ||
|
||
Versions 0.1.0 through version 0.11.0 require Java 8. Versions 0.12.0 plus require Java 11. | ||
|
||
```kotlin | ||
repositories { | ||
mavenCentral() | ||
} | ||
``` | ||
### 2. Import gestalt-core, and the specific modules you need to support your use cases. | ||
|
||
Gradle example: | ||
```groovy | ||
implementation 'com.github.gestalt-config:gestalt-core:${version}' | ||
implementation 'com.github.gestalt-config:gestalt-kotlin:${version}' | ||
``` | ||
Or with the kotlin DSL: | ||
```kotlin | ||
implementation("com.github.gestalt-config:gestalt-core:$version") | ||
implementation("com.github.gestalt-config:gestalt-kotlin:$version") | ||
``` | ||
Maven Example: | ||
```xml | ||
<dependency> | ||
<groupId>com.github.gestalt-config</groupId> | ||
<artifactId>gestalt-core</artifactId> | ||
<version>${version}</version> | ||
</dependency> | ||
``` | ||
|
||
### 3. Setup your configuration files | ||
|
||
Multiple types of configurations are supported from multiple sources. | ||
Here is an example of the `default.properties`: | ||
```properties | ||
db.hosts[0].user=credmond | ||
db.hosts[0].url=jdbc:postgresql://localhost:5432/mydb1 | ||
db.hosts[1].user=credmond | ||
db.hosts[1].url=jdbc:postgresql://localhost:5432/mydb2 | ||
db.hosts[2].user=credmond | ||
db.hosts[2].url=jdbc:postgresql://localhost:5432/mydb3 | ||
db.connectionTimeout=6000 | ||
db.idleTimeout=600 | ||
db.maxLifetime=60000.0 | ||
|
||
http.pool.maxTotal=100 | ||
http.pool.maxPerRoute=10 | ||
http.pool.validateAfterInactivity=6000 | ||
http.pool.keepAliveTimeoutMs=60000 | ||
http.pool.idleTimeoutSec=25 | ||
``` | ||
Here is an example of the `dev.properties`: | ||
```properties | ||
db.hosts[0].url=jdbc:postgresql://dev.host.name1:5432/mydb | ||
db.hosts[1].url=jdbc:postgresql://dev.host.name2:5432/mydb | ||
db.hosts[2].url=jdbc:postgresql://dev.host.name3:5432/mydb | ||
db.connectionTimeout=600 | ||
|
||
http.pool.maxTotal=1000 | ||
http.pool.maxPerRoute=50 | ||
``` | ||
|
||
### 4. Construct Gestalt using the builder. | ||
|
||
Use the builder to construct the Gestalt library. It is possible to do this manually, but the builder greatly simplifies the construction of the library. It uses the service loader to automatically load all the default dependencies. | ||
```java | ||
// Load the default property files from resources. | ||
URL devFileURL = GestaltSample.class.getClassLoader().getResource("dev.properties"); | ||
File devFile = new File(devFileURL.getFile()); | ||
|
||
// using the builder to layer on the configuration files. | ||
// The later ones layer on and over write any values in the previous | ||
Gestalt gestalt = new GestaltBuilder() | ||
.addSource(ClassPathConfigSourceBuilder.builder().setResource("/default.properties").build()) | ||
.addSource(FileConfigSourceBuilder.builder().setFile(devFile).build()) | ||
.build(); | ||
|
||
// Loads and parses the configurations, this will throw exceptions if there are any errors. | ||
gestalt.loadConfigs(); | ||
``` | ||
|
||
### 5. Retrieve configurations from Gestalt | ||
|
||
Using the Gestalt Interface you can load sub nodes with dot notation into a wide variety of classes. | ||
For non-generic classes you can pass in the class with `getConfig("db.port", Integer.class)` or for classes with generic types we need to use a special TypeCapture wrapper that captures the generic type at runtime. This allows us to construct generic classes with such as `List<String>` using `new TypeCapture<List<String>>() {}` | ||
|
||
```java | ||
Short myShortWrapper = gestalt.getConfig("http.pool.maxTotal", Short.class); | ||
HttpPool pool = gestalt.getConfig("http.pool", HttpPool.class); | ||
List<HttpPool> httpPoolList = gestalt.getConfig("http.pools", new TypeCapture<>() { }); | ||
var httpPoolList = gestalt.getConfig("http.pools", new TypeCapture<List<HttpPool>>() { }); | ||
``` | ||
|
||
The API to retrieve configurations: | ||
```java | ||
/** | ||
* Get a config for a path and a given class. | ||
* If the config is missing or there are any errors it will throw a GestaltException | ||
*/ | ||
<T> T getConfig(String path, Class<T> klass) throws GestaltException; | ||
|
||
/** | ||
* Get a config for a path and a given class. | ||
* If the config is missing, invalid or there was an exception it will return the default value. | ||
*/ | ||
<T> T getConfig(String path, T defaultVal, Class<T> klass); | ||
|
||
/** | ||
* Get a config Optional for a path and a given class. | ||
* If the config is missing, invalid or there was an exception it will return an Optional.empty() | ||
*/ | ||
<T> Optional<T> getConfigOptional(String path, Class<T> klass); | ||
``` | ||
|
||
#### Example | ||
Example of how to create and load a configuration objects using Gestalt: | ||
```java | ||
public static class HttpPool { | ||
public short maxTotal; | ||
public long maxPerRoute; | ||
public int validateAfterInactivity; | ||
public double keepAliveTimeoutMs = 6000; // has a default value if not found in configurations | ||
public OptionalInt idleTimeoutSec = 10; // has a default value if not found in configurations | ||
public float defaultWait = 33.0F; // has a default value if not found in configurations | ||
|
||
public HttpPool() { | ||
|
||
} | ||
} | ||
|
||
public static class Host { | ||
private String user; | ||
private String url; | ||
private String password; | ||
private Optional<Integer> port; | ||
|
||
public Host() { | ||
} | ||
|
||
// getter and setters ... | ||
} | ||
|
||
... | ||
// load a whole class, this works best with pojo's | ||
HttpPool pool = gestalt.getConfig("http.pool", HttpPool.class); | ||
// or get a spcific config value | ||
short maxTotal = gestalt.getConfig("http.pool.maxTotal", Short.class); | ||
// get with a default if you want a fallback from code | ||
long maxConnectionsPerRoute = gestalt.getConfig("http.pool.maxPerRoute", 24, Long.class); | ||
|
||
|
||
// get a list of objects, or an PlaceHolder collection if there is no hosts found. | ||
List<Host> hosts = gestalt.getConfig("db.hosts", Collections.emptyList(), | ||
new TypeCapture<List<Host>>() {}); | ||
``` |
This file was deleted.
Oops, something went wrong.
8 changes: 8 additions & 0 deletions
8
docs/gestalt-static/docs/loading-configuration/_category_.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"label": "Loading - Configurations", | ||
"position": 2, | ||
"link": { | ||
"type": "generated-index", | ||
"description": "5 minutes to learn the most important Gestalt concepts." | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
docs/gestalt-static/docs/loading-configuration/config-loader.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
--- | ||
sidebar_position: 4 | ||
--- | ||
|
||
# Config Loader | ||
Each config loader understands how to load a specific type of config. Often this is associated with a specific ConfigSource. For example the EnvironmentVarsLoader only loads the EnvironmentConfigSource. However, some loaders expect a format of the config, but accept it from multiple sources. For example the PropertyLoader expects the typical java property file, but it can come from any source as long as it is an input stream. It may be the system properties, local file, github, or S3. | ||
|
||
| Config Loader | Formats supported | details | module | | ||
|-----------------------|-----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------| | ||
| EnvironmentVarsLoader | envVars | Loads Environment Variables from the EnvironmentConfigSource, it expects a list not a InputStream. By default, it splits the paths using a "_". You can also disable failOnErrors if you are receiving errors from the environment variables, as you can not always control what is present. By treating Errors as warnings it will not fail if it finds a configuration the parser doesn't understand. Instead it will ignore the specific config. | core | | ||
| MapConfigLoader | mapConfig | Loads a user provided Map from the MapConfigSource, it expects a list not a InputStream. By default, it splits the paths using a "." and tokenizes arrays with a numeric index as "[0]". | core | | ||
| PropertyLoader | properties, props, and systemProperties | Loads a standard property file from an InputStream. By default, it splits the paths using a "." and tokenizes arrays with a numeric index as "[0]". | core | | ||
| JsonLoader | json | Leverages Jackson to load json files and convert them into a ConfigNode tree. | [`gestalt-json`](https://search.maven.org/search?q=gestalt-json) | | ||
| TomlLoader | toml | Leverages Jackson to load toml files and convert them into a ConfigNode tree. | [`gestalt-toml`](https://search.maven.org/search?q=gestalt-toml) | | ||
| YamlLoader | yml and yaml | Leverages Jackson to load yaml files and convert them into a ConfigNode tree. | [`gestalt-yaml`](https://search.maven.org/search?q=gestalt-yaml) | | ||
| HoconLoader | config | Leverages com.typesafe:config to load hocon files, supports substitutions. | [`gestalt-hocon`](https://search.maven.org/search?q=gestalt-hocon) | | ||
|
||
If you didn't manually add any ConfigLoaders as part of the GestaltBuilder, it will add the defaults. The GestaltBuilder uses the service loader to create instances of the Config loaders. It will configure them by passing in the GestaltConfig to applyConfig. | ||
To register your own default ConfigLoaders add them to the builder, or add it to a file in META-INF\services\org.github.gestalt.config.loader.ConfigLoader and add the full path to your ConfigLoader | ||
|
||
By default, Gestalt expects Environment Variables to be screaming snake case, but you can configure it to have a different case. | ||
|
||
By registering a `EnvironmentVarsLoaderModuleConfig` with the `GestaltBuilder` you can customize the Environment Loader. | ||
|
||
In this example it will expect double `__` as delimiter. | ||
```java | ||
GestaltBuilder builder = new GestaltBuilder(); | ||
Gestalt gestalt = builder | ||
.addSource(EnvironmentConfigSourceBuilder.builder().build()) | ||
.addModuleConfig(EnvironmentVarsLoaderModuleConfigBuilder | ||
.builder() | ||
.setLexer(new PathLexer("__")) | ||
.build()) | ||
.build(); | ||
|
||
gestalt.loadConfigs(); | ||
``` | ||
|
||
You can also customize many of the Loaders such as the `YamlLoader`, `TomlLoader`, `JsonLoader` and `HoconLoader` by registering the Module Configs with the builder. | ||
|
||
```java | ||
GestaltBuilder builder = new GestaltBuilder(); | ||
Gestalt gestalt = builder | ||
.addSource(ClassPathConfigSourceBuilder.builder().setResource("/default.yaml").build()) | ||
.addModuleConfig(YamlModuleConfigBuilder.builder() | ||
.setObjectMapper(customObjectmapper) | ||
.build()) | ||
.build(); | ||
|
||
gestalt.loadConfigs(); | ||
``` |
18 changes: 18 additions & 0 deletions
18
docs/gestalt-static/docs/loading-configuration/config-sources.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
--- | ||
sidebar_position: 1 | ||
--- | ||
|
||
# Config Sources | ||
|
||
Adding a ConfigSource to the builder is the minimum step needed to build the Gestalt Library. | ||
You can add several ConfigSources to the builder and Gestalt, and they will be loaded in the order they are added. Where each new source will be merged with the existing source and where applicable overwrite the values of the previous sources. Each Config Source can be a diffrent format such as json, properties or Snake Case Env Vars, then internally they are converted into a common config tree. | ||
|
||
```java | ||
Gestalt gestalt = builder | ||
.addSource(FileConfigSourceBuilder.builder().setFile(defaults).build()) | ||
.addSource(FileConfigSourceBuilder.builder().setFile(devFile).build()) | ||
.addSource(EnvironmentConfigSourceBuilder.builder().setPrefix("MY_APP_CONFIG").build()) | ||
.build(); | ||
``` | ||
In the above example we first load a file defaults, then load a file devFile and overwrite any defaults, then overwrite any values from the Environment Variables. | ||
The priority will be Env Vars > devFile > defaults. |
33 changes: 33 additions & 0 deletions
33
docs/gestalt-static/docs/loading-configuration/config-tree.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
--- | ||
sidebar_position: 2 | ||
--- | ||
|
||
# Config Tree | ||
The config files are loaded and merged into a config tree. While loading into the config tree all node names and paths are converted to lower case and for environment variables we convert screaming snake case into dot notation. However, we do not convert other cases such as camel case into dot notation. So if your configs use a mix of dot notation and camel case, the nodes will not be merged. You can configure this conversion by providing your own `Sentence Lexer` in the `GestaltBuilder`. The config tree has a structure (sort of like json) where the root has one or more nodes or leafs. A node can have one or more nodes or leafs. A leaf can have a value but no further nodes or leafs. As we traverse the tree each node or leaf has a name and in combination it is called a path. A path can not have two leafs or both a node and a leaf at the same place. If this is detected Gestalt will throw an exception on loading with details on the path. | ||
|
||
Valid: | ||
```properties | ||
|
||
db.connectionTimeout=6000 | ||
db.idleTimeout=600 | ||
db.maxLifetime=60000.0 | ||
|
||
http.pool.maxTotal=1000 | ||
http.pool.maxPerRoute=50 | ||
``` | ||
|
||
Invalid: | ||
```properties | ||
|
||
db.connectionTimeout=6000 | ||
db.idleTimeout=600 | ||
db=userTable #invalid the path db is both a node and a leaf. | ||
|
||
http.pool.maxTotal=1000 | ||
http.pool.maxPerRoute=50 | ||
HTTP.pool.maxPerRoute=75 #invalid duplicate nodes at the same path. | ||
``` | ||
|
||
All paths are converted to lower case as different sources have different naming conventions, Env Vars are typically Screaming Snake Case, properties are dot notation, json is camelCase. By normalizing them to lowercase it is easier to merge. However, we do not convert other cases such as camel case into dot notation. It is best to use a consistent case for your configurations. | ||
|
||
This is configurable if you desire to keep case or split on different tokens. |
Oops, something went wrong.