Config Objects

Config Objects - a framework for dynamically implementing "config object" interfaces that are backed by commons-configuration Configuration objects.

License

License

Categories

Categories

Net config Application Layer Libs Configuration
GroupId

GroupId

net.peachjean.confobj
ArtifactId

ArtifactId

config-objects
Last Version

Last Version

0.4.0
Release Date

Release Date

Type

Type

jar
Description

Description

Config Objects
Config Objects - a framework for dynamically implementing "config object" interfaces that are backed by commons-configuration Configuration objects.
Source Code Management

Source Code Management

https://github.com/peachjean/config-objects

Download config-objects

How to add to project

<!-- https://jarcasting.com/artifacts/net.peachjean.confobj/config-objects/ -->
<dependency>
    <groupId>net.peachjean.confobj</groupId>
    <artifactId>config-objects</artifactId>
    <version>0.4.0</version>
</dependency>
// https://jarcasting.com/artifacts/net.peachjean.confobj/config-objects/
implementation 'net.peachjean.confobj:config-objects:0.4.0'
// https://jarcasting.com/artifacts/net.peachjean.confobj/config-objects/
implementation ("net.peachjean.confobj:config-objects:0.4.0")
'net.peachjean.confobj:config-objects:jar:0.4.0'
<dependency org="net.peachjean.confobj" name="config-objects" rev="0.4.0">
  <artifact name="config-objects" type="jar" />
</dependency>
@Grapes(
@Grab(group='net.peachjean.confobj', module='config-objects', version='0.4.0')
)
libraryDependencies += "net.peachjean.confobj" % "config-objects" % "0.4.0"
[net.peachjean.confobj/config-objects "0.4.0"]

Dependencies

compile (13)

Group / Artifact Type Version
org.javassist : javassist jar 3.16.1-GA
org.apache.commons : commons-lang3 jar 3.1
commons-io : commons-io jar 2.4
commons-configuration : commons-configuration jar 1.8
commons-beanutils : commons-beanutils jar 1.8.3
commons-cli : commons-cli jar 1.2
org.apache.commons : commons-collections4 jar 4.0
javax.inject : javax.inject jar 1
javax.validation : validation-api jar 1.0.0.GA
org.apache.bval : bval-jsr303 jar 0.5
org.antlr : stringtemplate jar 4.0.2
net.peachjean.tater : tater-utils jar 0.2
org.joda : joda-convert jar 1.6

test (7)

Group / Artifact Type Version
org.ow2.asm : asm-util jar 4.1
joda-time : joda-time jar 2.3
junit : junit jar 4.11
org.hamcrest : hamcrest-core jar 1.3
org.hamcrest : hamcrest-library jar 1.3
org.easymock : easymock jar 3.1
net.peachjean.tater : tater-test-harness jar 0.2

Project Modules

There are no modules declared in this project.

Build Status Config Objects

This project is part of an evoluation over multiple iterations in my quest to improve the state of configuration in object-oriented Java applications.

Problems Addressed

  1. Type Safety Configuration parameters are not provided to the classes that use them in a type-safe manner. This means that we end up with different parsing mechanisms and inconsistent error messages across our code base.
  2. Validation Oftentimes, in addition to a type requirement, properties also must pass certain validation criteria. Perhaps we are looking for a number from 1 to 10, or a 6-10 character string.
  3. Consistent Error Reporting When the code for reading in, parsing, and validating properties is scattered across our codebase, we end up with a multitude of error messages and states for the exact same sort of failure.
  4. Discoverability If an application has users, then users need to configure it. This means that we need to document our configuration. I frequently see undocumented parameters, out of date documentation, and a lack of type or validation information for configuration parameters.
  5. Dynamicism The majority of configuration patterns that I see don't have any allowance for configuration to change during the application's lifecycle. This means that we end up with hacks, like continually parsing from Properties objects or reloading static services.

There are three common patterns that I tend to see for object configuration.

  1. Pass a Properties or Configuration object to a class's constructor. The class then parses the properties that it is interested in and uses them.
  2. A class defines setters for any configuration parameters that it is interested in. An external framework or utility parses configuration files and invokes these setters for relevant properties.
  3. A dependency injection framework is used where every value in a properties file is bound as a bean and some form of autowiring is used to inject those parameters into the classes that need them.

Approach

This project attempts to define two major components.

  1. Pattern A pattern for defining configuration objects. a. Interface The basic approach is that a user will define an interface of getter methods. Each method represents one parameter. The types for these methods are typically simple types, supporting the valueOf() convention, primitives, collections, or other configuration object types. These interfaces should be annotated with @ConfigObject. b. Validation Each getter method may define JSR-303 validation annotations. c. Documentation A basic description of the parameter should be provided in the javadoc for the getter method. d. Defaults An abstract nested static class named Defaults may be defined for the interface. If defined, it should implement any getters which should have defaults and return those defaults. Sometimes, default values depend on the environment. The Defaults class may specify a constructor that gets passed the relevant environment objects. JSR-330 @Qualifier annotations may be used on these constructor parameters.
  2. Implementation Support Utilities for working with ConfigObject interfaces. a. Introspection A basic utility providing visitor-style introspection for a ConfigObject interface. b. Implementation A utility for dyamically implementing ConfigObject interfaces using commons-configuration Configuration objects as the backing store. The entry point into this is the ConfigObjectFactory and the DefaultConfigObjectFactory implementation.

Basic Use

The application developer defines a configuration object as an interface of getter methods. He also defines an inner static abstract class of this interface, that implements this interface, named 'Defaults'. The return value of any methods implemented in 'Defaults' will be treated as default values. Any methods that do not have default values will be treated as required. Here is an example:

@ConfigObject
public interface MyConfig {
  /* a parameter named 'value1' */
  String getValue1();
  /* a parameter named 'value2' */
  String getValue2();
  /* a parameter named 'intValue' */
  Integer getIntValue();

  public static abstract class Defaults implements MyConfig {
    public String getValue2() {
      return "second value";
    }
    public Integer getValue() {
      return 42;
    }
  }
}

This class could map the properties file (myconfig.properties):

value1=My Configured Value
intValue=88

The developer would then create a ConfigObjectFactory and create instances of MyConfig from it:

ConfigObjectFactory confObjFactory = new DefaultConfigObjectFactory();
Configuration config = new PropertiesConfiguration(new File("myconfig.properties"));
MyConfig myConfig = confObjFactory.create(config, MyConfig.class);
// alternatively, an instantiator can be created
Instantiator<MyConfig> configFunction = confObjFactory.createGenerator(MyConfig.class);
MyConfig myConfig2 = configFunction.instantiate(config);

// prints "My Configured Value"
System.out.println(myConfig.getValue1());

// prints "second value"
System.out.println(myConfig.getValue2());

// prints "42'
System.out.println(myConfig.getIntValue());

Non-Obvious Needs / Future Work

  1. Field-type supporting exposing sets of properties as key/value pairs - used for supporting embedding configuration that will be passed to other libraries
  2. Subtype of field B determined by field A OR subtype of field A determined by a key in A - this supports configuration that defines a specific implementation to be used when specific implementations have different configuration objects.
  3. Support changes between versions of itsco interfaces
    • adding a field, removing a field, changing a type, changing a name, changing cardinality
    • testing that old versions are still compatible
  4. Discoverability - shall be able to dynamically describe available configuration
  5. Listeners on configuration change - some configuration will be used to create other objects - when the configuration changes, these objects will need to be modified
  6. Should support JSR-303 annotations on getters.
  7. Annotation processor for compile-time validation of @ConfigObject-annotated interfaces.

Release Instructions

mvn release:prepare
mvn release:perform
net.peachjean.confobj

PeachJean Software

Versions

Version
0.4.0
0.3.0
0.2.0
0.1.0