# Conf4j User's Guide
* [Overview](#overview)
* [Getting Started](#getting-started)
* [Configuration Factory](#configuration-factory)
* [Configuration Keys](#configuration-keys)
* [Type Converters](#type-converters)
* [Configuration Types with Generics](#configuration-types-with-generics)
* [Spring Framework Integration](#spring-framework-integration)
* [Extras](#extras)
## Overview
__conf4j__ is a library which allows accessing configuration data in object-oriented, type-safe manner.
_Configuration_ is represented as an interface or abstract class optionally annotated with _conf4j_ annotations.
#### Features
* Simple, intuitive, annotation driven _API_ for defining configuration types.
* All configuration properties are statically typed and validated.
* Out of the box support for all primitive types and they wrappers as well as `List` and `Map`.
* Integrated with [Spring Framework](https://projects.spring.io/spring-framework) and
and [Spring Boot](http://projects.spring.io/spring-boot).
* High level _API_ for accessing configuration values.
* Minimum set of dependencies: [SLF4J](http://slf4j.org),
[commons-lang](https://commons.apache.org/proper/commons-lang) and
[commons-text](https://commons.apache.org/proper/commons-text).
## Getting Started
To start playing with _conf4j_ just add the dependency to _com.sabre.oss.conf4j:conf4j-core_
or _com.sabre.oss.conf4j:conf4j-spring_ (if you wish to integrate the library with _Spring Framework_).
_Maven_
```xml
com.sabre.oss.conf4j
conf4j-core
${conf4j.version}
```
_Gradle_
```groovy
dependencies {
compile "com.sabre.oss.conf4j:conf4j-core:$conf4jVersion"
}
```
Of course make sure `conf4j.version` variable (for _Maven_) or `conf4jVersion` (for _Gradle_) is set to the proper
_conf4j_ version.
In _conf4j_, a configuration is expressed as a public interface or public abstract class annotated with _conf4j_ annotations.
```java
@Key("connection")
public interface ConnectionConfiguration {
String getUrl();
TimeoutConfiguration getTimeouts();
}
public interface TimeoutConfiguration {
@Default("60")
int getConnectionTimeout();
@Default("30")
int getReadTimeout();
}
```
The configuration consists of a set of properties (which follows _JavaBeans_ naming conventions).
Each property must be an `public`, `abstract`, _parameter-less_ method which return type cannot be `void`.
The return type can be any type supported by the `TypeConverter`, another configuration type
or `List` of configuration types.
Every configuration property has a set of _configuration keys_ assigned which are determined
based on _property name_, `@Key`, `@FallbackKey`, `@IgnorePrefix` and `@IgnoreKey` annotations.
The rules that govern how the _configuration key set_ is constructed are covered in the
[Configuration Keys](#configuration-keys) section.
When a value related to a configuration property is required, it is retrieved from the `ConfigurationSource`.
Each key associated with the property is checked in the sequence until the value associated with the key is found
in the _configuration source_. It is also possible to specify a default value for the property via the
`@Default` annotation and `@DefaultsAnnotation` meta-annotations. See _javadoc_ for details.
The configuration type is like a template. Before you begin accessing the configuration, a configuration instance
must first be created and bound to the _configuration source_. This can be done via `ConfigurationFactory.createConfiguration()`
as shown below.
```java
// Create configuration source from the property file.
ConfigurationSource source = new PropertiesConfigurationSource("configuration.properties");
// Create configuration factory - it is thread safe.
ConfigurationFactory factory = new JdkProxyStaticConfigurationFactory();
// Create configuration instance and bind it to the configuration source.
ConnectionConfiguration configuration = factory.createConfiguration(ConnectionConfiguration.class, source);
// Now you can access configuration properties via configuration object.
String url = configuration.getUrl();
int connectionTimeout = configuration.getTimeouts().getConnectionTimeout();
```
The `ConfigurationSource` allows accessing a configuration value associated with a key. Both key and value
are represented as string. Of course, very often, the configuration property type is not `java.lang.String`
so the value must be converted into the appropriate type.
_Type converters_ which implement `TypeConverter` interface are responsible for such conversion.
The _conf4j_ library provides type converters for all primitive types (like `boolean`, `int`, `double`),
primitive wrapper types (like `Boolean`, `Integer`, `Double`), _enumerations_ and many other types which are frequently
used (like `BigDecimal`). More complex types are also supported - there is a dedicated converter for `List` and `Map`
which is able to convert even very complex types like `Map>>`. Because lists and maps
are quite complex, the data must be encoded to be represented as a string. Currently `JsonLikeConverter` is used
as a default implementation. It encodes `List` and `Map` as _JSON_, but by default _compact mode_ is activated to make
the encoded string more user friendly.
For example `List` in compact mode is represented as `[one,two,tree]` in JSON mode as `["one","two","three"]`.
Map> in compact mode: `{key1:[val11,val12],key2:[val21,val22]}` in JSON: `{"key1":["val11","val12"],"key2":["val21","val22"]}`.
Because quotation mark `"` must be escaped in _java_, JSON format is very inconvenient for specifying default value in `@Default`.
Please consult javadoc for `JsonLikeConverter` class to get more information about the format.
Of course there is possibility to provide custom implementation of `TypeConverter`.
For details, see the [Type Converters](#type-converters) section.
_conf4j_ provides out of the box integration with [Spring Framework](https://projects.spring.io/spring-framework)
and [Spring Boot](http://projects.spring.io/spring-boot) application frameworks.
First of all add dependency to `com.sabre.oss.conf4j:conf4j-spring` module to your project.
```xml
com.sabre.oss.conf4j
conf4j-spring
${conf4j.version}
```
Then annotate the root configuration with _Spring Framework_ `@Component` annotation.
```java
@Component
public interface ConnectionConfiguration {
String getUrl();
String getUser();
}
```
Assuming you would like to use annotation-based configuration, use `@EnableConf4j` in your configuration class
and search for configurations with `@ConfigurationsScan` as shown below.
```java
@EnableConf4j
@ConfigurationScan
public class Application {
@Autowired
private ConnectionConfiguration connectionConfiguration;
public static void main(String[] args) {
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(Application.class)) {
Application application = context.getBean(Application.class);
application.run();
}
}
private void run() {
String url = connectionConfiguration.getUrl();
// ...
}
@Key("connection")
public interface ConnectionConfiguration {
String getUrl();
@Key("user")
String getUserName();
}
}
```
For details, see [Spring Framework Integration](#spring-framework-integration) section.
## Configuration Factory
The `ConfigurationFactory` interface specifies how the configuration type is instantiated and bound
to the `ConfigurationSource`. It takes a configuration type and _configuration source_ and creates a configuration instance.
```java
ConnectionConfiguration configuration = factory.createConfiguration(ConnectionConfiguration.class, source);
```
How a configuration instance is generated depends on the configuration factory implementation.
There are two flavors of the `ConfigurationFactory`: _static_ and _dynamic_ configuration factories.
_Static configuration factory_ creates _static_/_frozen_ configuration instances which retrieves values from
`ConfigurationSource` only during the instance-creation phase. The configuration values are then stored
in the configuration instance and never change in the future. Access to _static_ configuration properties is very fast,
it's just a getter invocation.
_Static_ configuration classes implements the `java.io.Serializable` interface which allows a configuration instance
to be serialized, as long as all configuration property types are also serializable.
`Dynamic configuration factory` creates a _dynamic_ configuration instance which hits `ConfigurationSource`
every time the configuration property method is invoked. It is an ideal choice when values in _configuration source_
changes over time and a configuration should reflect those changes.
Bear in mind there is an overhead associated with a configuration property access. It requires getting configuration
value from the source and converting it the proper type.
_conf4j_ provides three configuration factory families:
* `JdkProxyStaticConfigurationFactory` and `JdkProxyDynamicConfigurationFactory` use _jdk proxy_
`java.lang.reflect.Proxy` for creating configuration instances. These factories support configuration types
which are interfaces, abstract classes are not supported due to _jdk proxy_ limitations.
* `JavassistStaticConfigurationFactory` and `JavassistDynamicConfigurationFactory` use
[Javassist](http://jboss-javassist.github.io/javassist) to generate configuration implementations on the fly.
* `CglibStaticConfigurationFactory` and `CglibDynamicConfigurationFactory` use [CGLIB](https://github.com/cglib/cglib/wiki)
(to be precise - CGLIB repackaged version provided by _Spring Framework_). These factories are available only
when you use _conf4j_ integration with _Spring Framework_.
## Configuration Keys
Every configuration property has an ordered set of _configuration keys_ assigned. A property can be associated
with multiple keys, and multiple properties can be associated to the same keys. When a configuration value is required,
the keys from the _key set_ is submitted to `ConfigurationSource` in sequence until the value associated
with the key is found.
_Key set_ is constructed based on _conf4j_ annotations.
`@Key` annotation specifies the key for a value property. If `@Key` annotation is missing, the _property name_ is used.
```java
public interface ConnectionConfiguration {
String getUrl();
@Key("user")
String getUserName();
}
```
_ConnectionConfiguration_ has two configuration properties: _url_ and _userName_.
The key set for the _url_ property consists of one key _url_.
When the key name is not specified by the `@Key` annotation, the property name is used. `@Key` annotation without
parameters is optional (as long as default, _non-strict_ configuration model provider is used) and property name is used
as a key in such case. But using it, indicates explicitly a method is the configuration property and is a good practice.
The _userName_ property has one key _user_ assigned and its name is explicitly specified by the `@Key` annotation.
When applied to the configuration type or property which returns sub-configuration or list of sub-configurations,
an `@Key` annotation can define a prefix for the configuration keys within the hierarchy.
For the example below, _url_ property has two keys assigned: _connection.url_ and _alternateConnection.url_.
Please note the prefix and key are joined by `.` (dot) character which is a delimiter used to join configuration key components.
```java
@Key({"connection", "alternateConnection"})
public interface ConnectionConfiguration {
String getUrl();
@Key("user")
String getUserName();
}
```
`@Key` normally specifies just one key or prefix; however, it allows you to define multiple keys or prefixes.
If the key prefix value is not specified, the uncapitalized type name or property name is used
(when the `@Key` is applied to the _sub-configuration_).
Here is a more complex scenario with configuration and sub-configurations involved.
```java
@Key({"connection", "alternateConnection"})
public interface ConnectionConfiguration {
String getUrl();
@Key("user")
String getUserName();
TimeoutConfiguration getTimeout();
@Key("default")
TimeoutConfiguration getDefaultTimeout();
@Key("other")
List getOtherTimeouts();
}
@Key("timeout")
public interface TimeoutConfiguration {
@Key("connect")
Integer getConnectTimeout();
@Key("read")
Integer getReadTimeout();
}
```
Let's create a configuration instance `configuration`.
```java
ConnectionConfiguration configuration = factory.createConfiguration(ConnectionConfiguration.class, source);
```
For the following invocation:
```java
Integer connect = configuration.getTimeout().getConnectTimeout();
```
the key set for `getConnectionTimeout()` is: _connection.timeout.connect_, _alternateConnection.timeout.connect_.
_connection_ and _alternateConnection_ key prefixes are defined by `@Key` applied on `ConfigurationConfiguration`,
_timeout_ key prefix from `@Key` applied on `@TimeoutConfiguration`.
For the following invocation:
```java
Integer connect = configuration.getDefaultTimeout().getConnectTimeout();
```
the key set for `getConnectionTimeout()` is: _connection**.default**.timeout.connect_, _alternateConnection**.default**.timeout.connect_
because `getDefaultTimeout()` is annotated with `@Key("default")`
A list of sub-configurations are handled a little differently. Because the list contains multiple elements, there must
be a way to distinguish between keys assigned to different elements. Every element in the list has an index which
becomes part of the key.
For the following invocation:
```java
Integer connect = configuration.getOtherTimeouts().get(0).getConnectTimeout();
```
the key set for `getConnectionTimeout()` is:
_connection**.other[0]**.timeout.connect_, _alternateConnection**.other[0]**.timeout.connect_,
_connection.other.timeout.connect_, _alternateConnection.other.timeout.connect_.
The first two keys have `[0]` appended to the `other` key component. _conf4j_ uses the `[index]` convention
(where _index_ is an element index in the list) to indicate a key is associated with a list element.
Surprisingly the key set contains _connection.other.timeout.connect_ and _alternateConnection.other.timeout.connect_.
They are added for each element's key set as a fallback. These keys are used only when there is no value associated
with indexed keys in the _configuration source_.
For the configuration property which returns a list of sub-configuration there is a need to provide the size of the list.
There is an additional key set associated, which is created by appending the _.size_ suffix.
For `configuration.getOtherTimeouts()` the key set is: _connection.other.size_, _alternateConnection.other.size_.
_conf4j_ provides more annotations like `@FallbackKey` or `@IgnoreKey` which influence the way a _key set_ is generated.
Please consult the _javadoc_ for details.
## Type Converters
_Type converter_ allows converting a string value into the appropriate type like `int`, `decimal` or even `List`.
Type converter must implement a `TypeConverter` interface and provide the method for converting a string into a value
of a proper type as well as a method for converting the value back to a string representation.
`TypeConverter` is usually provided for simple types like `int` but it may also convert complex objects and graphs,
e.g., the objects annotated with _JAXB_ annotations.
The converter must be _thread safe_ and _stateless_ because it's accessed from multiple threads and different contexts.
Ideally it should be _symmetric_ so the result of _value_ conversion to string, and then the string back to the value,
produces the same value.
_conf4j_ provides converters for commonly used types like `int`, `long`, `boolean`, `double`, _enumerations_, `String`
but also converts to `List` and `Map` . The former converters are generic and support any `E, K, V` types
as long as converters for `E, K, V` are available.
Even exotic types are supported: `Map, List