Kohei Nozaki's blog 

Manage application config with Commons Configuration


Posted on Wednesday Feb 26, 2014 at 01:53PM in Technology


Environment

  • Commons Configuration 1.10
  • WildFly 8.0.0.Final
  • Oracle JDK7u51

Why go this way?

Various portable ways are available for manage configurations of the Java EE application. such as:

  • Configuration table in DB
    • I guess it's not bad, but I feel that is bit tedious work to coding.
    • There's a way to use this mechanism via Commons Configuration, but I haven't tried it yet.
  • Custom JNDI resources
    • I thought that it's most efficient at first, but negative points here:
      • Badness of lookup performance.
      • Editing JNDI resources is very annoying.

So sometimes I'm thinking of any better way, and I found Commons Configurations[1]. notable capabilities of Commons Configuration are:

  • Simple API
  • Auto-reloading and saving mechanism provided
  • Support of various formats and placement styles

So I'd try it.

Prepare sample project and related resources

Create a system property

  • Create a system property which specifies the path of configuration file.
  • I'm using WildFly so go with jboss-cli:
[standalone@localhost:9990 /] /system-property=app.config:add(value=/Users/kyle/app.properties)
{"outcome" => "success"}
[standalone@localhost:9990 /] 

Create the properties file

  • Location is same as “app.config” system property.
hoge=fuge

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.nailedtothex</groupId>
    <artifactId>config</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.10</version>
        </dependency>
    </dependencies>
</project>

Create an application-scoped bean

package org.nailedtothex.config;

import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;

@Named
@ApplicationScoped
public class AppConfigBean {

    private Configuration config;

    @PostConstruct
    void init() throws ConfigurationException {
        String path = System.getProperty("app.config");
        PropertiesConfiguration propConfig = new PropertiesConfiguration(path); 
        propConfig.setReloadingStrategy(new FileChangedReloadingStrategy());
        config = propConfig;
    }

    public Configuration getConfig(){
        return config;
    }
}
  • If you want to read the config at startup of application, I recommend that use of @javax.ejb.Singleton and @javax.ejb.Startup[2].

Create a servlet

  • Get a configuration variable from singleton bean.
package org.nailedtothex.config;

import java.io.IOException;
import java.io.PrintWriter;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns = "/*")
public class HelloServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Inject
    private AppConfigBean bean;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try (PrintWriter pw = response.getWriter()) {
            pw.write(bean.getConfig().getString("hoge"));
        }
    }
}

Access via browser

  • It needs only very small and clean code.

Check runtime reloading

Edit app.properties:

kyle-no-MacBook:~ kyle$ echo hoge=FUGE > app.properties
kyle-no-MacBook:~ kyle$ 

Reload browser:

  • I just reloaded the browser and I didn't do any other operations, but config was updated immediately.

Collaboration with CDI

  • There's a way to inject properties to CDI managed bean at [2]. that must be useful.

XMLConfiguration

  • XMLConfiguration is good for hierarchical configuration.
  • It depended on Commons Collection so don't forget to add the dependency.

pom.xml

        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.10</version>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>

XmlTest.java

package org.nailedtothex.config;

import java.util.Iterator;
import java.util.List;

import org.apache.commons.configuration.XMLConfiguration;

public class XmlTest {

    public static void main(String[] args) throws Exception {
        XMLConfiguration config = new XMLConfiguration();
        config.load(XmlTest.class.getResourceAsStream("/config.xml"));

        String backColor = config.getString("colors.background");
        String textColor = config.getString("colors.text");
        String linkNormal = config.getString("colors.link[@normal]");
        String defColor = config.getString("colors.default");
        int rowsPerPage = config.getInt("rowsPerPage");
        List<Object> buttons = config.getList("buttons.name");

        System.out.println(backColor);
        System.out.println(textColor);
        System.out.println(linkNormal);
        System.out.println(defColor);
        System.out.println(rowsPerPage);
        System.out.println(buttons);

        System.out.println();

        Object o;
        Iterator it = config.getKeys("colors.");
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
}

config.xml

<?xml version="1.0" encoding="UTF-8"?>
<gui-definition>
  <colors>
    <background>#808080</background>
    <text>#000000</text>
    <header>#008000</header>
    <link normal="#000080" visited="#800080"/>
    <default>${colors.header}</default>
  </colors>
  <rowsPerPage>15</rowsPerPage>
  <buttons>
    <name>OK,Cancel,Help</name>
  </buttons>
  <numberFormat pattern="###\,###.##"/>
</gui-definition>

Log

#808080
#000000
#000080
#008000
15
[OK, Cancel, Help]

colors.background
colors.text
colors.header
colors.link[@normal]
colors.link[@visited]
colors.default

References

  1. Commons Configuration - Java Configuration API
  2. CDIでアプリケーション設定をインジェクション - 見習いプログラミング日記
  3. Apache commonsが便利な件(commons-configuration編) - 都元ダイスケ IT-PRESS
  4. Commons Configuration - Hierarchical configurations and XML Howto



No one has commented yet.

Leave a Comment

HTML Syntax: NOT allowed