Optify Tech Blog

Featured Story

Adding namespaces to Java properties

Posted by Kamil Jiwa on February 3rd, 2012

Many software systems support running in multiple runtime environments — for example, development, staging, and production. Often, the software is written to be agnostic of its environment. Environment-specific settings are stored in configuration files such as config.common.properties, config.development.properties, etc. As the software becomes larger and more complex, the configuration files follow suit, making the system and runtime environment harder to understand and debug.

We have this challenge at Optify: our products include nearly 80 separate configuration files containing over 2,700 lines of property definitions. We wanted to improve our configuration management but we also did not want to introduce a solution that was highly intrusive to our code base. After some thought, we decided that a good solution would involve adding namespace support to Java’s property file syntax.

To illustrate the concept, consider a use case common to most Java projects — logging. In a development environment we might want verbose logs written to the console on stderr, while in production we might want only warnings and errors written to files that are rotated every hour. For example:

log4j.development.properties

log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout

log4j.production.properties

log4j.rootLogger=WARN, A1
log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A1.datePattern='.'yyyy-MM-dd-HH
log4j.appender.A1.file=application.log
log4j.appender.A1.layout=org.apache.log4j.PatternLayout

This results in 2 configuration files with 8 lines of property definitions.

In a namespace-aware world, our configuration might instead look like this:

log4j.properties With Namespaces

*.log4j.rootLogger=DEBUG, A1
*.log4j.appender.A1=org.apache.log4j.ConsoleAppender
*.log4j.appender.A1.layout=org.apache.log4j.PatternLayout

production.log4j.rootLogger=WARN, A1
production.log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
production.log4j.appender.A1.datePattern='.'yyyy-MM-dd-HH
production.log4j.appender.A1.file=application.log

In this simple example, the namespacing gives us a small improvement, resulting in 1 configuration file and 7 lines of property definitions. Notice the “*” representing the default namespace from which all other namespaces inherit their values.

We decided to try integrating this solution with one of our services. The service had 4 configuration files with 195 lines of property definitions and 29 source files with nearly 6,000 lines of code. The results were promising, with a reduction of over 50% in lines of configuration. The footprint on our codebase was also minimal. Here is a diffstat:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/config.development.properties |   55 ------
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/config.production.properties  |   52 ------
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/config.properties             |   80 ++++++++++
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/config.test.properties        |   43 -----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/config.unittest.properties    |   45 -----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.java                    |    9 +
6 files changed, 89 insertions(+), 195 deletions(-)

This is a pretty great improvement! We went from 195 lines of property definitions to 80 and changed less than 10 lines of code in 1 source file.

So what is actually required to enable namespace support? Our implementation was a rather simple extension of java.util.Properties. The base class does the hard work of loading and parsing the file, leaving us with the job of parsing the namespace and key. For backward compatibility, we ignored lines that do not appear to have an associated namespace, but you may wish to be more strict by throwing an exception.

OptifyConfigProperties.java

package com.optify.config;

import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

public class OptifyConfigProperties extends Properties {
    private static final long serialVersionUID = 1L;
    private static final String _DEFAULT_NAMESPACE = "*";

    private String _namespace;
    private Properties _properties;

    public OptifyConfigProperties(String namespace) {
        super();
        _namespace = namespace;
        _properties = new Properties();
    }

    public OptifyConfigProperties(String namespace, Properties p) {
        this(namespace);
        Iterator i = p.entrySet().iterator();
        while (i.hasNext()) {
            Map.Entry entry = (Map.Entry) i.next();
            setProperty((String) entry.getKey(), (String) entry.getValue());
        }
    }

    public synchronized Object setProperty(String key, String value) {
        String[] parts = key.split("\\.");
        if (parts.length < 2) {
            return null;
        }

        String ns = parts[0];
        String k = key.substring(ns.length() + 1);
        if (ns.equals(_namespace)) {
            _properties.put(k, value);
            return put(k, value);
        } else if (ns.equals(_DEFAULT_NAMESPACE) && !_properties.containsKey(k)) {
            return put(k, value);
        }

        return null;
    }
}

Once this file is available in our project, we can load our namespaced configuration files when our system initializes like so:

InputStream in = getClass().getClassLoader().getResourceAsStream("log4j.properties");
Properties p = new OptifyConfigProperties("production");
p.load(in);

We’re excited to share our solution with the community. We have found it to be a lightweight and powerful extension of the native Java properties format and hope that others benefit from it as much as we have.

More Stories

  1. Optify Logo

    Configuring Active MQ for high performan…

    Posted by Thairu in Uncategorized
    Optify has been using ActiveMQ (AMQ) since inception as the initial collection point for page view pings from our customer websites. We have used...
    10/15/11
    No Comments
  2. Building a Client-Side Alert Feed with J…

    Posted by Nathan Harkenrider in Javascript, UI
    The Optify application is designed to provide real-time insights to our customers about their website visitors, leads and keywords.  The real-time information about visitors,...
    9/22/11
    1 Comment
  3. Optify Logo

    Scaling the engineering team

    Posted by Chris Hundley in Agile, Scrum, Team
    For companies lucky enough to experience explosive growth there comes a time to scale individual teams within the organization.  For Optify’s engineering team that...
    9/15/11
    No Comments
  4. Building a server cluster with LinkedIn&…

    Posted by Thairu in Lead Scoring, Load Balancing, Scale, Server Architecture
    One way to solve performance problems and to get rid of single points of failures is to scale horizontally. You go from having one...
    9/12/11
    No Comments
  5. Optify Logo

    Adding Structure to Your JavaScript Appl…

    Posted by Nathan Harkenrider in Framework, Javascript
    I’ve recently been spending time evaluating several technologies that Optify could leverage to build the user interface for a couple of exciting new features....
    9/9/11
    1 Comment
  6. Optify Logo

    Welcome!

    Posted by Chris Hundley in Uncategorized
    Welcome to the Optify technology and engineering blog. Here you will find discussions focused on software architecture, code, industry trends, and other topics of...
    9/7/11
    No Comments
more