Mutant Mumblings

Thursday Aug 30, 2007

Saving Time with Spring Extensible XML Authoring

The Problem

When building CRUD screens for hibernate objects you end up building and configuring many different application layers for each and every one of your CRUD objects.  For example I usually end up needing the following objects when working with JSF:

  • A DAO object to handle data access.
  • A Service Object to handle any business logic I may want to reuse independent of the view
  • A request scoped form action/backing bean used to handle display controller logic for create/edit forms.
  • A request scoped paged list action/backing bean for handling display logic for displaying paged lists of my objects.
  • A session scoped ID holder for storing the id of the current object I am editing
  • A session scoped Pagination info bean that stores all the current paging/sorting information for my list page.
  • For many of my CRUD objects I end up configuring a request scoped bean that represents my currently selected object

That is a lot of objects to build and configure!  And that?s just for one CRUD type, I may have dozens in my application.  Thankfully CRUD logic is very independent of the type actually being persisted.  I can build a BaseDao, BaseService, BaseAction and BasePagingAction to handle all the logic and just inject the proper hibernate persisted Class object into the DAO instance.  So I really only have to write the CRUD code once for the application (or if I?m smart, write it once for a company and reuse it in library form in all my projects,) and then just configure different instances of the base classes for each of my CRUD types. 

 

Now I still have a problem, I have seven objects to configure for each of my CRUD types.  And the configuration is going to be almost identical for each type, just a different name for each, and a different Class injected into the DAO.  They all get wired together in exactly the same way for each type, the DAO is injected into the service, and the service is injected into the action etc.  But this isn?t simple configuration either, some beans are request scoped, others are session scoped, some have init methods that must be called while others do not.  This can easily end up being 100 lines of XML per CRUD type.  I don?t know about you, but I?m not going to type 100 almost identical lines of XML for each of my types.  I?m going to either cut and past the entire thing (and forget to change 2 or 3 names each time as well as never refactor anything ever again so that I don?t need to change thousands of lines of xml) OR I?m going to find a better way.

Spring Extensible XML Authoring: A better way

Since version 2.0, Spring has offered me a fairly simple solution to my problem.  Developers can now define their own tags to be used along side the old style bean definition syntax.  Out of the box Spring 2.0 demonstrates the power of this feature by giving us greatly simplified methods of configuring AOP and transaction management.  I want to use this same feature to let me transform 100 lines of xml configuring seven beans into this:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sai="http://www.sourceallies.com/spring/crud"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.sourceallies.com/spring/crud http://www.sourceallies.com/spring/crud/crud-config.xsd">

<sai:crudConfig baseName=?"person"? queryClass=?"com.sourceallies.Person?" />

</beans> 

 It turns out this is really fairly easy, but there are many files involved.

Step 1: Define the Schema

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.sourceallies.com/spring/crud"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.sourceallies.com/spring/crud"
elementFormDefault="qualified">

<xsd:element name="crudConfig">
<xsd:complexType>
<xsd:attribute name="baseName" use="required" type="xsd:string" />
<xsd:attribute name="queryClass" use="required" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:schema>

This is a very simple schema with just one element containing two attributes allowing us to define the name of our CRUD type and the underlying query class name.

Step 2: Implement a BeanDefinitionParser

This is where the meat of the implementation lies.  Given a DOM element, we need to construct all the bean definition metadata for our seven beans.

 

public class CrudConfigBeanDefinitionParser implements BeanDefinitionParser {
public AbstractBeanDefinition parse(Element element, ParserContext parserContext) {
String baseName = element.getAttribute("baseName");
String queryClass = element.getAttribute("queryClass");

BeanDefinitionRegistry registry = parserContext.getRegistry();

registerHolder(baseName, registry);

registerPageInfo(baseName, registry);

registerDao(baseName, queryClass, registry);

registerService(baseName, registry);

registerPagedAction(baseName, registry);

registerAction(baseName, registry);

registerCurrentBean(baseName, queryClass, registry);

return null;
}
protected void registerDao(String baseName, String queryClassName, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition("com.sourceallies.BaseDaoImpl");
builder.addPropertyReference("sessionFactory", "sessionFactory");
builder.addPropertyValue("queryClass", queryClassName);

registry.registerBeanDefinition(getDaoName(baseName), builder.getBeanDefinition());
}

protected String getDaoName(String baseName) {
return baseName + "Dao";
}

.
.
.
}

This is just a partial implementation, but you can get the general idea.  Spring will call the "parse" method passing in the DOM element for our crudConfig tag.  We strip out the baseName and queryClass attribute and then use them to build the metadata.  The code mimics the original xml file format fairly closely, i can add property values and property references to my bean defs, just like in the xml.  One of the best parts of doing things this way is that I can now easily enforce naming standards for my beans,  the DAO for the person object will be called personDao and the DAO for the bulding object will be buildingDao.  Same goes for the service, the actions and the rest of the beans!  Instant convention over configuration.

Step 3: The Rest of the Files

We have three things left to do:

  1. Register the CrudConfigBeanDefintiionParser to be used to parse the crudConfig tag using a NamespaceHandler
  2. Register our NamespaceHandler with our custom namespace
  3. Register our schema file(xsd) with our schema location

The NamespaceHandler is easy enough:

public class FrontNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("crudConfig", new CrudConfigBeanDefinitionParser());
}
}

You register the NamespaceHandler with your namespace by creating a file in your META-INF directory with the name "spring.handlers"  This is just a simple property file:

 

http\://www.sourceallies.com/spring/crud=com.sourceallies.CrudNamespaceHandler

And finally you register your schema file with your schema location by creating a file in your META-INF directory with the name "spring.schemas" also a simple property file:

 

http\://www.sourceallies.com/spring/crud/crud-config.xsd=com/sourceallies/crud-config.xsd

Beyond the Basics

This is really just a start,  I'm not going to always be able to get away with just using the base classes.  In some cases i'm going to want to swap in a more specific version of Services, DAOs, Actions etc. without sacrificing the consistency I get using my custom namespace.  This just involves extending my xsd file and extending my parser.  For example, my parser can currently handle something like this:

 

<sai:crudConfig baseName="location" queryClass="com.sourceallies.example.Location">
<sai:action class="com.sourceallies.example.LocationAction">
<sai:property name="currentPolicy" ref="currentPolicy" />
</sai:action>
</sai:crudConfig>

This lets me override the class used for my Form Action as well as add extra properties on top of the normal additions(IdHolder and Service)  But i dont have to worry about specifying the init method name, or the scope, and the bean name will continue to follow my conventions. 

Conclusion

 Spring's extensible XML authoring features are an easy way to greatly increase the amount of consistency and clarity in your applications configuration.  If you find yourself repeating the same configuration patterns over and over again, there is now a better way!

 

To learn more,  check out Appendix B in the Spring Reference Guide 

 

Calendar

Feeds

Search

Links

Navigation