This is not a new issue, as can be seen with this 2001 question on The Server Side. Back then they suggested *using JMX* ... which doesn't exactly fit the *simple* requirement.
Java EE is supposed to provide much of the groundwork for apps as part of the container, so the app author can get on with solving their problem. Right now it doesn't help much with application settings, and this needs improvement so that apps:
- Don't need to each provide a custom UI in order to edit even the simplest settings
- Can easily modify their own settings
- Can guarantee the persistence of their settings across redeploys and across cluster nodes
- Don't require a full database and data access layer / JPA / etc just to store simple configuration options.
UPDATE: Marcus Eisele pointed out a related post by Antonio Goncalves as part of a jsr342-experts post on configuration. Have a look at Antonio's piece; he's looking at it from a slightly different angle, but with many of the same issues.
See also this stack overflow post from a while back, where I asked for help on this topic and got crickets.
ApproachesA Java SE app can:
- Use a .properties file stored in a well-known location like the user's homedir. The user can edit this file and/or the app can load and rewrite it easily.
- Use the Preferences API. This doesn't provide easy direct editing by the user, but gives the app a very simple way to store its settings.
- Have the user set system properties
- Servlet context parameters
- JNDI environment resources
- Storing settings in a container-defined SQL datasource
- For JBoss AS 7: Deploy a companion archive
Direct file system accessDirect file system access for storing configuration isn't trivial, because there's no clear and obvious place for configuration files to live. Should they be stored in the homedir of the user who's running the app server? In the app server's directory? In some arbitrary platform-specific location?
More importantly, will the app actually have rights to read or write the file wherever it lands up, given that a SecurityManager may be in effect?
Preferences APIThe Preferences API would seem like the ideal solution despite the lack of direct .properties editing by users. Unfortunately, at least some application servers seem to like to overwrite preferences when an application is re-deployed. Replication of preferences in a clustered app server is undefined and app-server specific. It's a mess.
The app must also provide an editing UI for settings in the preferences API; there's no way for the user to edit it directly.
System propertiesSystem properties work well, but they're set via different methods in each application server. They're globally visible across all applications, which could be a security concern for some properties. Most importantly, they're difficult or impossible for the application to update - they're effectively read-only.
Servlet context parametersServlet context parameters can be difficult to access globally across the application without hacks like adding a servlet filter to capture them. Methods for setting them without source changes are application-server specific and not at all friendly.
JNDI environment resourcesJNDI environment resources are even less fun to define for the admin, and can be clumsy to use within the application. They have a legacy Java EE 5-or-older feel and appear little-used.
JBoss AS 7 deployment companion JARsJBoss AS 7's ability to deploy companion JARs with a deployment archive is extremely useful. These JARs are private to the deployment and on the classpath. They provide an easy mechanism for customising some application descriptors and configuration. Unfortunately you can't deploy a companion jar as a jar overlay to override descriptors in the original jar, but you can use it to provide app-server-specific injectable beans and services, and to provide deployment-specific configuration. However, this feature (a) isn't available on Glassfish and (b) requires the user to build a .jar and deploy it with your app using
jboss-cli. Again, the app cannot easily modify the configuration bundled in the archive.
Best choice: Configuration in the databaseA very common solution for Java EE applications is to store their configuration in a database. Many (most) EE apps are database driven anyway, so keeping the configuration in the DB makes sense. The DB is generally accessible to all instances of a distributed/clustered app.
This is all fine so long as you're using a container-provided JDBC / JTA datasource. However it isn't suitable for apps that want to use
@DataSourceDefinitionor a bundled datasource like a
-ds.xmlfile to make deployment easier for users.
It's also useless for the purpose of specifying the name of the data source you want the application to connect to if you need to avoid hard-coding that. You can use a system property for that purpose and get the rest of the configuration from the database, though.
Even for apps that require a container data source anyway, one annoyance with this approach is that it's hard for users to go in and edit it. You are likely to need to write configuration pages for everything; you can't just direct them to a config file. That's OK for highly dynamic configuration, but it's a real pain for largely static stuff that gets set at the start of the deployment and forgotten.
Alternative choice: System properties pointing to properties filesIf your app isn't database driven you probably don't want to make the user set up a datasource in the container for your app to store its settings. You just want them to be able to deploy the app.
Good luck with that.
A system property pointing to a properties file location can be a somewhat ugly but reasonable compromise in this case. It avoids the user needing to deal with per-app-server quirks re how to configure a new persistent H2 / Derby datasource. Setting system properties is at least usually easier.
So what do I think should be available?
Preferences API constrained for Java EE applications
My ideal would be for the Java EE 7 spec to constrain the Preferences API support on Java EE 7 compliant app servers with the following additional requirements:
- Implementations must preserve the preferences API settings across re-deploys of an application. They may offer a deployment option to clear and reset preferences.
- Implementations must provide an interface to edit, back up, clear, and restore/load stored preferences for a deployment. Command line, web, whatever; just provide an interface for it. This interface must accept and produce compliant preferences XML; it may accept and produce other formats.
- On EE7 implementations that provide clustering features,
java.util.prefs.Preferencesmust implement the new java.util.prefs.SharedPreferences interface, which provides methods (yet to be defined) to allow applications to control synchronization of preferences across the cluster.
SharedPreferencesmust be injectable via CDI.
- On non-clusterable EE7 implementations,
java.util.prefs.SharedPreferencesmust be CDI-injectable as a stub that throws UnsupportedOperationException on implemented methods.
This would make j.u.p.Preferences useful in EE apps. I'm sure the details would need work - for example, maybe java.util.prefs.SharedPreferences shouldn't be injectable unless supported, so you'd use an Instance
Standardize a deployment overlay mechanism
Right now it's a real pain to change things like bean alternatives in
beans.xmlor the settings in
persistence.xmlon a per-deployment or per-app-server basis.
How can we make it easy to override or merge/overlay deployment arbitrary deployment descriptors on a per-deployment basis? Things like:
- JBoss's -ds.xml files
- ... and you name it, lots more
- It makes it hard for "end users" to just deploy an app in a fuss-free manner;
- It dramatically reduces the utility of CDI's
- It forces the creation of per-app-server flavours of many app builds
- It's slow, fiddly, and annoying
beans.xmlwhere merging could be made relatively well-defined.