6
votes

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
2

2 Answers

1
votes

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) {
        // ...
    }
}
-1
votes

If you are trying to create a way to keep your app portable, my guess is that you are trying to write a program to handle something that is more quickly and efficiently handled by hand (by a human being instead of a computer). Each container (Glassfish, JBoss, WebSphere, etc.) is not constrained to having to do things the same way as another container. So their configuration files can and usually are significantly different if what you are after is portability. It is better to just figure out how to run the apps in the different servers and save the configuration files. At best you could do what we did at one place and create files with place-holders that can be used with simple properties files where a script substitutes in the values for each place-holder (e.g. port numbers, etc). How often will you be switching between different kinds of servers? If you say often, then you will have a lot more problems than this. Better to pick a platform and stick with it. Way fewer problems that way.

If you don't get an answer you like here, try on Server Fault (sister site to this one).