Search code examples
javaconfigurationpropertiesjava-ee-6glassfish-3

Configuration Management in JavaEE


My Goal

I’ve got a JavaEE environment (in my particular case it’s a Glassfish Web Profile) and I want a container independent way of configuring my application with the following features:

  1. Default configuration when nothing else is specified (inside WAR file)
  2. Custom configuration (outside WAR file) in two layers:
    • Host specific settings (in an external properties file; e.g. some working directory)
    • Application specific settings (in database; e.g. mailbox size)

My wish would be that there are as few preconditions as possible (the only one now is a JNDI datasource) to deploy and run my application (Set up JNDI datasource, deploy WAR file, have an optional .properties file in some configuration folder and - done).

This leads me to my first question: Is this a common/good/useful setup or is it unnecessarily complicated and/or very exotic?

My Idea (so far)

Default Configuration

The default configuration would be in a properties file:

src/main/resources/config/default.properties

An application scoped bean reads this properties on initialization as described here:

@Named
@ApplicationScoped
public class Configuration implements Serializable {

    ...

    @PostConstruct
    public void initConfiguration() {
        loadDefaultConfiguration();
    }

    private void loadDefaultConfiguration() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        try (InputStream input = classLoader.getResourceAsStream("/config/default.properties")) {
            properties.load(input);
        } catch(IOException ex) {
            LOGGER.error(...);
        }
    }

}

Application Specific Settings

These settings will be stored in a database table with key and value columns. They'll be always accessed via an EntityManager, hoping the caching of the JPA implementation will be clever :). The advantage here is, that these settings can easily be changed while the application is running.

@Named
@ApplicationScoped
public class Configuration implements Serializable {

    ...

    public T getProperty(final PropertyKeyEnum key, final Class<T> type) {
        if (key.getSource() == PropertySourceEnum.DATABASE) {
            return configurationDao.getByKey(key.getKey(), type);
        }

        ...
    }
}

Host Specific settings

Finally, here is my main problem:

How do I access an external properties file in a container independet way? The user shall be able to just place a myAppName.properties file into the default configuration folder of the container and the application shall be able to find and load this file (at least on application startup).

My Environment

  • JavaSE 7
  • JavaEE 6
  • Glassfish 3.1.2 Web (but this should not matter ;) )

Update

I've found a place in the admin area of Glassfish where you can specify some system properties which are easily accessable:

System.getProperty("myApp.propertyName");

This could be used to store the path to the external .properties file, but I'm not sure if this is a clean way because

  1. I don't know if every container (which supports JavaEE) has such a nice feature
  2. I don't really want to have plain file access from a web application

Solution

  • After talking with my colleagues and some research I've implemented the following for the host specific (external) configuration.

    As I need a working directory for my application anyway, I decided to use this working directory also as the location for my external configuration. Therefore I either use an environment variable (e.g. MYAPP_HOME) or, if the variable is not set, the user's home folder (e.g. <user.home>/.myapp):

    private Path discoverRootDirectory() {
        String myAppHome = System.getenv("MYAPP_HOME");
    
        if (myAppHome == null) {
            return Paths.get(System.getProperty("user.home"), ".myapp");
        } else {
            return Paths.get(myAppHome);
        }
    }
    

    The properties file will then be loaded as usual:

    private void loadConfiguration() {
        properties = new Properties();
        // ...
        try (InputStream inputStream = Files.newInputStream(discoverRootDirectory())) {
            properties.load(inputStream);
        } catch (FileAccessException | IOException ex) {
            // ...
        }
    }