Reinventing java properties or using “Owner” library

Most of java developers use property files in their daily work. Basically we need them to configure our applications, to make our code more flexible and reusable. Unfortunately Java API for properties is very far away from perfect also most of developers are used to writing lots of the repetitive and routine code when accessing properties.

What do I mean? Let’s consider the following example:

foo.properties

db.timeout=10

Bar.java

// static fields
private static final String TIMEOUT_KEY = "db.timeout";
private static final int DEFAULT_TIMEOUT = 10;
..
// within some method
String timeOutString = properties.getProperty(TIMEOUT_KEY);
Integer timeOut;
try {
    if(timeOutString != null &&  !timeOutString.isEmpty()) {
        timeOut = Integer.parseInt(timeOutString);
    } else {
        timeOut = DEFAULT_TIMEOUT;
    }
} catch (NumberFormatException e){
    timeOut = DEFAULT_TIMEOUT;
}
// using timeout

So in the example above we can see classical issues with accessing properties.

Issue #1 – Duplication of converting logic

try {
    timeOut = Integer.parseInt(timeOutString);
} catch (NumberFormatException e){
    timeOut = DEFAULT_TIMEOUT;
}

This try-catch is going to be placed in every place where you want to convert String to Integer, which leads to duplication. So if Integer property was changed to Long you would have to change it in all places where you use it.

Issue #2 – Duplication of fallback to default value

if(timeOutString != null && !timeOutString.isEmpty()) {
    timeOut = Integer.parseInt(timeOutString);
} else {
    timeOut = DEFAULT_TIMEOUT;
}

Unfortunately, I’ve seen many times a piece of code like one written above. Sometimes property value is being checked just for null, sometimes for emptiness, sometimes fallback value is used after unsuccessful conversion. In all cases same mechanism:

no effective value – use default

Issue #3 – Duplication of constants

private static final String TIMEOUT_KEY = "db.timeout";
private static final int DEFAULT_TIMEOUT = 10;

I guess most of you have ever seen such constants. Unfortunately, I faced many situation when constants were not extracted to a separate class and just were copy-pasted. So when we have service-A which uses property1,property2 and service-B which uses property1, property2 we would have 16 string constants.

  • 4 constants in service-A impl (prop1_key, prop1_default, prop2_key,prop2_default)
  • 4 constants in service-A test (prop1_key, prop1_default, prop2_key,prop2_default)
  • same for service-B impl
  • same for service-B test

Can you imagine how ineffective it is?

At this moment you may think that I’ve never heard about properties support in the spring framework or in Apache framework , but it is not true. I am aware of them and I am using them, but still they don’t solve issues mentioned above.  @Value annotation allows you to inject property value on context-startup and there is no ease way to reload injected properties ( I am aware only about spring-cloud). Apache framework provides lots of methods for conversion and mechanism of fallback to default, but still we need to duplicate constants which sometimes looks stupid, especially if you copy-them in your Junit test.

Here is  the moment when Owner framework enters the game. Basically it makes properties handling as ease as possible. This is how example above looks like using Owner:

    import org.aeonbits.owner.Config;
    public interface AppConfig extends Config {
        @DefaultValue("10")
        @Key("db.timeout")
        int dbTimeout();
    }
// somewhere in a method
ServerConfig cfg = ConfigFactory.create(ServerConfig.class);
int timeout = cfg.dbTimeout();
//using timeout

Is not that cool? The general idea is that we are accepting convention over configuration which allows us to write as less code as possible. Also annotations in the library lead us to more declarative style rather than imperative.

Instead of describing Owner documentation , which is good enough, I would prefer to look at some example.

First of all we need to create maven project, I created one using
mvn archetype:generate + maven-archetype-quickstart

Then we need to add owner library via dependency

    <dependencies>
        <dependency>
            <groupId>org.aeonbits.owner</groupId>
            <artifactId>owner-java8</artifactId>
            <version>1.0.6</version>
        </dependency>
    </dependencies>

Then I created 2 properties file. One is called resources/db.properties and the second one is resources/config/server.properties. I put the following content there:

// db.properties
db.url=jdbc:mysql://db.domain.com/mysql
dbTimeout=10:SECONDS

//server properties
server.usernames=thor,loki
server.blog.url=https://nikitapavlenko.wordpress.com/

Then we can create config class. I created interface called AppConfig.

@LoadPolicy(Config.LoadType.MERGE)
@Sources({"classpath:db.properties", "classpath:config/server.properties"})
interface AppConfig extends Config {

    @DefaultValue("10")
    int maxThreads();

    @Key("db.url")
    String databaseConnectionUrl();

    @ConverterClass(TimeoutConverter.class)
    TimeOut dbTimeout();

    @Separator(",")
    @Key("server.usernames")
    List<String> serverUsernames();

    @Key("server.blog.url")
    URL serverBlogUrl();

}

Without @Sources annotation owner tries to find ${configclassname}.properties file in a classpath. Since I named files in another I can say owner need to load my properties files via @Sources annotation:

@LoadPolicy(Config.LoadType.MERGE)
@Sources({"classpath:db.properties", "classpath:config/server.properties"})

In source you can use classpath resources as well as just file resources (e.g. “file:/home/foo.properties”)

Lets go through define methods one by one:

  • maxThreads – no property with such name is defined, default value from annotation should be used
  • databaseConnectionUrl – no property with such name, but there is @Key annotation which will be used to get correct value
  • dbTimeout – is existing property, but class timeout was defined by me, so custom Converter which I defined in @ConverterClass(TimeoutConverter.class) should be used
    
    public class TimeOut {
        private int amount;
        private TimeUnit timeUnit;
    
        public TimeOut(int amount, TimeUnit timeUnit
        ) {
            this.amount = amount;
            this.timeUnit = timeUnit;
        }
    
        public int getAmount() {
            return amount;
        }
    
        public TimeUnit getTimeUnit() {
            return timeUnit;
        }
    }
    
    public class TimeoutConverter implements Converter {
    
        @Override
        public Object convert(Method method, String s) {
            String[] values = s.split(":");
            int amount = Integer.parseInt(values[0]);
            TimeUnit timeUnit = TimeUnit.valueOf(values[1]);
            return new TimeOut(amount, timeUnit);
        }
    }
    
    
  • serverUsernames – such property does not exist, but again there is @Key annotation and @Separator, which means that values will be splitted using separator
  • serverBlogUrl – example of rich Convertion support, converting java.net.URL

Then I created a class ConfigDemo to show how to access defined properties.

public class ConfigDemo {

    private static void print(String value){
        System.out.println(value);
    }

    public static void main(String[] args) {
        AppConfig configApp = ConfigFactory.create(AppConfig.class);
        print("Max threads:" + configApp.maxThreads());
        print("DB URL: " + configApp.databaseConnectionUrl());
        print("Timeout " + configApp.dbTimeout().getAmount() + " " + configApp.dbTimeout().getTimeUnit());
        print("Usernames: " + configApp.serverUsernames());
        print("URL: " + configApp.serverBlogUrl());
    }

}

Here is the output of the program.

Max threads:10
DB URL: jdbc:mysql://db.domain.com/mysql
Timeout 10 SECONDS
Usernames: [thor, loki]
URL: https://nikitapavlenko.wordpress.com/

You can find this example on my github: owner-properties-demo.

As you see, it works pretty well. Now you can go much faster with your app configuration since there is no need to write dozens of lines to access , fallback or convert properties. All of that is already there.

Summary

If you are tired of writing routine and repetitive code for handling properties then owner it is something you should definitely try to use. You can be sure that is it stable, at least they use TDD and claim 97% code coverage. Also they are feature rich, which means that you will definitely find what you need.  In regards to me: if I need properties management in my next pet or production project  I will definitely use owner library.

Advertisements

Author: nikitapavlenko

I am Software Engineer from Kharkiv, Ukraine. I develop e-commerce web applications using Hybris platfrom mostly using Java.

1 thought on “Reinventing java properties or using “Owner” library”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s