25

A simple way to keep the settings of a Java application is represented by a text file with ".properties" extension containing the identifier of each setting associated with a specific value (this value may be a number, string, date, etc..). C# uses a similar approach, but the text file must be named "App.config". In both cases, in source code you must initialize a specific class for reading settings: this class has a method that returns the value (as string) associated with the specified setting identifier.

// Java example
Properties config = new Properties();
config.load(...);
String valueStr = config.getProperty("listening-port");
// ...

// C# example
NameValueCollection setting = ConfigurationManager.AppSettings;
string valueStr = setting["listening-port"];
// ...

In both cases we should parse strings loaded from the configuration file and assign the ​​converted values to the related typed objects (parsing errors could occur during this phase). After the parsing step, we must check that the setting values ​​belong to a specific domain of validity: for example, the maximum size of a queue should be a positive value, some values ​​may be related (example: min < max), and so on.

Suppose that the application should load the settings as soon as it starts: in other words, the first operation performed by the application is to load the settings. Any invalid values for the settings ​​must be replaced automatically with default values​​: if this happens to a group of related settings, those settings are all set with default values.

The easiest way to perform these operations is to create a method that first parses all the settings, then checks the loaded values ​​and finally sets any default values​​. However maintenance is difficult if you use this approach: as the number of settings increases while developing the application, it becomes increasingly difficult to update the code.

In order to solve this problem, I had thought of using the Template Method pattern, as follows.

public abstract class Setting
{
    protected abstract bool TryParseValues();

    protected abstract bool CheckValues();

    public abstract void SetDefaultValues();

    /// <summary>
    /// Template Method
    /// </summary>
    public bool TrySetValuesOrDefault()
    {
        if (!TryParseValues() || !CheckValues())
        {
            // parsing error or domain error
            SetDefaultValues();
            return false;
        }
        return true;
    }
}

public class RangeSetting : Setting
{
    private string minStr, maxStr;
    private byte min, max;

    public RangeSetting(string minStr, maxStr)
    {
        this.minStr = minStr;
        this.maxStr = maxStr;
    }

    protected override bool TryParseValues()
    {
        return (byte.TryParse(minStr, out min)
            && byte.TryParse(maxStr, out max));
    }

    protected override bool CheckValues()
    {
        return (0 < min && min < max);
    }

    public override void SetDefaultValues()
    {
        min = 5;
        max = 10;
    }
}

The problem is that in this way we need to create a new class for each setting, even for a single value. Are there other solutions to this kind of problem?

In summary:

  1. Easy maintenance: for example, the addition of one or more parameters.
  2. Extensibility: a first version of the application could read a single configuration file, but later versions may give the possibility of a multi-user setup (admin sets up a basic configuration, users can set only certain settings, etc..).
  3. Object oriented design.
1
  • For those proposing the use of a .properties file where do you store the file itself during development, testing and then production because it will not be in the same location, hopefully. Then the application will need to be recompiled with whatever location (dev, test or prod) unless you can detect the environment at runtime and then have hardcoded locations inside your app.
    – user146997
    Commented Aug 22, 2014 at 18:02

5 Answers 5

8

Essentially the external configuration file is encoded as a YAML document. This is then parsed during application start up and mapped to a configuration object.

The final result is robust and above all simple to manage.

0
6

Let's consider this from two points of view: the API to get the configuration values, and the storage format. They are often related, but it's helpful to consider them separately.

Configuration API

The Template Method pattern is very general, but I question whether you really need that generality. You'd need a class for each type of config value. Do you really have that many types? I'd guess that you could get by with just a handful: strings, ints, floats, booleans, and enums. Given these, you could have a Config class that has a handful of methods on it:

int getInt(name, default, min, max)
float getFloat(name, default, min, max)
boolean getBoolean(name, default)
String getString(name, default)
<T extends Enum<T>> T getEnum(name, Class<T> enumClass, T default)

(I think I got the generics on that last one right.)

Basically each method knows how to handle parsing of the string value from the config file and to handle errors and to return the default value if appropriate. Range checking for the numeric values is probably sufficient. You might want to have overloads that omit the range values, which would be equivalent to providing a range of Integer.MIN_VALUE, Integer.MAX_VALUE. An Enum is a type-safe way of validating a string against a fixed set of strings.

There are some things this doesn't handle, such as multiple values, values that are interrelated, dynamic table lookups, etc. You could write specialized parsing and validation routines for these, but if this gets too complicated, I'd start to question whether you're trying to do too much with a config file.

Storage Format

Java properties files seem fine for storing individual key-value pairs, and they support the types of values I described above pretty well. You could also consider other formats such as XML or JSON, but these are probably overkill unless you have nested or repeated data. At that point it seems way beyond a config file....

Telastyn mentioned serialized objects. This is a possibility, although serialization does have its difficulties. It's binary, not text, so it's hard to see and edit the values. You have to deal with serialization compatibility. If values are missing from the serialized input (e.g., you added a field to the Config class and you're reading an old serialized form of it), the new fields are initialized to null/zero. You have to write logic to determine whether to fill in some other default value. But does a zero indicate absence of a config value, or was it specified to be zero? Now you have to debug this logic. Finally (not sure if this is a concern) you still may need to validate values in the serialized object stream. It's possible (though inconvenient) for a malicious user to modify a serialized object stream undetectably.

I'd say to stick with properties if at all possible.

3
  • 2
    Hey Stuart, nice to see you here :-). I'll add to Stuarts answer that I think your tempalte idea will work in Java if you use Generics to strongly type, so you could have Setting<T> as an option as well. Commented Aug 16, 2012 at 7:40
  • @StuartMarks: Well, my first idea was just to write a Config class and use the approach proposed by you: getInt(), getByte(), getBoolean(), etc.. Continuing with this idea, I first read all the values ​​and I could associate each value to a flag (this flag is false if a problem occurred during deserialization, for example parsing errors). After that, I could start a validation phase for all ​​loaded values and set any default values.
    – enzom83
    Commented Aug 16, 2012 at 10:30
  • 2
    I'd favour some kind of JAXB or YAML approach to simplify all the details.
    – Gary
    Commented Aug 16, 2012 at 16:40
4

How I've done it:

Initialize everything to default values.

Parse the file, storing the values as you go. The places being set are responsible for ensuring the values are acceptable, bad values are ignored (and thus retain the default value.)

1
  • This could also be a good idea: a class that loads the values ​​of the settings may have to deal only to load the values ​​from the configuration file, that is, its responsibility could only be the one to load the values ​​from the configuration file; instead each module (which use some settings) will have the responsibility to validate the values.
    – enzom83
    Commented Aug 16, 2012 at 10:50
2

Are there other solutions to this kind of problem?

If all you need is a simple configuration, I like to make a plain old class for it. It initializes the defaults, and can be loaded from file by the app via the built in serialization classes. The app then passes it around to stuff that needs it. No mussing about with parsing or conversions, no screwing around with config strings, no casting garbage. And it makes the configuration way easier to use for in-code scenarios where it needs to be saved/loaded from server or as presets, and way easier to use in your unit tests.

2
  • 1
    No mussing about with parsing or conversions, no screwing around with config strings, no casting garbage. What do you mean?
    – enzom83
    Commented Aug 16, 2012 at 0:08
  • 1
    I mean that: 1. You don't need to take the AppConfig result (a string) and parse it into what you want. 2. You don't need to specify any sort of string to pick which config parameter you want; that's one of those things that is prone to human error and hard to refactor and 3. you don't need to then do other type conversions when going to set the value programmatically.
    – Telastyn
    Commented Aug 16, 2012 at 0:26
2

At least in .NET, you can pretty easily create your own strongly-typed configuration objects -- see this MSDN article for a quick example.

Protip: wrap your config class in an interface and let your application talk to that. Makes it easy to inject fake configuration for tests or for profit.

2
  • I read the MSDN article: it is interesting, essentially each subclass of ConfigurationElement class could represent a group of values, and for any value you can specify a validator. But if for example I wanted to represent a configuration element that consists of four probability, the four probability values ​​are correlated, since their sum must be equal to 1. How do I validate this configuration element?
    – enzom83
    Commented Aug 16, 2012 at 10:02
  • 1
    I'd generally argue that is not something for low level configuration validation -- I'd add a AssertConfigrationIsValid method to my configuration class to cover this in code. If that doesn't work for you I think you can create your own configuration validators by extending the attribute's base class. They have a compare validator so they obviously can talk cross-property. Commented Aug 16, 2012 at 10:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.