Skip to content

Latest commit

 

History

History
1082 lines (845 loc) · 40.3 KB

USERS-GUIDE.md

File metadata and controls

1082 lines (845 loc) · 40.3 KB

Conf4j User's Guide

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 and and Spring Boot.
  • High level API for accessing configuration values.
  • Minimum set of dependencies: SLF4J, commons-lang and 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

<dependency>
  <groupId>com.sabre.oss.conf4j</groupId>
  <artifactId>conf4j-core</artifactId>
  <version>${conf4j.version}</version>
</dependency>

Gradle

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.

@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 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.

// 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<E> and Map<K, V> which is able to convert even very complex types like Map<String, Map<Integer, List<Double>>>. 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<String> in compact mode is represented as [one,two,tree] in JSON mode as ["one","two","three"]. Map<String, List> 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 section.

conf4j provides out of the box integration with Spring Framework and Spring Boot application frameworks.

First of all add dependency to com.sabre.oss.conf4j:conf4j-spring module to your project.

<dependency>
  <groupId>com.sabre.oss.conf4j</groupId>
  <artifactId>conf4j-spring</artifactId>
  <version>${conf4j.version}</version>
</dependency>

Then annotate the root configuration with Spring Framework @Component annotation.

@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.

@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 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.

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 to generate configuration implementations on the fly.
  • CglibStaticConfigurationFactory and CglibDynamicConfigurationFactory use CGLIB (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.

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.

@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.

@Key({"connection", "alternateConnection"})
public interface ConnectionConfiguration {
   String getUrl();

   @Key("user")
   String getUserName();

   TimeoutConfiguration getTimeout();

   @Key("default")
   TimeoutConfiguration getDefaultTimeout();

   @Key("other")
   List<TimeoutConfiguration> getOtherTimeouts();
}

@Key("timeout")
public interface TimeoutConfiguration {
   @Key("connect")
   Integer getConnectTimeout();

   @Key("read")
   Integer getReadTimeout();
}

Let's create a configuration instance configuration.

ConnectionConfiguration configuration = factory.createConfiguration(ConnectionConfiguration.class, source);

For the following invocation:

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:

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:

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<Integer>. 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<E> and Map<K, V> . 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<String>, List<Map<Integer, Double>>.

TypeConverter specifies the format of the string representation of a type. For some types like int the natural string representation is already defined, e.g., by toString() method. On the other hand, you may wish to change the format slightly. For example, StringConverter replaces new line separator with \n character or tab with \t - in general, it uses the same rules for escaping as Java.

ConfigurationFactory is pre-configured with wide range of converters (it may be can be altered by setTypeConverter()). In case there is a need to use a type converter which is not known by ConfigurationFactory or the type converter registered in the factory is not appropriate (e.g. you would like to convert Integer to hexadecimal value) @Converter annotation can be used as shown below:

public interface DateRange {
  @Converter(DateTimeConverter.class)
  DateTime getStart();

  @Converter(DateTimeConverter.class)
  DateTime getEnd();
}

TypeConverter implementation pointed by @Converter must provide public, parameterless constructor and it is instantiated on runtime via reflection. Type converter has to be thread-safe and stateless. Because many instances of the converter are created, make sure creating new instance is fast and created object doesn't occupy much memory.

In general favor setting up ConfigurationFactory with the appropriate converters over using @Converter annotation.

Formatted Type Converters

Many type converters support custom formats which can be specified via format meta-attribute. Format is converter specific and defines how the configuration value is converter from/to a string. In addition, converters for the numbers support locale meta-attribute - its value must be valid ISO 639 locale code.

Meta-attribute value can be assigned to a configuration property via @Meta annotation as shown below:

public interface DisplayConfiguration {
   @Meta(name="format", value="#.000")
   BigDecimal getValue();
}

@Meta annotation associates meta-attribute with a configuration value. It can be applied on the configuration property method level as well as on the configuration interface/class level - in this case all configuration properties defined in the interface/class inherit this meta-attribute. For more details please consult @Meta javadoc.

meta-attributes are available for type converters and configuration value sources and can be used for any purpose. As mentioned earlier, many conf4j type converters understand format meta-attribute. Whenever type converter is asked for converting string to value (and vice versa), the format attribute's value is used and appropriate formatting applied.

@Meta annotation can be used for meta annotating custom annotation. It is very handy and allows creating dedicated annotation like in the example below:

@Meta(name = "format")
@Retention(RUNTIME)
@Target(METHOD)
@Documented
public @interface Format {
    String value();
}

Now @Format annotation can be used instead of @Meta(name="format", value="#.000"):

public interface DisplayConfiguration {
   @Format(value="#.000")
   BigDecimal getValue();
}

The table below contains the list of available type converters and the format they support.

Converter Supported format
BooleanConverter {true value}/{false value}, for example: true/false or yes/no
ByteConverter As defined by DecimalFormat
ShortConverter As defined by DecimalFormat
IntegerConverter As defined by DecimalFormat
LongConverter As defined by DecimalFormat
FloatConverter As defined by DecimalFormat
DoubleConverter As defined by DecimalFormat
BigDecimalConverter As defined by DecimalFormat
LocalDateTimeConverter As defined by DateTimeFormatter
InstantConverter As defined by DateTimeFormatter
OffsetDateTimeConverter As defined by DateTimeFormatter
DurationConverter As defined by DurationFormatUtils

Configuration Types with Generics

conf4j configuration types supports generics. It simplifies creating configurations which shares same behaviour.

Following example demonstrates how to use generics to create a base configuration type ValidatorConfiguration with simple properties name, enabled and customizable property constraints.

@AbstractConfiguration
@Key
public interface ValidatorConfiguration<C> {
    String getName();

    boolean isEnabled();

    C getConstraints();
}

public interface IntegerValidatorConfiguration extends ValidatorConfiguration<IntegerConstraints> {
}

public interface IntegerConstraints {
    Integer getMin();

    Integer getMax();
}

public interface StringValidatorConfiguration extends ValidatorConfiguration<StringConstraints> {
}

public interface StringConstraints  {
    Pattern getPattern();
}

Because ValidatorConfiguration serves as a template (actual sub-configuration type is not known), it has been marked as an abstract using @AbstractConfiguration annotation.

There are two concrete configurations IntegerValidatorConfiguration and StringValidatorConfiguration which extends ValidatorConfiguration and specifies the constraint type.

Generics can be used not only for sub-configurations properties but for value properties too. MinMax abstract configuration type (see example below) has two min and max properties of generic type T.

IntegerConstraint and SubStringConstraint extends MinMax and specifies the type of min and max as Integer.

@AbstractConfiguration
@Key
public interface MinMax<T extends Comparable<T>> {
    T getMin();

    T getMax();
}

public interface IntegerConstraint extends MinMax<Integer> {
}

public interface StringConstraint extends MinMax<String> {
    Pattern getPattern();
}

More unusual generic usages for value properties are also possible.

@AbstractConfiguration
@Key
public interface AbstractExoticConfiguration<K, V> {
    List<Map<K, List<V>>> getProperty();
}

public interface SomeConfiguration extends AbstractExoticConfiguration<String, Integer> {
}

Spring Framework Integration

conf4j provides out of the box integration with Spring Framework First of all add dependency to com.sabre.oss.conf4j:conf4j-spring module to your project.

Maven

<dependency>
  <groupId>com.sabre.oss.conf4j</groupId>
  <artifactId>conf4j-spring</artifactId>
  <version>${conf4j.version}</version>
</dependency>

Gradle

dependencies {
  compile "com.sabre.oss.conf4j:conf4j-spring:$conf4jVersion"
}

conf4j supports XML schema-based as well as annotation driven configurations and both types can be used simultaneously.

XML Schema-based Configuration

conf4j provides custom configuration schema http://www.sabre.com/schema/oss/conf4j which exposes three custom tags: <conf4j:configure/>, <conf4j:configuration/>, <conf4j:converter/>, <conf4j:converter-decorator/> and <conf4j:configuration-scan/>.

<conf4j:configure/> is used for activating conf4j integration with Spring Framework. It registers several infrastructure beans (like ConfigurationFactory, ConfigurationSource or TypeConverter) and post processors. Such beans are used by conf4j for registering configuration types as beans, creating configuration instances and binding the configuration source.

<conf4j:configuration/> registers in the context a bean for the configuration type. This tag expects one attribute class which specifies fully qualified name of the configuration type. It is also possible to specify the bean name using id attribute (by default fully qualified class name is used).

<conf4j:configuration
    class="com.your.organization.configuration.package.ConnectionConfiguration"/>

<conf4j:converter/> registers in the context a type converter. This tag expects one attribute class which specifies fully qualified name of the converter class. It is also possible to specify the bean name using id attribute (by default fully qualified class name is used). Moreover this tag supports order attribute, which is XML equivalent of the org.springframework.core.annotation.Order annotation in Java based Spring configuration.

<conf4j:converter
    class="com.your.organization.configuration.package.CustomConverter"
    order="1"/>

<conf4j:converter-decorator/> registers in the context a decorating converter factory. Converter decorator is a converter providing high level conversion policy and delegating low level details to the inner converter. As an example consider converter for arrays. Its responsibility would be adding brackets and separating elements with commas. Although conversion of each element would be delegated to the inner converter. This tag allows to specify the bean name using id attribute. By default, if factory attribute is provided, fully qualified factory name is used. Otherwise the bean name will be fully qualified class name with $Conf4jDecoratingConverterFactory suffix. Attribute factory specifies fully qualified name of of the com.sabre.oss.conf4j.converter.DecoratingConverterFactory implementation. If it is not provided, com.sabre.oss.conf4j.spring.converter.DefaultDecoratingConverterFactory with class attribute injected will be used. Attribute class states fully qualified name of the converter class. This class must provide a constructor with com.sabre.oss.conf4j.converter.TypeConverter parameter type. Moreover this tag supports order attribute, which is XML equivalent of the org.springframework.core.annotation.Order annotation in Java based Spring configuration.

<conf4j:converter-decorator
    class="com.your.organization.configuration.package.CustomConverterDecorator"
    order="2"/>
<conf4j:converter-decorator
    factory="com.your.organization.configuration.package.CustomDecoratingConverterFactory"
    order="3"/>

<conf4:configuration-scan> is very similar to <context:component-scan/> provided by Spring Framework. It searches for the configuration types in the package (and all its sub-packages) specified by base-package attribute and register them as a beans to the context. To make a conf4j configuration type discoverable, it must be annotated (or meta-annotated) by @Component annotation provided by Spring Framework. There also an option use your own annotation (or annotations), just specify it by configuration-annotations attribute.

<conf4j:configuration-scan
    base-package="com.your.organization.configuration.package"
    configuration-annotations="com.your.organization.ConfigurationAnnotation"/>

<conf4:configuration-scan> supports filtering via include-filter and exclude-filter elements exactly the same way as <context:component-scan/>.

The example below shows how to use <conf4j:configure/> and <conf4:configuration-scan> to activate conf4j and register all configuration types from com.your.organization.configuration.package in the the context.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:conf4j="http://www.sabre.com/schema/oss/conf4j"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.sabre.com/schema/oss/conf4j http://www.sabre.com/schema/oss/conf4j/conf4j.xsd"
       default-lazy-init="true">

    <!-- Enable conf4j -->
    <conf4j:configure/>

    <!-- Find configuration types from the specified package and register in the context. -->
    <conf4j:configuration-scan base-package="com.your.organization.configuration.package"/>

</beans>

As mentioned earlier, <conf4j:configure/> registers several beans with into the context. Each bean has pre-defined name which starts with com.sabre.oss.conf4j. prefix.

The bean com.sabre.oss.conf4j.ConfigurationSource is used for providing configuration values and must implement ConfigurationSource interface. conf4j registers PropertySourceConfigurationSource by default. This configuration source integrates with Environment and all PropertySourcesPlaceholderConfigurers registered in the context (e.g. by <context:property-placeholder .../> or @PropertySource).

If you would like to provide your own implementation of ConfigurationSource (e.g. to fetch configuration values from the database) just declare own bean or an alias with com.sabre.oss.conf4j.ConfigurationSource name.

<bean id="com.sabre.oss.conf4j.ConfigurationSource"
      class="com.your.organization.CustomConfigurationSource">
  <!-- ... -->
</bean>

In general, all conf4j specific bean can be overridden this way.

The bean com.sabre.oss.conf4j.typeConverter is used for converting string representation of the values to appropriate type. By default conf4j registers converter provided by DefaultTypeConverters.getDefaultTypeConverter(). This converter is able to convert all primitive types (and they object counterparts), enumerations any combination of List and Map e.g. Map<String, List<Integer>>.

The bean com.sabre.oss.conf4j.configurationFactory is used for creating configuration instances. conf4j registers JavassistDynamicConfigurationFactory or, if its not on the classpath, it fallbacks to CglibDynamicConfigurationFactory. jaavssist based implementation is preferred because it has significantly better performance. But if you don't like an additional dependency on the classpath, CGLIB based implementation is used (to be precise, repackaged CGLIB version available with Spring Framework is used).

JavassistDynamicConfigurationFactory (and its static counterpart JavassistStaticConfigurationFactory) implementation is provided in the com.sabre.oss.conf4j:conf4j-javassist module. If you would like to use it instead of CglibDynamicConfigurationFactory, make this dependency is included in your project.

Maven

<dependency>
  <groupId>com.sabre.oss.conf4j</groupId>
  <artifactId>conf4j-javassist</artifactId>
  <version>${conf4j.version}</version>
</dependency>

Gradle

dependencies {
  compile "com.sabre.oss.conf4j:conf4j-javassist:$conf4jVersion"
}

Annotation-based Configuration

conf4j provides the following annotations which can be used with Spring Framework and Spring Boot: @EnableConf4j, @ConfigurationScan, @ConfigurationType.

The @EnableConf4j enables conf4j integration and works as <conf4j:configure/> tag.

The @ConfigurationScan scans for the configuration types and its functionality is very similar to <conf4j:configuration-scan> tag.

The @ConfigurationType registers in the context a bean for the configuration type and its functionality is very similar to <conf4j:configuration> tag.

conf4j annotations can be used in Spring Framework configurations classes, i.e. classes annotated or meta-annotated with @Configuration.

The following example shows how to use @ConfigurationScan to find all configuration classes in the package (and sub-packages).

@Configuration
@EnableConf4j
@ConfigurationScan(basePackageClasses = ConnectionConfiguration.class)
public class SampleConfiguration {
}

@Component
public interface ConnectionConfiguration {
   String getUrl();
   // ...
}

If you prefer explicitly listing the configuration types use @ConfigurationType as shown below. In such case using @Component is redundant and can be skipped.

@Configuration
@EnableConf4j
@ConfigurationType(ConnectionConfiguration.class)
@ConfigurationType(UserConfiguration.class)
public class SampleConfiguration {
}

public interface ConnectionConfiguration {
   String getUrl();
   // ...
}

public interface UserConfiguration {
   String getLogin();
   // ...
}

Spring Boot Integration

conf4j integrates with Spring Boot via auto-configuration mechanism based on org.springframework.boot.autoconfigure.EnableAutoConfiguration. To activate it, just add dependency to com.sabre.oss.conf4j:conf4j-spring-boot module:

Maven

<dependency>
  <groupId>com.sabre.oss.conf4j</groupId>
  <artifactId>conf4j-spring-boot</artifactId>
  <version>${conf4j.version}</version>
</dependency>

Gradle

dependencies {
  compile "com.sabre.oss.conf4j:conf4j-spring-boot:$conf4jVersion"
}

Spring Boot finds conf4j during the bootstrap phase and activates it - there is no need to use @EnableConf4j nor <conf4j:configure/> anymore.

@SpringBootApplication
@ConfigurationScan
@PropertySource("classpath:application.properties")
public class SimpleBootApplication implements CommandLineRunner {
    @Autowired
    private ConnectionConfiguration connectionConfiguration;

    public static void main(String[] args) {
        SpringApplication.run(SimpleBootApplication.class, args);
    }

    @Override
    public void run(String... args) {
        System.out.println("url: " + connectionConfiguration.getUrl());
        //...
    }

    @Component
    @Key("connection")
    public interface ConnectionConfiguration {
        String getUrl();
        // ...
    }
}

If you want to use javassist based configuration factories, don't forget to add dependency to com.sabre.oss.conf4j:conf4j-javassist.

Extras

The modules grouped under conf4j-extras provides integration with additional frameworks and data formats, for example YAML.

YAML

conf4j-extras-yaml module provides integration with YAML format.

In order to use it, just add a dependency to com.sabre.oss.conf4j:conf4j-extras-yaml module.

Maven

<dependency>
  <groupId>com.sabre.oss.conf4j</groupId>
  <artifactId>conf4j-extras-yaml</artifactId>
  <version>${conf4j.version}</version>
</dependency>

Gradle

dependencies {
  compile "com.sabre.oss.conf4j:conf4j-extras-yaml:$conf4jVersion"
}

com.sabre.oss.conf4j.yaml.converter.YamlConverter is capable of converting POJO from/to YAML document. By default, this converter can be applied only to the properties which have converter meta-attribute value set to yaml. For conveniences, this attribute can assigned with com.sabre.oss.conf4j.yaml.converter.Yaml annotation (as shown below), but you can use regular @Meta as well.

public interface YamlConfiguration {
    @Yaml
    ComplexType getComplexType();
}

Note: The class converted by YamlConverter must be compliant with JavaBeans specification.

com.sabre.oss.conf4j.yaml.source.YamlConfigurationSource supports reading configuration values from YAML document. Because YAML can be hierarchical it must be flattened to key and value set.

For example:

users:
    admin:
       name: John Smith
       age: 30
       country: USA
    jane:
       name: Jane Doe
       age: 25
       country: PL

is flattened to:

users.admin.name=John Smith
users.admin.age=30
users.admin.country=USA
users.jane.name=Jane Doe
users.jane.age=25
users.jane.country=PL

and:

continents:
    - Asia
    - Africa
    - North America
    - South America
    - Antarctica
    - Europe
    - Australia

is flattened to:

continents[0]=Asia
continents[1]=Africa
continents[2]=North America
continents[3]=South America
continents[4]=Antarctica
continents[5]=Europe
continents[6]=Australia

JSON

conf4j-extras-json module provides integration with JSON format.

In order to use it, just add a dependency to com.sabre.oss.conf4j:conf4j-extras-json module.

Maven

<dependency>
  <groupId>com.sabre.oss.conf4j</groupId>
  <artifactId>conf4j-extras-json</artifactId>
  <version>${conf4j.version}</version>
</dependency>

Gradle

dependencies {
  compile "com.sabre.oss.conf4j:conf4j-extras-json:$conf4jVersion"
}

com.sabre.oss.conf4j.json.converter.JsonConverter is capable of converting POJO from/to JSON document. By default, this converter can be applied only to the properties which have converter meta-attribute value set to json. For conveniences, this attribute can assigned with com.sabre.oss.conf4j.json.converter.Json annotation (as shown below), but you can use regular @Meta as well.

public interface JsonConfiguration {
    @Json
    ComplexType getComplexType();
}

com.sabre.oss.conf4j.json.source.JsonConfigurationSource supports reading configuration values from JSON document. Because JSON can be hierarchical it must be flattened to key and value set.

For example:

{
  "teams": {
    "ferrari": {
      "engine": "ferrari",
      "score": 522,
      "private": false
    },
    "williams": {
      "engine": "mercedes",
      "score": 83,
      "private": true
    }
  }
}

is flattened to:

teams.ferrari.engine=ferrari
teams.ferrari.score=522
teams.ferrari.private=false
teams.williams.engine=mercedes
teams.williams.score=83
teams.williams.private=true

and:

{
  "months": {
    "odd": [
      "January",
      "March",
      "May",
      "July",
      "August",
      "October",
      "December"
    ],
    "even": [
      "April",
      "June",
      "September",
      "November"
    ],
    "both": "February"
  }
}

is flattened to:

months.odd[0]=January
months.odd[1]=March
months.odd[2]=May
months.odd[3]=July
months.odd[4]=August
months.odd[5]=October
months.odd[6]=December
months.even[0]=April
months.even[1]=June
months.even[2]=September
months.even[3]=November
months.both=February

JAXB

conf4j-extras-jaxb module provides integration with XML format, using JAXB library.

In order to use it, just add a dependency to com.sabre.oss.conf4j:conf4j-extras-jaxb module.

Maven

<dependency>
  <groupId>com.sabre.oss.conf4j</groupId>
  <artifactId>conf4j-extras-jaxb</artifactId>
  <version>${conf4j.version}</version>
</dependency>

Gradle

dependencies {
  compile "com.sabre.oss.conf4j:conf4j-extras-jaxb:$conf4jVersion"
}

com.sabre.oss.conf4j.yaml.converter.JaxbConverter is capable of converting POJO from/to XML document.

The class converted by JaxbConverter must be annotated with @XmlRootElement, for example:

@XmlRootElement(name = "book")
public class Book {
    private String name;
    private String author;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name){
        this.name = name;
    }
    
    public String getAuthor() {
        return author;
    }
    
    public void setAuthor(String author){
        this.author = author;
    }
}