Over the weekend I was debugging a peculiar issue with ActiveMQ (turned out to indeed be a bug that had been reported a few months ago: https://issues.apache.org/jira/browse/AMQ-3359 ) and I became curious about how the components were being loaded up, wired together, and eventually started by the infrastructure code. If there is any interest in that, and I have time, I may blog about that later, but I stumbled upon a pretty cool way of creating your own custom namespaces for spring application context configuration files. Well, I stumbled upon how it’s done in ActiveMQ. I didn’t see much documentation online about how they did it, so I loaded up the remote debugger and figured it out.
Spring XML configuration can be quite verbose if you’re using the basic constructs of beans and properties. Solutions such as autowiring to try to keep the config files smaller just add confusion especially if you’re new to a project that uses a lot of autowiring. A better solution is to use namespaces that allow you to create your own XML elements and keep the syntax more concise and clear.
The Spring documentation has always been a valuable part of using the Spring Framework, and adding custom namespaces and hooking it into the config files is clearly explained . See the appendix of the online documentation to see the details, but basically you follow some conventions, create a schema for your new elements, create your own namespace handler and bean definition parsers. It’s not difficult, but it’s more manual steps and parsing classes than you should have to create.
That is where xbean-spring comes into play.
The xbean-spring project (and its accompanying ant and maven plugins) allows you to create your own namespaces for spring configuration files while taking care of all of the boilerplate steps for you. It allows you to map a set of pojo java beans, using annotations, to the your custom spring config elements and handles creating the XSD, hooking into the namespace handlers, and parsing the bean with a bean definition parser. In other words, all you do is annotate your existing java beans and let xbean-spring take care of the rest. This makes creating your own custom xml configuration elements much easier, and as previously mentioned, using custom xml configuration namespaces allows your configuration to be much more concise and readable.
Add the following dependency to your pom.xml for xbean-spring:
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>${xbean-version}</version>
</dependency>
First thing you need to do to enable xbean-spring is annotate your classes with the xbean-spring annotations. The project relies on comment/JavaDoc style annotations and uses the awesome QDox project to parse out these annotations (not necessary to know, but interesting none-the-less).
Here’s an example spring application context with a custom namespace and config elements:
<?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
http://christianposta.com/schema/core http://christianposta.com/schema/core/core.xsd">
<simple xmlns="http://christianposta.com/schema/core" myProperty="testMe">
<simpleController>
<simpleController controllerName="testMeToo" />
</simpleController>
<controllers>
<complexController pattern="testPattern" />
<complexController pattern="testPattern2" />
<complexController pattern="testPattern3" />
</controllers>
</simple>
</beans>
Enabling this custom namespace requires three pretty straight forward and easy things none of which require writing custom XSDs or parsers.
- Annotate the pojos you want to map to these custom elements
- Add the maven xbean-spring plugin to your pom.xml file, configure it
- Create your spring config file and use the xbean-spring ApplicationContext subclasses
Here are the steps for creating the above spring config (source code is included), or as a git repo at github.com.
1) Annotate the pojos
For this sample, I’ve put together three pojos:
com.christianposta.postaprojects.xbeanspring.SimpleBean
, which will represent the top-level element (<simple />). The code is below, but the only thing required for the xbean-spring integration is the @org.apache.xbean.XBean
annotation in the comments. And since this is the root element, there is another prop to set. See the code:
[java]
/**
* @org.apache.xbean.XBean element="simple" rootElement="true"
*/
public class SimpleBean {
private String myProperty;
private SimpleController simpleController;
private List<ComplexController> controllers;
public String getMyProperty() {
return myProperty;
}
public void setMyProperty(String myProperty) {
this.myProperty = myProperty;
}
public SimpleController getSimpleController() {
return simpleController;
}
public void setSimpleController(SimpleController simpleController) {
this.simpleController = simpleController;
}
public List<ComplexController> getControllers() {
return controllers;
}
/**
* @org.apache.xbean.Property alias="controllers" nestedType="com.christianposta.postaprojects.xbeanspring.ComplexController"
*/
public void setControllers(List<ComplexController> controllers) {
this.controllers = controllers;
}
}
[/java]
There are three javabean properties in this class, each demonstrating a different type of configuration. The myProperty field maps to the “myProperty” attribute of the simple element: <simple myProperty=”value” />. The simpleController field maps to a complex type which is a child of the <simple> element. For xbean-spring to know this, and map it properly, add the @org.apache.xbean.XBean
annotation to the com.christianposta.postaprojects.xbeanspring.SimpleController
class. Then xbean-spring will figure it out automatically and know that the <simpleController> element maps to that bean:
[java]
/**
* @org.apache.xbean.XBean
*/
public class SimpleController {
private String controllerName;
public String getControllerName() {
return controllerName;
}
public void setControllerName(String controllerName) {
this.controllerName = controllerName;
}
}
[/java]
The last property, controllers, is a little more complex, but still very straight forward. Above the setter/mutator for that property, is another xbean annotation that specifies to what type to map the java.util.List
. The @org.apache.xbean.Property
annotation along with the nestedType
attribute let xbean-spring figure out how to map the elements from <controllers><complexController/></controllers> to the com.christianposta.postaprojects.xbeanspring.ComplexController
class. That class also needs the @org.apache.xbean.XBean
annotation to participate:
[java]
/**
* Sample ComplexController bean
* @org.apache.xbean.XBean
*/
public class ComplexController {
private String pattern;
public String getPattern() {
return pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
}
[/java]
After figuring out how you want the structure of your custom namespace to look, including all sub-elements and attributes, and after applying all the annotations, then you’re done with step #1. No code, no XSD, no parsers. Easy.
2) Add the xbean-spring maven plugin to pom.xml
Upon running maven to compile your code, you need the xbean-spring plugin to do the magic of binding the xbean annotations to all of the spring boilerplate code (building the XSD, the spring.handlers, and the spring.schemas files). Add the following plugin declaration to your pom.xml:
<plugin>
<groupId>org.apache.xbean</groupId>
<artifactId>maven-xbean-plugin</artifactId>
<version>${xbean-version}</version>
<executions>
<execution>
<phase>process-classes</phase>
<configuration>
<namespace>http://christianposta.com/schema/core</namespace>
<schema>${basedir}/target/classes/core.xsd</schema>
<outputDir>${basedir}/target/classes</outputDir>
<generateSpringSchemasFile>true</generateSpringSchemasFile>
<strictXsdOrder>false</strictXsdOrder>
</configuration>
<goals>
<goal>mapping</goal>
</goals>
</execution>
</executions>
</plugin>
An explanation of the config options for the plugin:
namespace: this is the default namespace that’s used for the XSD and the elements from it. It should match what you declare in your spring config file for your new namespace
schema: this is the location and name to put the generated schema
outputDir: this is where the xbean-spring and spring boilerplate properties files will go (under META-INF mostly, but some others under the /classes root)
generateSpringSchemasFile: whether to generate spring.schemas file. optional if you want to supply your own
strictXsdOrder: available from xbean-spring 3.9 and above, allows the namespace elements to be in any order or strict order.
After running any of the maven lifecyles that runs the "compile" lifecycle (e.g., package, install), you'll see in the target/classes/ folder all of the artifacts produced by xbean-spring that hook into the spring namespace system for building custom namespaces. You'll see the XSD is generated and located wherever you specified in the schema property of the plugin. You'll also notice that target/classes/META-INF also contains the spring.handlers and spring.schemas properties files that are required by spring. These were generated by the plugin.
3) Create your applicationContext file and load up the Spring Context
See the applicationContext from above. You also need to use the xbean-spring aware ApplicationContext classes. There are four of them, and they map to the ApplicationContext classes found within spring, but with added support for xbean-spring:
org.apache.xbean.spring.context.ClassPathXmlApplicationContext
org.apache.xbean.spring.context.FileSystemXmlApplicationContext
org.apache.xbean.spring.context.ResourceXmlApplicationContext
org.apache.xbean.spring.context.XmlWebApplicationContext
Like I said, they’re exactly the same as the ones that come with spring, but with added support for xbean. Note, the only one that doesn’t have a counterpart in the core spring is ResourceXmlApplicationContext.
See this unit test from the accompanying source code:
[java]
@Test
public void testBeanGetsCreated() {
// Got to use the XBean version of the Application Context
ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext-test.xml");
assertEquals(1, context.getBeansOfType(SimpleBean.class).size());
SimpleBean bean = context.getBean(SimpleBean.class);
assertEquals("testMe", bean.getMyProperty());
SimpleController controller = bean.getSimpleController();
assertEquals("testMeToo", controller.getControllerName());
List<ComplexController> controllers = bean.getControllers();
assertEquals(3, controllers.size());
}
[/java]
That’s it! Feel free to look at the source code for the xbean-spring project: http://svn.apache.org/repos/asf/geronimo/xbean/trunk/xbean-spring/
Please feel free to comment if you know of more options and more advanced xbean mapping. I realize there are more complicated mappings, so please advise!