Spring 3.1 Unified property management: before and after

My pet project

Sometimes in between, I work on a little pet project of mine.

If you download the Oracle XE Database, you get an HR example schema with it. You can check out the HR schema’s layout and contents on this link.
My idea was to create an enterprise application on this schema to practice using new frameworks or new features.
At this moment I’m developing a Spring Web MVC application backed by Spring and JPA.

The problem description

When I’m at work, during lunch-time, I’d like my application and tests to be run on the real Oracle database.
But when I continue to work on it at home, I’d just like to run the tests on an in-memory HSQLDB database.

So I’ll need two different configurations.
I don’t want to have to copy or switch configurations all the time and I want to re-use as much of the general configuration as possible without resorting to copy-paste behavior.
How can we accomplish such a thing?

Before: Maven profiles FTW

I can use different Maven profiles for both databases. One oracle profile, and one hsqldb profile.
When I want to switch, I just pass the profile to be used with the Maven build. The Oracle profile will be set as default.
When a profile is activated, I will use filtering during the Maven build to filter database configuration properties from one properties file (oracle.properties or hsqldb.properties) to the database.properties and hibernate.properties files. The database.properties file will then be used as a property-placeholder to setup the dataSource Spring bean.

Here’s an example. You can download the source code from my public Dropbox folder: hr-maven-profiles.zip.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	...
	<profiles>
		<profile>
			<id>oracle</id>
			<activation>
				<activeByDefault>true</activeByDefault>
			</activation>
			<build>
				<resources>
					<resource>
						<directory>src/main/resources</directory>
						<includes>
							<include>**</include>
							<include>**/**</include>
						</includes>
						<filtering>true</filtering>
					</resource>
				</resources>
				<filters>
					<filter>${basedir}/src/main/resources/META-INF/config/oracle/oracle.properties</filter>
				</filters>
			</build>
		</profile>
		<profile>
			<id>hsqldb</id>
			<build>
				<resources>
					<resource>
						<directory>src/main/resources</directory>
						<includes>
							<include>**</include>
							<include>**/**</include>
						</includes>
						<filtering>true</filtering>
					</resource>
				</resources>
				<filters>
					<filter>${basedir}/src/main/resources/META-INF/config/hsqldb/hsqldb.properties</filter>
				</filters>
			</build>
		</profile>
	</profiles>
</project>

With each profile, I activate filtering on all resource files. I also specify what properties file to use to get the property values from. These property values will be filtered into the specified resource files (here, all of the resource files).
You can find more information about the filter and filtering element on the following links:
filter: http://maven.apache.org/pom.html#BaseBuild_Element
filtering: http://maven.apache.org/pom.html#Resources

More specifically, with the hsqldb profile I specify that filtering should be performed on all resource files in the src/main/resources folder and all of it’s sub folders. These will be used as resources on the classpath.
The file used to fetch the values from is the ${basedir}/src/main/resources/.../hsqldb.properties file. When Maven executes the build and performs the tests, these values will be filtered in the files where the property-placeholder key’s are used.

These are the property values for the hsqldb profile:

username=sa
password=
url=jdbc:hsqldb:mem:hr
driverClassName=org.hsqldb.jdbcDriver

dialect=org.hibernate.dialect.HSQLDialect
hbm2ddl.auto=create-drop
hbm2ddl.import_files=/import.sql

They will be filtered in files that use the property-placeholders, respectively database.properties and hibernate.properties:

database.username=${username}
database.password=${password}
database.url=${url}
database.driverClassName=${driverClassName}
hibernate.dialect=${dialect}
hibernate.hbm2ddl.auto=${hbm2ddl.auto}
hibernate.hbm2ddl.import_files=${hbm2ddl.import_files}

After that, the database.properties file is specified as a property-placeholder in the Spring applicationContext.xml configuration file.
The hibernate.properties file will be used automatically by Hibernate when the persistence context is initialized.

<?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"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">

	<context:property-placeholder location="classpath*:META-INF/config/database.properties" />
	...
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="username" value="${database.username}" />
		<property name="password" value="${database.password}" />
		<property name="url" value="${database.url}" />
		<property name="driverClassName" value="${database.driverClassName}" />
	</bean>
	...
</beans>

As you can see, the keys of the property value to filter and it’s respective Spring property-placeholder cannot be the same, otherwise Maven won’t be able to know where to filter the value’s into.
If you use the same property name for Maven filtering as for the Spring property-placeholder, you’ll get the following Exception message:
Invalid bean definition with name 'dataSource' defined in class path resource [META-INF/config/applicationContext.xml]: Circular placeholder reference 'database.username' in property definitions

To summarize:

  • Specify different profiles for the different configurations
  • Enable filtering within these profiles, by specifying either a properties file to fetch the values from or by specifying the property key-value pairs in the Maven profiles itself.
    If separate properties files are being used, specify all key-value pairs in those properties files
  • When building the application or running tests through Maven, pass the desired profile by using the -P option
  • Maven will filter the key-value pairs into the specified resources and replace the property-placeholders with the specified values
  • The filtered files will be used by Hibernate and Spring (the latter through a property-placeholder element) to configure the dataSource bean and the persistence context

After: Spring Unified Property Management

When I continued reading the Spring reference documentation, more specifically the part about the new features and enhancements in Spring 3.1 and the PropertySource abstraction, I realized that the previous solution was too complex and I could rely only on Spring to solve the problem.

You can download the example on the following link: hr-spring-propertysource.zip.
I also refer to the following SpringSource blog post: Spring 3.1 M1: Unified Property Management.

With the PropertySource abstraction, the Spring Environment object performs a search over a set of PropertySource objects. A PropertySource object is an abstraction over any source of key-value pairs.
Per default, the Spring Environment searches in the JVM system properties and the system’s environment variables. This way, you can pass environment variables to the application using the -D option.

I don’t specify any profiles anymore in the Maven pom.xml. This makes the Maven configuration much more simple and concise. I’ll pass environment variables when running the tests to specify which database to use.

The only thing I have to do is put the environment variable key (in this case, “database”) as a placeholder in the property-placeholder element.
When Spring initializes the container, it will use the PropertySource abstraction to search for the “database” key and replace it’s value into the placeholder.
This way, I can specify the sub folder to fetch the database.properties file from, which will be used for the property-placeholders in the applicationContext.xml.

As an example, I’ll show you the HSQLDB database.properties file, and a part of the applicationContext.xml.
I removed the hibernate.properties file because when using the PropertySource abstraction, the hibernate.properties file cannot be filtered anymore.
This can only be used in the applicationContext.xml files or within annotations in your Java classes.
Therefore, instead of relying on hibernate.properties or persistence.xml, I specify the Hibernate properties directly in the entityManagerFactory Spring bean.

database.username=sa
database.password=
database.url=jdbc:hsqldb:mem:hr
database.driverClassName=org.hsqldb.jdbcDriver

hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.hbm2ddl.auto=create-drop
hibernate.hbm2ddl.import_files=/import.sql
<?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"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">

	<!-- 
	I make use of the Environment abstraction and property sources in Spring to define which database to use.
	The environment variable 'database' has to be set one way or another to be able to run the application or tests.
	-->
	<context:property-placeholder location="classpath*:META-INF/config/${database}/database.properties" />
	...
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="username" value="${database.username}" />
		<property name="password" value="${database.password}" />
		<property name="url" value="${database.url}" />
		<property name="driverClassName" value="${database.driverClassName}" />
	</bean>

	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="persistenceUnitName" value="persistenceUnit" />
		<property name="jpaProperties">
			<props>
				<prop key="hibernate.dialect">${hibernate.dialect}</prop>
				<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
				<prop key="hibernate.hbm2ddl.import_files">${hibernate.hbm2ddl.import_files}</prop>
			</props>
		</property>
	</bean>
	...
</beans>

When running the tests, you should pass the desired database using the -D option on command line or environment variables in Eclipse.
An example of this can be: ... -Ddatabase=oracle or ... -Ddatabase=hsqldb.
Respectively, Spring will search the database.properties file in the oracle sub folder or the hsqldb sub folder, and use the specified values to replace the property placeholders.

This concludes my post about Spring’s Unified Property Management for using different configurations.

What about Spring Bean Definition Profiles?

The new Spring @Profile annotation is also a possible candidate to get this kind of behavior.
But when using the @Profile annotation, I don’t think I would be able to let the integration tests run on the Oracle database first, and then switch it to the HSQLDB database without having to change some code.

Using the @Profile annotation, you can set the @ActiveProfiles annotation on your test classes to activate one or more profiles. To switch, you’ll have to change the annotation value to another profile value.
I can only switch the active profile depending on the environment, like DEV or PRD, by activating the hsqldb profile in the test classes, and specifying the oracle profile as an spring.profiles.active init-param in web.xml. This doesn’t solve my initial problem of being able to easily switch databases for my test cases.

Do you think one can use the Spring @Profile annotation without having to change the annotation’s value to switch profiles?
If so, please feel free to comment to this post and share your ideas.

I hope you enjoyed this blog post, and eagerly await your comments and suggestions.

Advertisements

About Steve Schols

I am Steve Schols, a senior Java consultant working for several clients in Belgium. I mainly blog about the Java language and relevant frameworks and technologies. All statements made here are solely my own and do not represent the opinion of my employer, colleagues or my clients.
This entry was posted in Java, Spring and tagged , , , , , , , , . Bookmark the permalink.

5 Responses to Spring 3.1 Unified property management: before and after

  1. Pingback: JavaPins

  2. springeip says:

    Your final solution was possible in Spring before version 3.1.
    The @Profile features are meant for cases where the bean definitions are different, not just property values, like in your example. A common use case for @Profile is switching between a properties file based config and a JNDI based one.

    My experience with Maven is that it’s not the best place to configure runtime properties. It’s just not flexible enough and you can’t adjust config easily after the build is completed.

    The profile mechanism you have is good for setting up integration test scenarios that run at build time, however. We use something similar to point to a special db instance that is meant for automated persistence code testing using Unitils and DBUnit.

    • Steve Schols says:

      Hi springeip.
      Thanks for your comment.

      Indeed, I thought that property resolvement like that was already possible prior to Spring 3.1, but perhaps the way of resolving properties in PropertySources is now changed or abstracted differently.
      I couldn’t really get that from the reference documentation.

  3. Fruzenshtein says:

    Nice article, but unfortunately with XML-config

  4. Rod says:

    In my project I found no other solution than to create a test class hierarchy in order to run the same tests with different databases.

    @ActiveProfiles({ “test” })
    abstract class AbstractDatabaseTest {… the actual tests here .. }

    @ActiveProfiles({ “mysql” })
    public class MySqlTest extends AbstractDatabaseTest { … MySQL ping here …}

    @ActiveProfiles({ “mongo” })
    public class MongoTest extends AbstractDatabaseTest { … MongoDB ping here …}

    I have further subclasses for H2, Apache Derby, and a key-value store.

    The subclasses contain code that checks whether the desired database is available. If not available the tests are skipped (via JUnit assume in a @before method).

    There are different data source and repository beans for each profile (I use Spring data).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s