Carlos Chacin

Software Engineering Experiences


Java 8 Type Safe Configuration with default methods

Posted at December 21, 2015 /

There is a common requirement in most of the Java/JavaEE projects related to how differentiate environment configurations like development, test, QA, production, etc. for that reason there are a lot of frameworks or libraries to resolve that:

Even there is a Java Specification Request (JSR) Proposal:

How those libraries resolve the problem

Most of those libraries require to have an injection point(s) for the configurations and also the configurations source(s), something like this:

dev.properties:

host=localhost
port=5432
schema=public

prod.properties:

host=production.com
port=5432
schema=public

ConfigurationResolver.java:

class ConfigurationResolver implements SomeLibraryInterface {
  @Override
  public String resolvePropertyFilename() {
    // probably do some trick to load a global/base configuration
    // if is not provided for the library
    return String.format("%s.properties", System.getProperty("MY_ENV", "dev"));
  }
}

MyType.java:

class MyType {
  @Configuration(key = "host")
  String host;
  @Configuration(key = "port")
  int port;
  @Configuration(key = "schema", defaultValue = "public")
  String schema;
}

Notice that all the granularity is only needed because lack of support for custom types

Besides that, with the new Java 8 capabilities, specifically with default methods and some utilities classes is easy to get this behavior.

With Java 8 default methods

DefaultConfiguration.java:

interface DefaultConfiguration {
  default DataSource datasource {
    // create the DS with host, port and schema
    return new MyDataSource("localhost", 5432, "public");
  }
}

DevConfiguration.java:

class DevConfiguration implements DefaultConfiguration {
  // No need to override methods if they are the same
}

ProdConfiguration:

class ProdConfiguration implements DefaultConfiguration {
  default DataSource datasource {
    return new MyDataSource("production.com", 5432, "public");
  }
}

ConfigurationFactory.java:

abstract class ConfigurationFactory {
    private static final Map<String, DefaultConfiguration> configurations = new HashMap<>();

    static {
        configurations.put("development", new DevConfiguration());
        configurations.put("production", new ProdConfiguration());
    }

    public static DefaultConfiguration configuration() {
        return configurations.getOrDefault(System.getProperty("MY_ENV"), new DevConfiguration());
    }
}

Notice that you can use an enumeration instead a map as a configuration factory.

Pros

Cons

source code