I found myself wanting to have certain property values in my Spring projects differ based on what server the app is being deployed.
For example your company has dev, test, qa, prod boxes etc… But there are certain properties in your app such as default db username and passwords, database name, thread pool configurations, etc… that are specific to the server or environment where the app is running.
The technique presented in this tutorial allows you to place all common and default properties in a properties file and provide overrides based on the hostname of the server where the app is running.
For example you can have a application.myDevBoxHostName.properties that will allow you to load property values associated to your own local environment versus a application.myCompany.com.properties that will load the right set of properties in production servers. Also you can have simply application.properties to load all properties that are common or with default values for all environments that do not override properties
This tutorial is available for download with svn:
svn checkout http://raulrajatutorials.googlecode.com/svn/trunk/ raulrajatutorials-read-only
Comments in the code itself should be self-explanatory if you already have some java + spring experience
And now to the point.
applicationContext.xml
<?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"> <!-- This service allows loading of properties based on hostname supporting default values in application.properties and allowing other servers to override the default property values in their own server specific application._HOST_NAME_.properties --> <bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="systemPropertiesMode" value="1"/> <property name="locations"> <list> <value>/WEB-INF/conf/application.properties</value> <!-- In deployments servers: /WEB-INF/conf/application._HOST_NAME_.properties will override properties in application.properties. based on the hostname in the deployed server --> <bean class="com.raulraja.util.deployment.CompositeStringResourceProvider" factory-method="getReplacedResource"> <constructor-arg> <bean class="com.raulraja.util.deployment.impl.SpringInjectedServletContextProviderImpl" /> </constructor-arg> <constructor-arg value="/WEB-INF/conf/application._HOST_NAME_.properties"/> </bean> </list> </property> <property name="ignoreResourceNotFound" value="true"/> </bean> <!-- This is a sample service that will get properties injected and will display its values on initialization --> <bean id="sampleService" class="com.raulraja.util.deployment.impl.SampleServiceImpl" init-method="init"> <property name="firstProperty" value="${first.property.placeholder}" /> <property name="secondProperty" value="${second.property.placeholder}" /> </bean> </beans>
application.properties
#In this file live all common properties and default property values for all servers first.property.placeholder=this value was declared in application.properties second.property.placeholder=this value was declared in application.properties
application.yourHostNameHere.properties
# This file contains properties overriden for the host name were the app is being deployed second.property.placeholder=this value is overriden in a specific server config declared in application.properties
CompositeStringResourceProvider.java
package com.raulraja.util.deployment; import org.apache.log4j.Logger; import org.springframework.core.io.Resource; import org.springframework.core.io.ClassPathResource; import org.springframework.web.context.support.ServletContextResource; import java.net.UnknownHostException; import java.net.InetAddress; /** * A service that provides a resource based on the host name */ public class CompositeStringResourceProvider { private final static Logger log = Logger.getLogger(CompositeStringResourceProvider.class); private final static String HOSTNAME_PLACEHOLDER = "_HOST_NAME_"; /** * This method is invoked by the container at startup * @param servletContextProvider a servlet context provider * @param baseResource the base resource * @return the resource assigned to the current running server * @throws UnknownHostException if the host is unknown */ public static Resource getReplacedResource(ServletContextProvider servletContextProvider, String baseResource) throws UnknownHostException { InetAddress localhost = InetAddress.getLocalHost(); baseResource = baseResource.replaceAll(HOSTNAME_PLACEHOLDER, localhost.getHostName()); Resource resource = null; if (servletContextProvider.getServletContext() != null) { resource = new ServletContextResource(servletContextProvider.getServletContext(), baseResource); if (resource.exists()) { log.debug("Loading override for common properties: " + resource.getFilename()); } } else { resource = new ClassPathResource(baseResource); } return resource; } }
ServletContextProvider.java
package com.raulraja.util.deployment; import javax.servlet.ServletContext; /** * Provides a servlet context */ public interface ServletContextProvider { /** * @return the servlet context */ ServletContext getServletContext(); }
SpringInjectedServletContextProviderImpl.java
package com.raulraja.util.deployment.impl; import com.raulraja.util.deployment.ServletContextProvider; import org.springframework.web.context.ServletContextAware; import javax.servlet.ServletContext; /** * Spring based impl of the ServletContextProvider */ public class SpringInjectedServletContextProviderImpl implements ServletContextProvider, ServletContextAware { /** * the servlet context */ private ServletContext servletContext; /** * @return the servlet context */ public ServletContext getServletContext() { return servletContext; } /** * sets the servlet context, invoked by the container at startup since this service implements ServletContextAware * @param servletContext the servlet context */ public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } }
SampleServiceImpl.java
package com.raulraja.util.deployment.impl; import org.apache.log4j.Logger; /** * Demonstrates how values of properties are injected based on a specific hostname config */ public class SampleServiceImpl { private final static Logger log = Logger.getLogger(SampleServiceImpl.class); /** * the first prperty */ private String firstProperty; /** * the second property */ private String secondProperty; /** * service initialization method */ public void init() { log.debug("first property: " + firstProperty); log.debug("second property: " + secondProperty); } /** * sets the first property * @param firstProperty the first property */ public void setFirstProperty(String firstProperty) { this.firstProperty = firstProperty; } /** * sets the second property * @param secondProperty the second property */ public void setSecondProperty(String secondProperty) { this.secondProperty = secondProperty; } }
Now notice that my local machine hostname is raul-raja-martinezs-macbook.local
Before application.raul-raja-martinezs-macbook.local.properties existed:
06/13 17:49:52 WARN org.springframework.beans.factory.config.PropertyPlaceholderConfigurer – Could not load properties from ServletContext resource [/WEB-INF/conf/application.raul-raja-martinezs-macbook.local.properties]: Could not open ServletContext resource [/WEB-INF/conf/application.raul-raja-martinezs-macbook.local.properties]
06/13 18:00:47 DEBUG com.raulraja.util.deployment.impl.SampleServiceImpl – first property: this value was declared in application.properties
06/13 18:00:47 DEBUG com.raulraja.util.deployment.impl.SampleServiceImpl - second property: this value was declared in application.properties
After application.raul-raja-martinezs-macbook.local.properties existed:
06/13 18:03:52 DEBUG com.raulraja.util.deployment.CompositeStringResourceProvider – Loading override for common properties: application.raul-raja-martinezs-macbook.local.properties
06/13 18:03:52 DEBUG com.raulraja.util.deployment.impl.SampleServiceImpl – first property: this value was declared in application.properties
06/13 18:03:52 DEBUG com.raulraja.util.deployment.impl.SampleServiceImpl – second property: this value is overriden in a specific server config declared in application.properties











Discuss
No Comments