How to access Environment Variables in the Spring IoC Configuration
THE PROBLEM: We have a simple properties file used to dynamically define the Data Source. Here is the Data Source configuration:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${db.driver}</value>
</property>
<property name="url">
<value>
${db.connectionType}://${db.url}:${db.port}/${db.name}
</value>
</property>
<property name="username">
<value>${db.username}</value>
</property>
<property name="password">
<value>${db.password}</value>
</property>
</bean>
I had a PropertyPlaceholderConfigurer that pointed to the location of my properties file, which was located in the classpath. (<property name="location" value="classpath:context.properties" />). My application, built using Maven, would create an executable jar containing the dependencies and everything ran perfectly… in my development environment.
I realized that this would not be ideal for other environments, but I kind of pushed this out of my mind during the initial development phase. However, it was now time to support multiple environments dynamically.
SOLUTIONS:
Manually editing the properties file was not an option, it was locked in the executable jar. So, it is clear that the file would need to be placed somewhere else in the file system.
My first solution was to simply change the location to: <property name="location" value="file://${ctx.props}" />. This would allow us to start the application with whichever properties we wished.
Unfortunately, we want to avoid using System properties when starting applications, instead we would like to use environment variables. Aside from being lazy, this can lead to enormous headaches trying to manage several environment variables throughout several environments. I would much rather create a simple startup script.
Here is where things get a bit confusing. Java 1.4 deprecated the System.getenv() method, which gave a Java application access to the environment variables. Therefore Spring did not support accessing environment variables. So, when I googled “spring environment variables”, I found several posting indicating that it was not supported and not possible.
It wasn’t until dug into the API (which is where I should have started), that I found that Spring does in fact support this out of the box. So, now the environment variable problem was solved as follows:
<property name="location" value="file://${CONFIG_DIR}/context.properties" />
Now, however, I (and all other developers) were stuck having to create an environment variable locally for a deployment requirement. So, after some quick research, I ended up with this solution:
<bean id="dataConfigPropertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file://${CONFIG_DIR}/batchContext.properties</value>
<value>file://${ctx.props}</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="false" />
<property name="searchSystemEnvironment" value="true" />
</bean>
Now, I can run this locally (assuming that I have not set the environment variable) with the ctx.props set as a System property or I can run it with the environment variable set (and no System variable).
ONE MORE THING:
With this setup, I would have a properties file in my jar that I never use (because I am using a file resource rather than a classpath resource). So, I had to update my Maven POM to exclude the properties file in the build.
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>executable.Startup</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>install</phase>
<goals>
<goal>attached</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>*.properties</exclude>
</excludes>
</resource>
</resources>
</build>
A more in-depth look at property files can be found here.