Johnny
Johnny

Reputation: 1569

Managing application configuration properties in Java

I'm developing a Java application that needs some configuration files, but I always forget the property names and I find it not comfortable creating a get method for every configuration property. However I find them useful for the return type they provide for me.

I wonder how configurations are managed in well-design Java-apps but I'd like to know if it is a good practice if I do it using enums like this. I imagine how complicated it would become if they where localization files. But this idea is still seducing me.

public enum Confs {

    HLENGTH("hlength", Integer.class),
    ;

    private String propertyName;
    private Class type;


    private Confs(String propertyName, Class type) {
        this.propertyName = propertyName;
        this.type = type;
    }

    public Object getVal(){
// This hash map is loaded in a singleton class which uses java Properties feature to read the configuration file
        return MyLoadedConfigurationsSingleTon.getMap().get(this.propertyName);
    }

}

Anyway, isn't there a standard solution to solve this problem?

Upvotes: 2

Views: 800

Answers (1)

Jason C
Jason C

Reputation: 40396

Keys

It really depends on your requirements. There are tons of options here. For simple applications I generally use something that maps arbitrary configuration keys to values. Then, the names of the keys can be specified as string constants, e.g., somewhere:

public static final String KEY_COLOR = "color";
public static final String KEY_SIZE = "size";

This, of course, presents a problem of where to specify these constants. You could specify them in some globally shared location (e.g. in your configuration class, or in some special constant values class, or even as an enum like you proposed). This has the advantage of letting you see all key names in one place, thus easily avoiding conflicts. This has the distinct major disadvantage, however, of breaking modularity and forcing all classes to be dependent on this collection of keys -- adding/removing/moving objects now requires you to modify your global key collection.

Another thing you could do is define configuration key names as string constants in the classes/packages that actually use them. This does not break modularity. However, you now run the risk of conflicting key names with other unrelated classes. Adding new keys means you have to go through every object that uses your configuration and make sure the new key name is not already in use.

However, the conventional Java solution to that is to have the key names also include the package (and possibly class name) of the class that uses them. E.g.:

package com.me.whatever;
public class Something {
    static final String KEY_COLOR = "com.me.whatever.Something.color";
    static final String KEY_SIZE = "com.me.whatever.Something.size";
}

package com.me.util;
public class Other {
    static final String KEY_SIZE = "com.me.util.Other.size";
    static final String KEY_GROUP = "com.me.util.Other.group";
}

Sometimes it makes more sense to specify the package name only, if that is more representative of your situation.

The above ideas also hold true for other configuration schemes. E.g. with the Preferences API, the path to the keys can be derived from the package names. That API even provides things like systemNodeForPackage() that take care of this for you.

So, just do whatever leads to the clearest, most maintainable code. Find a balance between simplicity, modularity, and flexibility. For simple throw-away applications there's nothing wrong with breaking "OOP" concepts and sticking them all in one place, as long as what you are doing is clear. Otherwise, store the key values where they are primarily used, and take advantage of package and class names to ensure uniqueness and avoid key namespace pollution.

Note that an enum may not necessarily be the most appropriate or convenient data type to store key names in, if key names are split between multiple packages. You could certainly come up with some clever system to make that work, but more often than not string constants are both adequate and easy to work with. However, an enum could be an appropriate solution if you are storing more than just the key (e.g. the value type).


Value Types

As for value types by the way, you have many options there too. You could have conversion be done by the client class instead of enforcing it in the configuration side -- but, of course, the latter is very convenient in many cases.

The problem with using an enum is that you are essentially defining all your configuration keys in one place (see above) and it is difficult to extend the available sets of configuration keys in a modular way when classes are added or removed.

You could create a general purpose configuration key class, that can be instantiated as needed, instead of using fixed enum, e.g.:

public class Conf {

    public static class Key {

        final String key; 
        final Class<?> type;

        public Key (String key, Class<?> type) {
            this.key = key;
            this.type = type;
        }

    }

    public Object getValue (Key key) {
        ...
    }

}

The above can be easily improved through use of generic types.

Then in your client classes:

package com.me.whatever;
public class Something {
    static final Conf.Key KEY_COLOR = new Conf.Key("com.me.whatever.Something.color", Color.class);
    static final Conf.Key KEY_SIZE = new Conf.Key("com.me.whatever.Something.size", Integer.class);
}

Speaking of key names again: You could even make the package name prefix addition be a function of Conf.Key, similar to how Preferences.systemNodeForPackage() works, by making it take the declaring class's type as a parameter and extracting the package name, so the above declarations become, e.g.:

static final Conf.Key KEY_COLOR = new Conf.Key(Something.class, "color", Color.class);

Conclusion

I'm glossing over a lot because, like I said, there are infinite options. I can't really cover every case of every option here but hopefully you get the idea. It's more about approaching it in a sane way, rather than approaching it in one specific "correct" way.

Upvotes: 3

Related Questions