Single Spring Application Deployment for both Local and CloudFoundry.com Servers
Join the DZone community and get the full member experience.
Join For FreeIn my previous post I showed how it’s possible, using Spring 3.0, to deploy a database application to CloudFoundry.com and what changes are needed for a CloudFoundry.com datasource. In this post, I’m going to show how a Spring 3.1 application can be configured at runtime to use either a local MySQL database or a CloudFoundry.com MySQL database thus allowing a single deployable Spring application to be deployed either locally or on CloudFoundry.com.
Deploying a Spring application to CloudFoundry.com does not mandate the use of Spring 3.1, however Spring 3.1 makes the process much easier due to the new profile features. So, first off, we must upgrade to Spring 3.1
Upgrading to Spring 3.1
To upgrade a Spring STS application to use Spring 3.1 is an easy procedure. In the project’s pom.xml file, we first need to change the version number of Spring to 3.1.0.M1. I say it’s easy with a STS application as everything else (for example, Spring’s Milestone Maven repository) is already preconfigured in the pom.xml file.
If you’re not using Maven, you can download Spring 3.1 M1 from the Community Download page.
Having upgraded the version in Maven, we need to change the namespaces for any application context files. This is as straightforward as changing schema locations to be xxx-3.1.xsd instead of xxx-3.0.xsd
Upgrading to Spring 3.1.M1 will unfortunately have some side effects within STS. After upgrading, you may see that some lines within application context files are flagged with errors such as:
Error occured processing XML 'org/springframework/core/convert/support/ArrayToCollectionConverter'. See Error Log for more details
This is because STS (v2.6) does not currently fully support Spring 3.1 due to some API changes in 3.1. Support for Spring 3.1 can be tracked on SpringSource’s Jira issue #1655.
Configuring Bean Definition Profiles
After configuring an application to use Spring 3.1, we can start using the new profiles feature to specify both a cloud datasource and a local datasource.
In this XML fragment, we can see that there are embedded <beans /> elements inside the main <beans /> element. This is a new feature of Spring 3.1 that allows different configurations to be easily specified within one configuration file. Here we’ve defined a bean called dataSource as a JDBC datasource and as a CloudFoundry.com datasource. The profile attribute allows us to specify which profile the bean is to be instantiated in. So, in the “default” profile, i.e. a local profile where development and testing is most likely to be performed, a JDBC datasource is defined. The “cloud” profile specifies a datasource for CloudFoundry.com. A bean profile can be set to cover multiple profiles, but in this example, the dataSource bean is either a local JDBC datasource or a CloudFoundry.com datasource and can never be defined in both cases.
For a detailed description of Spring’s bean definition profiles, check out Chris Beams blog post.
Choosing a Profile at Runtime
The final stage of changing an application to use the bean definition profiles defined above is to add a class that implements the org.springframework.context.ApplicationContextInitializer interface. This interface allows the spring container to be customised before it in initialized. In our instance, we can hook into this and, depending on the environment, set the active profile to be the “default”, local JDBC datasource, or the “cloud” CloudFoundry.com datasource.
public class Initializer implements
ApplicationContextInitializer<ConfigurableWebApplicationContext> {
protected final Log logger = LogFactory.getLog(getClass());
public void initialize(ConfigurableWebApplicationContext ctx) {
ConfigurableEnvironment environment = ctx.getEnvironment();
ApplicationInstanceInfo instanceInfo = new CloudEnvironment()
.getInstanceInfo();
if (instanceInfo == null) {
// We are running locally.
logger.info("Setting default profile.");
environment.setActiveProfiles("default");
} else {
// We are running in the cloud.
logger.info("Setting cloud profile.");
environment.setActiveProfiles("cloud");
}
}
}
In this class, we attempt to get the instanceInfo of the CloudEnvironment class. If this returns null, the application is not running on CloudFoundry.com and the local “default” bean definition profile is set as the active profile. If, however, a valid instance is returned, then the app is running on CloudFoundry.com and the “cloud” bean definition profile is set as the active profile.
That’s all there is to it and now the application can be developed and deployed as easily locally as it can on CloudFoundry.com
The sample code used in this post can be found on GitHib at https://github.com/daveys/spring-todo
Opinions expressed by DZone contributors are their own.
Comments