Project Configuration with Spring

Table of Contents

1. Configuration Must Be Environment Specific

Configuration must be environment specific – that’s just a fact of life. If that weren’t the case, then it wouldn’t be configuration and we would just hardcode values in code.

For a Spring application there are several solutions you can use – from simple solutions all the way to uber-flexible, highly complex alternatives.

One of more common and straightforward solutions is a flexible use of properties files and the first class property support provided by Spring.

As a proof of concept, for the purposes of this article, we’ll take a look at one specific type of property – the database configuration. It makes perfect sense to use one type of database configuration for production, another for testing and yet another for a dev environment.

2. The .properties Files for Each Environment

Let’s start our Proof of Concept – by defining the environments we want to target:

  • Dev
  • Staging
  • Production

Next – let’s create 3 properties files – one for each of these environments:

  • persistence-dev.properties
  • persistence-staging.properties
  • persistence-production.properties

In a typical Maven application, these can reside in src/main/resources, but the wherever they are, they will need to be available on the classpath when the application is deployed.

An important sidenote – having all properties files under version control makes configuration much more transparent and reproducible. This is in opposition to having the configs on disk somewhere and simply pointing Spring to them.

3. The Spring Configuration

In Spring, we’ll include the correct file based on the environment:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-4.0.xsd">

      <context:property-placeholder
         location="
         classpath*:*persistence-${envTarget}.properties" />

</beans>

The same can of course be done with Java configuration as well:

@PropertySource({ "classpath:persistence-${envTarget:dev}.properties" })

This approach allows for the flexibility of having multiple *.properties files for specific, focused purposes. For example – in our case, the persistence Spring config imports the persistence properties – which makes perfect sense. The security config would import security related properties and so on.

4. Setting the Property in Each Environment

The final, deployable war will contain all properties files – for persistence, the three variants of persistence-*.properties. Since the files are actually named differently, there is no fear of accidentally including the wrong one. We will set the envTarget variable and thus select the instance we want from the multiple existing variants.

The envTarget variable can be set in the OS/environment or as a parameter to the JVM command line:

-DenvTarget=dev

5. Testing and Maven

For integration tests that need persistence enabled – we’ll simply set the envTarget property in the pom.xml:

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-surefire-plugin</artifactId>
   <configuration>
      <systemPropertyVariables>
         <envTarget>h2_test</envTarget>
      </systemPropertyVariables>
   </configuration>
</plugin>

The corresponding persistence-h2_test.properties file can be placed in src/test/resources so that it will only be used for testing and not unnecessarily included and deployed with the war at runtime.

6. Going Further

There are several ways to build additional flexibility into this solution if needed.

One such way is to use a more complex encoding for the names of the properties files, specifying not just the environment in which they are to be used, but also more information (such as the persistence provider). For example, we might use the following types of properties: persistence-h2.properties, persistence-mysql.properties or, even more specific: persistence-dev_h2.properties, persistence-staging_mysql.properties, persistence-production_amazonRDS.properties.

The advantage of such a naming convention – and it is just a convention as nothing changes in the overall approach – is simply transparency. It now becomes much clearer what the configuration does only by looking at the names:

  • persistence-dev_h2.properties: the persistence provider for the dev environment is a lightweight, in-memory H2 database
  • persistence-staging_mysql.properties: the persistence provider for the staging environment is a MySQL instance
  • persistence-production_amazon_rds.propertie: the persistence provider for the production environment is Amazon RDS

7. Conclusion

This article discusses a flexible solution for doing environment specific configuration in Spring. An alternative solution using profiles can be found here.

The implementation of the solution can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

I usually post about Spring stuff on Google+ - you can follow me there:

 

>> GET THE EBOOKS <<
Get the eBooks and Learn to Build a Simple App
×
Build a Simple but Working App with Spring

  • http://javarevisited.blogspot.com/2011/12/how-to-traverse-or-loop-hashmap-in-java.html javin @ loop map in java

    Nice post and thanks for sharing. Using IDE like Eclipse or Netbeans and using Maven for managing dependencies and library is my preferred way.I have blog my experience on spring as Ldap authentication using Spring with Example let me know how do you find it.

  • Leandro de Oliveira

    You are duplicating the same thing among 3 files, it’s better to have only one persistence.properties file and configure your build to change it according to the environment you’re building for. You should take a look at http://code.google.com/p/maven-config-processor-plugin/ to do this. This way you can even keep your production configuration in another place and have the plugin apply it at build time only, making your production env more secure.

    • baeldung

      Yes, I considered introducing this kind of details in the post. I have used these kinds of Maven magic in the past and it was always never the right approach. The downside of having the properties replicated into 3 files is likely minimal (keep in mind that the common stuff, that doesn’t change should be included into a common.properties file). The upside of doing things this way is that the solution is SIMPLE and easily understood, while a custom maven plugin doing transformation is not. For me, this is hugely important – I would much rather work with plain text files and keep things transparent and clear for ops and everybody much more than I would want to reduce redundancy to the bare possible minimum. That being said, I’m sure that there are some projects where the trade-offs are not as clear, or where personal preference is towards the Maven solution, it’s just that in my experience that hasn’t been the case.
      Thanks for the interesting feedback.
      Eugen.

      • Leandro de Oliveira

        Your solution is simpler for devs but isn’t for ops because the program will expose three config files to them and requires a JVM parameter.
        The build-time solution requires a config file for transformations and a pom.xml change, your solution also requires a change to make tests use the correct properties file. That may force people to configure JUnit executions in their IDE instead of just clicking “Run JUnit”. Also, this solution will present ops with just one config file instead of three.
        The less parameters you have to pass to your program, the less complicated it’ll be to run it from your IDE or send it over to operations run it.

        • baeldung

          Agreed – a parameter will need to be passed to the JVM when running the tests (which is something very standard in any IDE).
          Regarding the pom.xml change – there are pom changes and then there are pom changes – setting a variable is one thing, introducing a custom plugin doing transformations is another ( setting a JVM parameter is hopefully something simple and familiar to both dev and ops ). So, while I understand the appeal of the Maven solution, it is one more moving part that you need to understand and debug when things go wrong; I’d much rather do that with plain text files than with a Maven plugin, but it’s clearly a decision to be evaluated based on the context, and a tread-off one way or another. My personal preference is towards this solution and I would choose the Maven plugin only if the complexity price would really be justified (perhaps on extremely large projects with lots and lots of changing configuration).

    • Dave

      We generally don’t think it’s a good idea to re-build for each target environment. We want to feel confident that the artifact tested in one environment (e.g. system test) is the same artifact tested in the next environment (e.g. UAT). So I actually think environment-specific properties files is the right way to go in this case.

  • SK

    If we have to change the production environment configuration (let’s say there is some change to the database host/port), does this mean that i have to rebuild my maven artifact with the new properties file?

    • SK

      Ah, never mind – I see your point about making the properties file available on the classpath of the application, thanks!

    • Eugen

      Well that depends on how you read/load the properties. In most cases (and by default in Spring) properties are loaded once when the Spring context is bootstrapped, so whatever you add later on, the running web application will not pick up. You can change values by hand and restart – no need to build again. Also, if you go once step forward, if you have a mechanism that listens on changes of these properties files (and reloads the values when they change) they you no longer need the restart (see what LiveRebel does for an example), but to answer your questions – no, you don’t need to rebuild – that’s the entire point of keeping these properties in properties files and not in code.
      Thanks for the feedback.
      Eugen.

  • MaciejWalkowiak

    With Spring 3.1 there were introduced useful feature called profiles. With that you can define active profile for your environment – like you did with envTarget system property: http://blog.springsource.org/2011/02/14/spring-3-1-m1-introducing-profile/ .

    Recently I have posted how it can be used and configured together with Tomcat – to make it easy to build one package for all environments and make environment choose confiuration: http://maciejwalkowiak.pl/blog/2012/03/27/spring-3-1-profiles-and-tomcat-configuration/

  • avi

    Is there any way we can update bean properties in application context using properties object instead of properties file.

    • baeldung

      What exactly do you mean by properties object? Do you mean defining the properties programatically?