Allowing Java WARs to Play Well with Others

You're a software developer or an Operations person that is working with a Java application. Here are some questions for you.

  • Do you have a WAR that you need to deploy?
  • Do you know if it comes to you preconfigured or a blank slate?
  • Do you know what happens if you deploy the WAR to an application server and not realize that it is preconfigured?

I've now seen this many times and I'm here to get up on my soapbox and say something. Java was originally intended to be write once, run anywhere but I have repeatedly seen where the configuration is embedded within the WAR container. This embedding now renders the WAR file useless to run anywhere but the exact machine/platform/environment where the configuration is for.

In this situation, your application does not play well with others because it comes with a complete set of baggage in its WAR file. It also imposes cognitive load on whomever works with the codebase or the WAR file, with many environment-specific gotchas.

Here is the process many software developers use to package their products in Enterprise settings:

  1. Download source code
  2. Run ant/maven
  3. Code is compiled
  4. Point to a settings file
  5. Settings file read in as properties
  6. Properties are token-replaced into various other properties files
  7. Compiled code and properties files are packaged into a WAR file
  8. WAR file is versioned and put into a dist directory
  9. Optionally, WAR is deployed onto a server to uploaded to S3

What's wrong here? The WAR's filename cannot be versioned correctly because it contains configuration information. Saying that app-1.0.war is deployed on Production and Staging environments and is the same on both environments is incorrect, because the existence of the configuration files has modified the package. If I ran a diff on both WAR files, it would show that they are different.

Another issue is that the configuration is burned into the entire package, so if any of that configuration needs to be changed on-the-fly, one must go into the WAR file (or the exploded directory contents) and modify it there. This brings with it ownership and permissions issues, versioning issues, and a whole slew of other problems.

What should actually happen when creating a WAR file:

  1. Download source code
  2. Run ant/maven
  3. Code is compiled
  4. Compiled code is packaged into a WAR file
  5. WAR file is versioned and put into a dist directory

Then on an application server:

  1. Environment variable is exposed to application server pointing to the environment-specific properties files
  2. WAR is deployed onto a server
  3. WAR is provisioned and activated
  4. WAR codebase looks for environment variable
  5. WAR load the properties files found at environment variable or die with a clear error message

It's just so simple. The WAR can now be versioned, uploaded to S3 and forgotten about. It is just a generic, versioned, piece of compiled software that assumes nothing about its surroundings other than to look for a specific environment variable to bootstrap itself.

Lastly, with this method of configuring a Java application, when something happens in Production to app-1.0.war, an Operations person can run a diff on the same WAR file in the Staging environment and see that there are no differences in the file. That removes one massive variable from the troubleshooting process, and leaves only a code bug or a configuration bug.