At Atlassian we use daemontools to manage all our server processes, both for our internal services as well as our customers hosted services. One of the utility programs provided by daemontools is `envdir`. As the documentation suggests, this is used as a convenience for setting environment variables. So commonly for our tomcat apps, we’ll have a directory structure like this:

/service/mytomcatapp/env
– JAVA_OPTS
– CATALINA_HOME
– CATALINA_BASE

and then our `run` command will look like this:

envdir ./env bash -c ‘
${CATALINA_HOME}/bin/catalina.sh run

Because each environment variable is just a file, it’s very easy for provisioning scripts to generate these as needed, which makes mass deployments in a hosted environment quite nice.

However, there is a problem with the above. Often you need to pass system properties to java apps, and overtime these tend to build up. System properties can be specified in `JAVA_OPTS`, but before long, this becomes a difficult to maintain one line file, and we were finding that we were moving system properties out of `JAVA_OPTS` and into the run command, like this:

envdir ./env bash -c ‘
export JAVA_OPTS=”${JAVA_OPTS}
-Dhttp.proxyHost=proxy.host.com
-Dhttps.proxyHost=proxy.host.com
-Djava.awt.headless=true
-Djira.home=/path/to/jira/home”
${CATALINA_HOME}/bin/catalina.sh run

This is exactly where this sort of configuration doesn’t belong. The other reason why we had this configuration in the run command was that often our system properties were based on environment variables, and so needed to be generated after `envdir` had been run.

We needed a solution that allowed flexible setting of system properties that could include environment variables. So I wrote a little script that used a similar mechanism to `envdir`, but for system properties. It takes a folder containing system properties, and builds a string of `-Dkey=value` pairs from the files in this folder. It also does environment variable substitution of the values, so they can be dynamically generated based on the environment set up by `envdir`. After generating this string, it appends it to the named environment variable passed in to the function.

Here’s the script:

#! /bin/bash
#
# This script provides functions for constructing system property arguments to the JVM in a similar method to the way
# daemontools environment variables work. Each system property is a file with the name of the property, and the
# contents being the value of the property. As yet, spaces in values are not yet supported, but you can probably
# achieve this by putting ” or ‘ around your value. Environment variables in values are substituted.
#
# @author James Roper
#
#
# Build system properties
#
# @param DIR The directory to read the system properties from
# @param ENVVAR The environment variable to append the properties to
#
# Who said you can’t do javadocs in bash?
#
build_system_properties() {
local DIR=$1
local ENVVAR=$2
if [[ -z “${DIR}” || ! -d “${DIR}” ]] ; then
echo “${DIR} is not a directory”
return
fi
if [ -z “${ENVVAR}” ] ; then
echo “No ENVVAR specified”
return
fi
local PROPS=””
local PROP
for PROP in $(ls ${DIR}) ; do
local VALUE=$(< ${DIR}/${PROP})
# This expands any environment variables in the value
local EXPANDED_VALUE=$(eval echo “${VALUE}”)
PROPS=”${PROPS} -D${PROP}=${EXPANDED_VALUE}”
done
# PROPS will always start with a space, or be empty, so no space between the two values below
local NEWPROPS=${!ENVVAR}${PROPS}
unset ${ENVVAR}
eval true ${${ENVVAR}:=${NEWPROPS}}
}
Now our `run` script is updated to look like this:
envdir ./env bash -c ‘
. ./sysprops.sh
build_system_properties ./sysprops JAVA_OPTS
export JAVA_OPTS
${CATALINA_HOME}/bin/catalina.sh run

And we have a convenient way of adding and maintaining system properties for our Java applications in a similar manner to the way the daemontools `envdir` utility works.

Fresh ideas, announcements, and inspiration for your team, delivered weekly.

Subscribe now