JBoss ESB is powerful, easy to use, opensource SOA infrastructure. It combines a wide range of technologies to rapidly create powerful services that can process message from various resources. It provides out-of-the-box suppport for message validation, transformation, routing, notifcation, etc.
Two areas in which functionality and support had been lacking over the years were:
- Support for unit testing: because your custom actions (ESB units of logic) needed to extend from the JBoss ESB framework classes, actions were hard to unit test.
- Spring integration: Although standards like EJB3 (3.1) and CDI are gaining popularity, a lot of companies and developers still use the Spring Framework to provide Dependency Injection (DI) functionality. Although JBoss ESB does provide an AbstractSpringAction to use Spring in an ESB context, the support is still very basic.
The first issue, unit testing your JBoss ESB actions, has been solved with the support for annotations introduced in JBoss ESB 4.9. The annotations make it possible to create a JBoss ESB action by annotating a simple POJO. This eases the development of custom actions, but more importantly, it makes unit testing your actions a lot easier, as one does not have to worry about JBoss ESB specific classes and functionality anymore (e.g. you do not have to build a ConfigTree instance anymore as actions can now have a default constructor).
The second issue was solved by the AbstractSpringAction, but using this action has a number of downsides:
- One has to extend the AbstractSpringAction, tying your logic to JBoss ESB again. Something we tried to prevent with the ESB annotations.
- A Spring ApplicationContext is created by every single ESB action that extends the AbstractSpringAction. Re-using a Spring ApplicationContext in multiple actions is not possible. This puts a higher (and unnecessary) demand on resources.
We would like to be able to define a Spring ApplicationContext which can be re-used by multiple actions and we would like to be able to retrieve Spring beans from this application context without having to write a lot of plumbing code and without having to extend from JBoss ESB specific classes. Ideally we would like to inject our Spring beans into our actions using annotations, for example via Spring's @Autowired annotation.
By utilizing a number of different Spring utility classes and methods, this functionality is however not really hard to implement. By re-using the approach of Spring's ContextLoader class to load a parent context, we can make sure that a Spring ApplicationContext is loaded only once, but can be referenced many times. The support is based on the correct usage of Spring's ContextSingletonBeanFactoryLocator, which has been specifically designed to access shared BeanFactory instances and allows you to retrieve a reference to an ApplicationContext (BeanFactory). If the ApplicationContext is referenced for the first time, Spring will initialize it, when all references have been destroyed, Spring will destroy it.
The Spring annotation support in JBoss ESB actions is implemented by using functionality from Spring's AutowireCapableBeanFactory. This bean factory contains the method autowireBean(Object exitistingBean), which allows us to programmatically instruct Spring to autowire any existing Object, including JBoss ESB action instances. Every Spring ApplicationContext is able to retrieve such an AutowireCapableBeanFactory.
My JBossEsbSpringIntegration GitHub repository contains a sample project which combines these approaches to autowire a JBoss ESB Action using Spring. The AbstractAutowiredSpringAction is the base class which provides the ApplicationContext loading and autowiring support. Your custom action needs to extend from this class to be autowired. Although your custom action still needs to extend from an abstract base class, this base class is built using JBoss ESB annotations, which prevents your logic from being tied into the JBoss ESB framework.
I've provided an example class called TestDIAction which shows this approach. Because AbstractAutowiredSpringAction uses the JBoss ESB @Initialize and @Destroy annotations to retrieve and destroy the Spring BeanFactory (ApplicationContext) reference, these annotations can not be used in your custom action. Therefore the abstract class provides 2 empty non-final methods, doInitialize() and doDestroy(), which can be overridden in your action class to execute additional initialize and destroy functionality.
One configuration parameter is required when configuring your action in the jboss-esb.xml deployment descriptor, the context-key parameter, which should contain the bean-id of the ApplicationContext to be used, defined in the beanRefContext.xml file. An optional configuration option, locator-factory-selector, can be used to configure the BeanFactoryLocator instance from which the ApplicationContext is obtained. The default value is 'classpath*:beanRefContext.xml'. An example is again provided in the project, see the 'jboss-esb.xml' file in the JBossEsbSpringIntegration project.
The AbstractAutowiredSpringAction can be packaged in a separate JAR and can be included as a library in your JBoss ESB and/or JBoss SOA-Platform instance. This is an initial attempt to implement annotated dependency injection support in JBoss ESB. Feel free to use the code and let me know what you think.
Special thanks go to Dominique Vandensteen for showing me Spring's AutowireCapableBeanFactory functionality.