Wednesday, October 7, 2009

ConfigSlurperPlaceholderConfigurer for Spring



Lately I'm working on Spring 3.0 hobby project in my spare time. Frequent readers of my blog or followers me on Twitter might think: Why plain Spring and not just Grails? So let me explain that first:


  1. I love Grails (and Groovy), and if I could make the decision I would use it on every project! But...

  2. I want to host my hobby project cheap, and even Amazon EC2/AWS is to expensive currently. So I decided to host my application on Google App Engine. And to be honest, Grails support for GAE is just alpha/beta state in my opinion (I hope it will change one day). So Grails was not an option.

  3. I've been coding Groovy and Grails for a while now and also doing commercial projects. But I have the feeling my basic Java and Spring knowledge might become a little bit rusty. And to face the facts, there is a change that one of my next clients will not use Groovy/Grails, but just Java and one of the 1,000,000 Java frameworks outhere. So with my hobby project I will be up to date with the latest Java and Spring 3.0 expertise!



But let's get back to the title of this post: ConfigSlurperPlaceholderConfigurer for Spring. In my hobby project I needed the concept of per environment configuration. Yes, in Grails you have it out-of-the-box but in Spring not.

I searched the web and found some interesting thoughts:


What I like about Grails' concept of per environment configuration is that you can define global config properties and just override them in a specific environment if needed. So if 90% of my properties are the same for each environment I don't need to specify them for all environment but just once.

This concept was not supported by the RuntimeEnvironmentPropertiesConfigurer, and also not by the GroovyPlaceholderConfigurer. I know the GroovyPlaceholderConfigurer uses a Groovy ConfigSlurper and this does support per environment configuration (Grails uses it!). So I decided to create a proper ConfigSlurperPlaceholderConfigurer which also supports different environments using a system property. At the end this is how my Spring config now looks:

Example XML context definition:

<bean class="org.springframework.beans.factory.config.ConfigSlurperPlaceholderConfigurer">
<property name="environment" value="#{systemProperties['runtime.environment']}" />
<property name="defaultEnvironment" value="production" />
<property name="location" value="/WEB-INF/config.groovy" />
</bean>

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>${dataSource.driverClassName}</value></property>
<property name="url"><value>${dataSource.url}</value></property>
<property name="username"><value>${dataSource.username}</value></property>
<property name="password"><value>${dataSource.password}</value></property>
</bean>


Example config.groovy:

dataSource {
driverClassName = "org.hsqldb.jdbcDriver"
username = "sa"
password = ""
}
environments {
development {
dataSource {
url = "jdbc:hsqldb:mem:devDB"
}
}
test {
dataSource {
url = "jdbc:hsqldb:mem:testDb"
}
}
production {
dataSource {
url = "jdbc:hsqldb:file:prodDb;shutdown=true"
password = "secret"
}
}
}


I created a new issue in Spring JIRA to add this to core. If you are interested you can vote for the issue. It also contains the source code if you want to use it in your own project.

7 comments:

ruben01 said...

hi, I have been using a propertyplaceholder using a groovy ConfigSlurper for the last year at work, and can confirm that it's great.

having a single war that can be deployed in every environments is really useful specially combined with something like hudson.

The only features that I think are missing (from the ConfigSlurper) are :
1) the ability to use some kind of inheritance of environments, using something like development_user1, and development_user2 where I can override some properties of the development environment.
2) late resolution of properties. If I declare a host and a url that uses the host (http://${host}/example) it is resolved in the default environment. I cant override the host in the other environments

Marcel Overdijk said...

@Ruben: late resolution in CofigSlurper would be great yes. Perhaps file a enhancement request at the Groovy project?

index.php said...

Isn't a default environment of "production" quite dangerous?

Marcel Overdijk said...

@index.php Yes I agree, this is just an example. You can set it to development or even leave that property out of the bean config.

I'm using "production" as default as I can't set system properties on Google App Engine.

mrhaki said...

Aside from the fact that the ConfigSlurper is of course very "Groovy", there is another solution to have a per environment configuration in a Spring enviroment.
I've used Apache Cocoon 2.2 in some projects a last year and discovered the Spring Configurator component. This component can be used standalone and supports a flexible way to customize properties per environement. I've written a blog series about the component.

Marcel Overdijk said...

Thanks @mrhaki, I will have a look at your blog series tonight.

Jeff Johnston said...

I am one of the developers behind Configleon and I just wanted to thank you for giving a link back to the project!

Just one point of clarification. Configleon has per environment and per server context configs. This allows the common attributes to be defined in a global file and then overridden at the environment and server context.

So I believe that the things some of your posters are looking for, such as ruben01, is solved by Configleon's ability to override any attribute when Spring loads.