Skip to content

Configuration Mapping

elandau edited this page Sep 24, 2013 · 21 revisions

As part of initializing an object, Governator can assign configuration values to object fields. To use this feature, you must bind an instance that implements ConfigurationProvider in the bootstrap phase or annotation a ConfigurationProvider with @AutoBindSingleton. Governator provides a few default implementations to use. Then, annotate fields with @Configuration.

ConfigurationProvider

The ConfigurationProvider is used to provide configuration values from configuration names as specified by @Configuration annotations. Governator bundles three implementations that you can use:

Class Description
SystemConfigurationProvider Returns System properties via System.getProperty()
PropertiesConfigurationProvider Returns values from a Properties object
ArchaiusConfigurationProvider Returns values using Netflix’s Archaius

Additionally, CompositeConfigurationProvider can be used to chain ConfigurationProviders together.

There are two ways to identify the ConfigurationProvider to Governator:

  • Annotate the ConfigurationProvider with @AutoBindSingleton (making sure it is in the set of packages passed to the classpath scanner).
  • Bind the ConfigurationProvider in the bootstrap module.

Here is an example of binding in the bootstrap module:

LifecycleInjector.builder()
    .withBootstrapModule
    (
        new BootstrapModule()
        {
            @Override
            public void configure(Binder binder, RequiredAssetBinder requiredAssetBinder)
            {                            
               binder.bindConfigurationProvider().to(MyProvider.class);
            }
        }
    )

@PreConfiguration

You can annote methods you want called prior to configuration mapping. E.g.:

@PreConfiguration
public void myConfigurationLoader()
{
   // gets called prior to configuration mapping and prior to PostConstruct methods
}

@Configuration

Fields annotated with @Configuration will get assigned configuration values based on the type of the field and the value set in the annotation. Here’s an example:

@Configuration("configs.qty.things")
private int   numberOfThings = 10;

For the above, Governator will call ConfigurationProvider.has(“configs.qty.things”) (note the argument is actually a ConfigurationKey – see below). If it returns false, “numberOfThings” will stay set to 10 – i.e. 10 is the default value. If it returns true, “numberOfThings” is assigned the value returned by ConfigurationProvider.getInteger(“configs.qty.things”). If “numberOfThings” was a String, ConfigurationProvider.getString(“configs.qty.things”) would be called, etc.

Dynamic Configuration

Some configuration parameters may change at runtime and it would be desirable to have the new value picked up immediately. To specify a dynamic configuration simply define the field as a Supplier. Here’s an example,

@Configuration("configs.qty.things")
private Supplier<Integer>   numberOfThings = Suppliers.ofInstance(10);

Governator will bind a supplier to the internal dynamic mechanism provided by the ConfigurationProvider. Note that special consideration must be given to properties that have yet to be specified at the configuration binding phase but that can be specified later at runtime. For example, the above property ‘configs.qty.things’ may not exist in the configuration when the object is configured but may be defined in the future. To make the ConfigurationProvider aware of this property you must provide it with a property ownership policy. The ownership policy tells Governator that certain properties belong to a provider regardless of whether they exist or not. Several policies are provided and can be specified on the ArchaiusConfigurationProvider as follows,

 ArchaiusConfigurationProvider.builder()
     .withOwnershipPolicy(ConfigurationOwnershipPolicies.ownsAll())
     .build()

ConfigurationKey

Each method of the ConfigurationProvider takes a ConfigurationKey as an argument as opposed to a simple string. The ConfigurationKey enables a convention for providing variables within the @Configuration value. Governator will parse the value into tokens. Tokens can be either a literal value or a variable substitution. Variables are specified as ${variable-name}. The ConfigurationKey has a method to apply variables and return the applied key:

public String getKey(Map<String, String> variableValues);

The variableValues maps a variable name to its value. Some examples:

Value variableValues Actual Key
${stage}.server “stage” → “test” test.server
password.${user}.${month} “user” → “jz”, “month” → “oct” password.jz.oct

ConfigurationKey also has a method to get the list of variable names present in the @Configuration value.

Configuration Documentation

Governator provides a documentation mechanism for configuration. When LifecycleManager.start() is called (after configuration assignments) Governator will log detailed information about the configuration values used in your application. In support of this, you should specify descriptive documentation in your @Configuration annotations via the optional documentation property. E.g.

@Configuration
(
    value = "configs.qty.things", 
    documentation = "This controls the number of things used."
)
private int   numberOfThings = 10;

The configuration logging looks like this:

PROPERTY     FIELD               DEFAULT    VALUE           DESCRIPTION           
prop.name.1  com.foo.Bar#aBool   false      true            This is a boolean     
other.prop   com.foo.Bar#xyz     something  another thing   This does something

Configuration type mismatch

Governator uses ConfigurationProvider’s various methods to obtain a value which matches with the target field data type. However, in cases where the property’s value is wrongly configured, such that the value can not be converted to the target data type, Governator initialization will fail. This behavior is suitable for most of the cases where the configured field is critical for the functioning of the module. However, in cases where the configured field can work well with a default value, this behavior is disruptive. In such a case, in order to ignore the type mismatch, one can set the property “ignoreTypeMismatch” to true. This will ignore any type mismatch that occurs during configuration.

. E.g.

@Configuration
(
    value = "configs.qty.things", 
    documentation = "This controls the number of things used.",
    ignoreTypeMismatch = true
)
private int   numberOfThings = 10;

In the above case, if the value of the property “configs.qty.things” is set to “foo”, the property value will be ignored. The field numberOfThings will remain set to 10 i.e. the default value above.

Governator determines that a failure to retrieve a value was a type mismatch, if the ConfigurationProvider throws any of the following exceptions, upon property retrieval:

  1. java.lang.IllegalArgumentException
  2. org.apache.commons.configuration.ConversionException