Spring 3.1 GA was announced early this month. One feature we have been looking for some time is the new environment abstraction with bean definitions profile. This is covered in details in two excellent articles (here and here).
In short, Spring 3.1 permits the use of nested <beans/>
elements with a new profile attribute. If the profile defined in that attribute is enabled, the bean definitions and import statements are parsed and included in the application context. Consider for instance the following bootstrap-environment.xml
configuration file:
<?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"> <description> Bootstrap the environment according to the active profiles. </description> <beans profile="env1"> <import resource="classpath:/META-INF/app/bootstrap/env1.xml"/> </beans> <beans profile="env2"> <import resource="classpath:/META-INF/app/bootstrap/env2.xml"/> </beans> </beans>
This basically states that if env1 is enabled, the env1.xml
configuration file will be included in the application context. We could assume here that this would load something specific to that environment.
We have applications that can either deploy on a full blown JavaEE compliant server or on a lightweight container such as Apache Tomcat. A standard JavaEE server will bring a JTA transaction manager for distributed transaction and a message broker for JMS-based communication. A lightweight container, however, does not provide that infrastructure out-of-the-box so we have to bridge the gap somehow, yet keeping that maximum flexibility for customization.
Internally, we use a web application skeleton. This skeleton contains all the resources file, the web.xml and the dependencies required to build the application. We have then one project per environment were we can customize the skeleton at will, including overriding files. For that we use the overlay feature of the maven war plugin. In the end, the customization required to start the application is restricted to the environment itself.
It would be much better if we didn’t have to provide these projects in the first place and simply use a properties file containing the profile(s) to activate. This use case is not supported out-of-the-box: Spring can determine which profile(s) to activate based on an environment property, a system property or a servlet context parameter. We do not want our users to hack their startup scripts to deploy our applications and since our web.xml is shared, we can’t really use the servlet context parameter either.
Fortunately, Spring offers an access to the lower-level API for more fine grained customization. A custom strategy simply needs to implement the ApplicationContextInitializer interface and register it properly before the context is refreshed. We have a showcase on github demoing a simple implementation of that interface looking up for a properties file at a default location (META-INF/application-context.properties
). Once you have that in your project, you only need to add an extra <context-param/> to your web.xml
:
<context-param> <param-name>contextInitializerClasses</param-name> <param-value>com.bsb.showcase.spring.profile.ProfilesActivationApplicationInitializer</param-value> </context-param>
To enable a set of profiles, place a properties file at the default location in the classpath of your application:
# # The spring profiles to activate # spring.profiles.active=env2
Profiles activation relies now on a simple properties file. You can add as many profiles as you want and the value can even be filtered by your build tool if you cannot hardcode the value. The advantage of this solution is that we can still use our shared infrastructure and it does not require any system or environment property.