views:

1088

answers:

5

I want to store configuration for a web project outside of the web project (ear/war file). The application shouldn't know in which container it's running (WebSphere/JBoss etc.).

What is the best way to handle this?

Is JNDI a clean way? If JNDI can solve my problems, how should I configure it? (Custom Objects?)

In my case are there only simple Key=>Value pairs (String,String) for SOAP/WS endpoints.

+6  A: 

I use an environment variable to point to a URL (which probably is a file:// URL) that has my configuration in it. This is very simple to setup and doesn't require the JNDI infrastructure.

Here's some sample code (typed from memory - I haven't compiled/tested this):

public void loadConfiguration() {
   String configUrlStr = System.getenv("CONFIG_URL"); // You'd want to use a more
                                                      // Specific variable name.
   if(configUrlStr == null || configUrlStr.equals("") {
       // You would probably want better exception handling, too.
       throw new RuntimeException("CONFIG_URL is not set in the environment."); 
   }


   try {
       URI uri = new URI(configUrlStr);
       File configFile = new File(uri);
       if(!configFile.exists()) {
          throw new RuntimeException("CONFIG_URL points to non-existant file");
       }
       if(!configFile.canRead()) {
          throw new RuntimeException("CONFIG_URL points to a file that cannot be read.");
       }
       this.readConfiguration(configFile);
   } catch (URISyntaxException e) {
       throw new RuntimeException("Malformed URL/URI in CONFIG_URL");
   }



}
Jared
Simple as it may be, it has the disadvantage that you need to restart the container if you need to change a value.
kgiannakakis
You only have to restart the container if you need the change the location of the file - you can execute loadConfiguration() anytime you need to re-read the contents of the file.
Jared
+3  A: 

See this question for reading properties file outside of the WAR file.

See this question for reading variable values from JNDI. I believe that this is the best solution. You can read a String variable with this code:

Context initialContext = new InitialContext();
String myvar = (String) initialContext.lookup("java:comp/env/myvar");

The above code will work on all containers. In Tomcat you declare the following in conf/server.xml:

<GlobalNamingResources ...>
  <Environment name="myvar" value="..."
         type="java.lang.String" override="false"/>
</GlobalNamingResources>

The above will create a global resource. It is also possible to define a resource in the context of application. In most containers the JNDI resources are available through a MBeans Management Console. Some of them offer a graphical interface to edit them. At most an application restart is needed, when a change is made.

How JNDI resources are defined and edited is container specific. It is the job of the configurator/administrator to apply the appropriate settings.

These are the benefits offered by JNDI:

  • You can define default values of the parameters in the WAR/EAR file.
  • Parameters are easily configurable at the container.
  • You don't need to restart the container when you modify the value of a parameter.
kgiannakakis
In my case, the parameter setting needed to be done by "untrained" end users - navigating a complex XML file to find the one and only thing that needed to change wasn't an option (too much risk they'd change something they shouldn't and break the whole container.)
Jared
What's an untrained end user doing administering a container?
Draemon
In many organisations the configuration is performed by network administrators. In some cases only a manager is allowed to configure passwords or other sensitive data. These persons may have little experience with XML.
kgiannakakis
+3  A: 

My favorite places are : Environment Variables and Properties files (as suggested by Jared and kgiannakakis above.)

Database Table storing environment properties

However one other simpler solutions is to have Database table storing environment properties.

If your application uses database

  • this is relatively easy to setup
  • Gives really easy way to control/change values
  • It can be integrated in the process well by making it part of DB scripts
peacefulfire
+1: If you are already using a Database, then storing configuration parameters in a table is the simplest solution. Since you will already have production/development/testing databases you can easily store different values for the different deployments. You don't even have to restart the application, when a change is made.
kgiannakakis
But how do you get the connection information for connecting to the database (that's exactly the kind of information I would want to be able to get out of this solution)? This is a chicken-egg problem.
Jared
Agreed - Jared. I would say for DB you rely on JNDI with same JNDI string. So your app will always point to java:comp/db/datasource. Configuration information about DB (URL, user,pwd, etc) is stored outside in container. DB Properties will work for things like URLs, External Strings, env. dependent constant values, etc. Some container provides specifying all these as JNDI, but not all (eg. Websphere allows to associate String values with JNDI, Weblogic to my knowledge does not)
peacefulfire
+3  A: 

you can just store then is a normal java properties file that is on the class path and just load the properties?

it is straightforward and pretty simple.. unless I am missing something

Aaron Saunders
Although this sounds too simple to be good, it's really a good alternative. A lot of app servers have paths that get added to the classpath, so you can drop .properties files there.
alex
+3  A: 

We had a similar configuration requirement when deploying a webapp for different developers, and on Amazon's EC2: how do we separate configuration from the binary code? In my experience, JNDI is too complex, and varies too much between containers to be used. Also, hand-editing XML is very susceptible to syntax errors, so was the idea was thrown out. We resolved this with a design based on a few rules:

1) only simple name=value entries should be used

2) new configurations should be loadable by changing only one parameter

3) our WAR binary must be reconfigurable w/o repackaging it

4) sensitive parameters (passwords) will never be packaged in the binary

Using .properties files for all configuration, and using System.getPropert("domain"); to load the appropriate properties files, we were able to meet the requirements. However, the system property does not point to a file URL, instead we created a concept we call "domain" to specify the configuration to use. The location of the configuration is always:
$HOME/appName/config/$DOMAIN.properties.

So if I want to run my app using my own configuration, I start the app by setting the domain to my name:
-Ddomain=jason
on startup, and the app loads the file:
/home/jason/appName/config/jason.properties
This lets developers share configurations so we can recreate the same state of the app for testing and deployment without recompiling or repackaging. The domain value is then used to load .properties from a standard location, outside of the bundled WAR.

I can completely recreate the production environment on my workstation by using the production configuration like:
-Ddomain=ec2 which would load:
/home/jason/appName/config/ec2.properties

This setup allows us to do have dev/QA/release cycles with exactly -one- set of compiled binaries, using different configurations in each environment. There's no risk of having passwords/etc bundled in the binaries, and people can share their configurations to recreate issues that we're seeing.

Jason Thrasher
Does it work for different application servers?
Martin K.
Hi Martin, yes, it does work for different application servers, as there is zero dependency on the app. We're using this for Tomcat and Jetty, and they behave the same way. It should work for any non-web app as well.
Jason Thrasher
Nice. I believe, however, that some application servers may restrict your access to the file system, and then this won't work for them.
Thorbjørn Ravn Andersen