Java 8 Type-Safe Configuration with default methods
Carlos Chacin ☕👽
Posted on November 15, 2019
There is a common requirement in most of the Java/JavaEE projects related to how to differentiate environment configurations like development, test, QA, production, etc. for that reason there are a lot of frameworks or libraries to resolve that:
- DeltaSpike Configuration
- Apache Commons Configuration
- Sabot
- Apache Tamaya
- Dropwizard Configuration
- Jackson Dataformat YAML
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 source of the configuration(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 of lack of support for custom types
Besides that, with the new Java 8 capabilities, specifically with default methods and some utility classes are 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 of a map as a configuration factory.
Pros
- Typesafe configurations
- Support for all kinds of types not only
String
,Date
or primitives. you can specify or exampleDataSource
,TimeUnits
,JedisPool
, etc. - No conversion/mapping from strings to types
- No more
.properties
,.xml
or.yml
files - Possibility to use other sources to fill the properties like properties files, database, rest API's, etc.
- No library/framework/dependency required in your app
Cons
- Changes in the configuration require to compile/deploy (this shouldn’t be a problem in the 99% of the cases)
Posted on November 15, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.