Question

A class loads properties from a file and uses those as it performs its duties. For testing purposes I want to be able to modify said properties directly without having to use a test properties file. I did the implementation, but it seems filled with smells -- I'm wrestling with keeping this thing small (avoiding frameworks like Spring that will wrap and inject the properties).

In particular, I cannot recall seeing a constructor that initializes itself via a statically loaded set of properties -- particularly that it sets variable values from somewhere other than passed-in parameters.

I'm looking for arguments for/against this approach, and hoping to hit upon some alternatives if this is really as uncool as I think it is.

private static final String CONFIG_FILE_NAME = String.format("config-%s.properties",
        null != System.getProperty("JAVA_ENV") ? System.getProperty("JAVA_ENV") : "development");
static {
    PROPERTIES = new Properties();
    try {
        PROPERTIES.load(MyClass.class.getClassLoader().getResourceAsStream(CONFIG_FILE_NAME));
        if (PROPERTIES.keySet().size() < 1) {
            LOGGER.error(String.format("failed to load %s: no properties", CONFIG_FILE_NAME));
        }
    }
    catch (final IOException ie) {
        LOGGER.error(String.format("failed to load %s", CONFIG_FILE_NAME), ie);
    }
}

private String property1;
private String property2;

public MyClass() {
    property1 = PROPERTIES.getProperty("prop.1");
    property2 = PROPERTIES.getProperty("prop.2");
}

/* public accessors for property1 and property2 go here */
Was it helpful?

Solution

To start with, static initializer of a named class or interface can not throw a checked exception (JLS 11.2.3. Exception Checking). That is, if application design somehow involves checked exceptions for various problems at initialization, static initializers are simply not an option.

Given your code snippet, you already noticed this - your catch block just logs IOException without re-throwing it. But even without checked exceptions, this is quite a troublesome practice. You can test that by adding code that re-throws runtime exception from your catch block (this will compile) and simulating the absence of config file.

Think of how it would feel to those maintaining your application when they get logs reporting an obscure ExceptionInInitializerError for a routine typo in config file name.

Yet another reason why this practice isn't convenient can be found in JLS 12.4.1. When Initialization Occurs

...Invocation of certain reflective methods in class Class and in package java.lang.reflect also causes class or interface initialization...

Above means that you give up a lot of control for when initialization occurs. Some framework may trigger it when you don't even expect it. If memory serves, in my practice this happened once when logging was't yet fully initialized - believe me, it was no fun to investigate why application crashes when log files were empty.


The last but not the least, this design is very inconvenient for unit testing purposes. Whenever you would want to use MyClass in unit tests, you'd have to ensure that 1) config file is there in file system and 2) that it is stuffed with desired values and properties.

Compare this design to constructor that simply takes Properties as a parameter:

    MyClass(Properties properties) {
        this.properties = properties; // initialize field from constructor parameter
    }

Testing with class constructed like above is as easy as it gets. No messing with the filesystem, no messing with filling the file with desired properties and values, plain Java code. You create Properties object and stuff it however you want, straight from your test code, wherever it is, whenever it is convenient to you.

Summing up, there are plenty reasons why you cannot recall seeing a constructor that initializes itself via a statically loaded set of properties.

Licensed under: CC-BY-SA with attribution
scroll top