The problem: when developing my properties files are available on the classpath. I have many layered projects so the props files end up in a jar which gets embedded in a JAR inside the WAR. This doesn't make a very friendly config file for someone deploying the app. Essentially I want my WAR to read a config file outside the classpath, in a location determined by an environement variable. I realized I could extend Spring's PropertyPlaceHolderConfigurer to do the trick. This class and config file are below, as well as some test files.
package harschware; import java.io.File; import java.util.List; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import com.google.common.collect.Lists; public class HomePropertyPlaceholderCongfigurer extends PropertyPlaceholderConfigurer { private String homePathEnv; private String[] homePathLocations; public HomePropertyPlaceholderCongfigurer(String homePathEnv, String[] homePathLocations ) { super(); this.homePathEnv = System.getenv(homePathEnv); this.homePathLocations = homePathLocations; } // end method public void setLocations(Resource[] locations) { List<Resource> locList = Lists.newArrayList(locations); for (String homePathLocation : homePathLocations) { if( homePathEnv != null ) { Resource propFileRespource = new FileSystemResource(homePathEnv + File.separator + homePathLocation); if( propFileRespource.exists() ) locList.add(propFileRespource); } // end if } // end for locations = locList.toArray(locations); super.setLocations(locations); } // end method } // end class
The Spring configuration file:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="harschware.HomePropertyPlaceholderCongfigurer"> <constructor-arg value="MY_HOME" /> <constructor-arg> <list> <value>home.properties</value> </list> </constructor-arg> <property name="locations"> <value>classpath:test.properties</value> </property> </bean> <bean id="testStr" class="java.lang.String"> <constructor-arg value="${t.x}" /> </bean> </beans>
A Simple Test Driver:
package harschware; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestMe { /** * @param args */ public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:app.xml"); System.out.println((String)ctx.getBean("testStr")); } }
The properties file "test.properties" (existing in the project's classpath):
t.x=Not Overridden!
The properties file "home.properties" (existing at %MY_HOME%/home.properties):
t.x=It was overidden
Running the test app would produce the output "It was overidden".
That's it!
Some comments about the code.
"if( homePathEnv != null ) {" can be outside the for-loop
Re-assignment of input parameter should be avoided. Better to end with: "super.setLocations(locList.toArray(locations));"