- Simple configuration of application for different environments (dev, test, staging, prod, etc.)
- Ease of modification of characteristics without requiring rebuild (depending on the way a project is built/assembled, this could include on-the-fly configuration of properties without requiring a restart)
- Central grouping of properties for the application which can ease support and customization efforts
Having mainly worked with xml and properties files, I recently worked on a project where one of the developers wanted to use the DatabaseConfiguration class and keep the properties in a database table. This aproach has several advantages, such as quick and easy search capability (hello SQL!), centralization of properties and live updates. I figured, "great, since the class exists in commons-configuration I'm sure they added the ability to define the source in the configuration definition file". Alas, I was mistaken. Again. You'd think I'd be getting used to this...
Thankfully, the DefaultConfigurationBuilder is extensible to deal with exactly this situation. Well, more or less. The commons group apparently has the intention to make the configuration definition file easily extensible in the future, but for now this takes a bit of tweaking. So, without further ado...
Extending the commons-configuration configuration definition file to handle DatabaseConfiguration configurations
My intention was to allow the definition of a database configuration in the definition file as follows:
<database jndi="jdbc/dataSourceName" table="CONFIG_TABLE" keyColumn="NAME" valueColumn="VALEUR"/>
In order to allow this definition, we first need to create a bean to handle the definition node:
public class DatabaseConfigDef {
private String jndi;
private String table;
private String keyColumn;
private String valueColumn;
public DatabaseConfigDef() {
super();
}
public DatabaseConfigDef(String jndi, String table,
String keyColumn, String valueColumn) {
super();
this.jndi = jndi;
this.table = table;
this.keyColumn = keyColumn;
this.valueColumn = valueColumn;
}
// GETTERS AND SETTERS
.
.
.
}
private String jndi;
private String table;
private String keyColumn;
private String valueColumn;
public DatabaseConfigDef() {
super();
}
public DatabaseConfigDef(String jndi, String table,
String keyColumn, String valueColumn) {
super();
this.jndi = jndi;
this.table = table;
this.keyColumn = keyColumn;
this.valueColumn = valueColumn;
}
// GETTERS AND SETTERS
.
.
.
}
Now we need to implement the ConfigurationProvider for our node:
public class DatabaseConfigurationProvider
extends ConfigurationProvider {
public DatabaseConfigurationProvider(String configClassName)
{
super(DatabaseConfiguration.class.getName());
}
public DatabaseConfigurationProvider(Class configClass) {
super(DatabaseConfiguration.class);
}
public DatabaseConfigurationProvider() {
super();
}
@Override
public AbstractConfiguration getConfiguration(
ConfigurationDeclaration decl) throws Exception {
DatabaseConfigDef def = (DatabaseConfigDef)createBean(
DatabaseConfigDef.class, decl, null);
DataSource ds = (DataSource)new InitialContext().lookup(
def.getJndi());
return new DatabaseConfiguration(ds, def.getTable(),
def.getKeyColumn(), def.getValueColumn());
}
}
extends ConfigurationProvider {
public DatabaseConfigurationProvider(String configClassName)
{
super(DatabaseConfiguration.class.getName());
}
public DatabaseConfigurationProvider(Class configClass) {
super(DatabaseConfiguration.class);
}
public DatabaseConfigurationProvider() {
super();
}
@Override
public AbstractConfiguration getConfiguration(
ConfigurationDeclaration decl) throws Exception {
DatabaseConfigDef def = (DatabaseConfigDef)createBean(
DatabaseConfigDef.class, decl, null);
DataSource ds = (DataSource)new InitialContext().lookup(
def.getJndi());
return new DatabaseConfiguration(ds, def.getTable(),
def.getKeyColumn(), def.getValueColumn());
}
}
And now we need to define this provider when we initialise the config. I usually do this in a helper that is called during the loading of the application (ie. in a startup servlet or the constructor of an ejb):
CombinedConfiguration config = new CombinedConfiguration();
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
DefaultConfigurationBuilder.ConfigurationProvider provider = new DatabaseConfigurationProvider();
builder.addConfigurationProvider("database", provider);
builder.setFile(masterConfigFile);
CombinedConfiguration cc = builder.getConfiguration(true);
config.addConfiguration(cc);
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
DefaultConfigurationBuilder.ConfigurationProvider provider = new DatabaseConfigurationProvider();
builder.addConfigurationProvider("database", provider);
builder.setFile(masterConfigFile);
CombinedConfiguration cc = builder.getConfiguration(true);
config.addConfiguration(cc);
And that's it - now our database configuration source is loaded along with the rest of the configuration sources. A couple of things to note:
- I use the jndi name for a JDBC DataSource in the definition file. Thus, we must be sure that the jndi tree has been initialised before calling the load for our configuration.
- Since the DatabaseConfiguration constructor uses a DataSource object, if we wanted to avoid using jndi (eg. by specifying the connection parameters for the database in the definition file) we would need to modify our DatabaseConfigurationProvider class to build the DataSource from the connection parameters.