It is assumed that you know the basics of JBoss ESB.

 

The Problem

 

Recently I have started working with JBoss ESB and was supprised at the lack of resources around the integration of Spring and Hibernate with JBoss esb.  The most common article that I have found relates to using the AbstractSpringAction which creates a Bean Factory for each action that requires spring. If you look at the AbstractSpringAction, when the Action starts up, it simply reads the provided Spring XML from the TreeConfig and constructs a new Xml Bean Factory.   You will find this approach well documented in the sample applications provided in the JBoss ESB download.

 

I did not want to go down this path as our ESB is quite large and utilizes a number of shared beans across a number of actions.  We also had a requirement where we were using a ScheduledEventListener as a listener for one of our services and we needed this listener to utilize a number of existing DAO's to lookup a database and extract some data.  Following the documented AbstractSpringAction pattern, it was not possible to inject the beans we required without creating yet another SpringBeanFactory by hand.

 

After further reseach I discovered the HibernateMBean (org.jboss.hibernate.jmx.Hibernate) which creates a Hibernate Session Factory and regisiters itself with the JNDI context and this lead me to an idea to create a SpringMBean which create a Spring Bean Factory and registers the Bean Factory in JNDI.

 

This solved my issue by allowing me to create a single Spring Bean Factory that can be shared across all my Actions, and in fact any deployable artifact without needing to create a new module such as a SAR or a HAR.

 

The Solution

 

Register the Datasource to use with Hibernate

 

The first step is to register the datasource that you want to use with hibernate.  This is just a standard JBoss datasource registration which is well documented all over the web.

 

The following is contained in example-ds.xml

 

{code:xml}

<?xml version="1.0" encoding="UTF-8"?>

<datasources>

    <local-tx-datasource>

        <jndi-name>ExampleDS</jndi-name>

        <connection-url>jdbc:oracle:thin:@localhost:1521:EXAMPLE</connection-url>

        <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>

        <user-name>example</user-name>

        <password>example</password>

        <min-pool-size>5</min-pool-size>

        <max-pool-size>20</max-pool-size>

    </local-tx-datasource>

</datasources>

{code}

 

Provided this is in the root of your deployment or in the META-INF folder, JBoss will locate and register the datasource within JNDI

Register the HibernateMBean

 

JBoss has conviently provided a usefull MBean: org.jboss.hibernate.jmx.Hibernate which creates a Hibernate Session Factory.  This is also well documented and the JBoss user guide for hibernate deployment methods can be found here

 

For this example, we chose to register the HibernateMBean directly rather than creating a HAR file.  The convient thing about the HibernateMBean is that you do not need to tell hibernate where your hbm configuration files are.  The MBean will automaticly scan the classpath and setup your entities for you.

 

We created the following configuration file which is used fro both our Hibernate and Spring MBean registrations

 

example-service.xml

 

{code:xml}

<?xml version="1.0" encoding="UTF-8"?>

<server>

    <mbean code="org.jboss.hibernate.jmx.Hibernate"

           name="example.esb:service=HibernateSessionFactory,name=ExampleHibernateSessionFactory">

        <attribute name="DatasourceName">java:/ExampleDS</attribute>

        <attribute name="Dialect">org.hibernate.dialect.Oracle10gDialect</attribute>

        <attribute name="SessionFactoryName">java:/hibernate/ExampleHibernateSessionFactory</attribute>

        <attribute name="ShowSqlEnabled">true</attribute>

        <depends>jboss.jca:service=DataSourceBinding,name=ExampleDS</depends>

    </mbean>

</server>

{code}

 

Some important things to note about this mbean registration is the relationship back to the datasource you created in the previous step. 

  • The DatasourceName (java:/ExampleDS) references the datasource you just created. 
  • The SessionFactoryName is the JNDI identifier that you will use to retrieve the Hibernate Session Factory
  • The <depends> node is very important as we cant create the Hibernate Session Factory untill the Datasource has been created and registered with JNDI.

Create the SpringMBean

 

Next we need to create our MBean which will bootstrap the Springframework and register the Bean Factory in JNDI.

 

SpringMBean.java

 

{code:java}

package example.esb.mbean.spring;

 

import org.jboss.system.ServiceMBean;

 

import javax.management.MBeanRegistration;

 

/**

* User: Grant Currey

* Date: 28/04/2011

* Time: 11:43:54 AM

*/

public interface SpringMBean extends ServiceMBean {

    public String getSpringContextXml();

    public void setSpringContextXml(String springContextXml);

}

{code}

 

Spring.java

 

{code:java}

package example.esb.mbean.spring;

 

import org.apache.log4j.Logger;

import org.jboss.deployment.DeploymentInfo;

import org.jboss.soa.esb.client.ServiceInvoker;

import org.jboss.system.ServiceMBeanSupport;

import org.jboss.util.naming.Util;

import org.springframework.beans.factory.BeanFactory;

import org.springframework.beans.factory.xml.XmlBeanFactory;

import org.springframework.core.io.ClassPathResource;

 

import javax.naming.InitialContext;

import javax.naming.NamingException;

 

/**

* User: Grant Currey

* Date: 28/04/2011

* Time: 11:42:13 AM

*/

public class Spring extends ServiceMBeanSupport implements SpringMBean {

 

    private static final String SPRING_BEAN_FACTORY = "SpringBeanFactory";

 

    private static final Logger LOGGER = Logger.getLogger(Spring.class);

 

    private String springContextXml;

    private XmlBeanFactory beanFactory;

 

    /**

     * Convenient method for getting the BeanFactory which this MBean registered in JNDI.

     */

    public static BeanFactory getBeanFactory() throws Exception {

        return (BeanFactory) Util.lookup(SPRING_BEAN_FACTORY, BeanFactory.class);

    }

 

    @Override

    protected void startService() throws Exception {

        LOGGER.debug("Starting SpringMBean");

        if (beanFactory == null) {

            loadAndBindSpringIoc();

        }

    }

 

    @Override

    protected void stopService() throws Exception {

        beanFactory = null;

        unbind();

    }

 

    public DeploymentInfo getDeploymentInfo() {

        try {

            return super.getDeploymentInfo();

        }

        catch (Throwable e) {

            return null;

        }

    }

 

    private void loadAndBindSpringIoc() throws Exception {

        beanFactory = new XmlBeanFactory(new ClassPathResource(springContextXml));

        beanFactory.setSerializationId(Long.toString(-1436030605927869707L));

        bind();

    }

 

    private void unbind() throws NamingException {

        InitialContext context = new InitialContext();

        context.unbind(SPRING_BEAN_FACTORY);

    }

 

    private void bind() throws NamingException {

        LOGGER.debug("Binding spring bean factory in jndi context");

        InitialContext context = new InitialContext();

        context.bind(SPRING_BEAN_FACTORY, this.beanFactory);

    }

 

    @Override

    public String getSpringContextXml() {

        return this.springContextXml;

    }

 

    @Override

    public void setSpringContextXml(String springContextXml) {

        this.springContextXml = springContextXml;

    }

}

{code}

 

The Spring MBean implements the ServiceMBeanSupport class.  This is a convient class as it provides implementations for most methods for us and allows us just to override the pieces we care about.  In this instance, we want to create a new Spring Bean Factory during the service startup and destroy it during the service shutdown.

 

Creating the actuall Bean Factory is just standard Spring Bootsrapping where we create a new XmlBeanFactory.  Note, we are using the XmlBeanFactory rather than a ClassPathXmlApplicationContext because the XmlBeanFactory is serializable whereas the ClassPathXmlApplicationContext is not.  When constructing a new XmlBeanFactory you als need to set the serializationID otherwise when java tries to serialize the Factory to store it in JNDI it will complain.  To do this you can use the serialver tool provided by java. Details can be found here

 

You should also note that we actually register the newly created BeanFactory with JNDI using the bind() method, and de-register the BeanFactory using the unbind() methods.

 

The final important note is the static method getBeanFactory which provides a convienent way to lookup the BeanFactory from JNDI from within your code.

Register ESB Dependencies

 

We need to register the new SpringMBean in a similar way we registered the HibernateMBean.  Open the example-service.xml file we created earlier and add the following MBean definition to the file.

 

{code:xml}

<mbean code="example.esb.mbean.spring.Spring"

           name="example.esb:service=SpringBeanFactory,name=ExampleSpringBeanFactory">

        <attribute name="SpringContextXml">applicationContext.xml</attribute>

        <depends-list>

            <depends-list-element>

                example.esb:service=HibernateSessionFactory,name=ExampleHibernateSessionFactory

            </depends-list-element>

        </depends-list>

</mbean>

{code}

 

So this is a standard MBean registration.  The only required attribute is the SpringContextXml which should be the classpath to your spring applicationContext.xml file; in this example, the applicationContext is stored in the root of the classpath.

 

The application context that we are using is a standard spring application context that actually creates a new TransactionManager using the HibernateSessionFactory we registered earier in the guide.  To ensure that this MBean registration does not fail, we must set the <depends-list> on the HibernateMBean we registered earlier.

 

Here is an example of our application context which ties everything together

 

applicationContext.xml

 

{code:xml}

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"

       xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

 

    <jee:jndi-lookup id="hibernateSessionFactory" jndi-name="java:/hibernate/VisionHibernateSessionFactory"/>

 

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <property name="sessionFactory">

            <ref local="hibernateSessionFactory"/>

        </property>

    </bean>

 

     ......

</beans>

{code}

 

There is one final thing required.  We need to ensure that the jboss-esb.xml file is not deployed before all the MBeans and Datasource we have just created.  Your project will probably already contain a META-INF/deployment.xml file where you will probably have dependencies on various queues fory our ESB.  We need to add a new dependecy to deployment.xml for the SpringBeanFactory we just created.

 

META-INF/deployment.xml

 

{code:xml}

<?xml version="1.0" encoding="UTF-8"?>

<jbossesb-deployment>

 

    <depends>example.esb:service=SpringBeanFactory,name=ExampleSpringBeanFactory</depends>

 

    <jmsQueue>answer_erp_queue</jmsQueue>

    <jmsQueue>answer_erp_gw_queue</jmsQueue>

    <jmsQueue>purchase_order_queue</jmsQueue>

    <jmsQueue>purchase_order_gw_queue</jmsQueue>

 

</jbossesb-deployment>

{code}

Using the Spring Bean Factory in your code

 

Finally you can use the newly registered Spring Bean Factory in any of your actions, or infact any code that runs within your ESB module.

 

 

{code:java}

package example.esb.service.order;


...

/**

* User: Grant Currey

* Date: 27/04/2011

* Time: 11:17:04 PM

*/

public class UnProcessedPurchaseOrderListener implements ScheduledEventListener {


    private PurchaseOrderDao purchaseOrderDao;

    private ServiceInvoker purchaseOrderServiceInvoker;

 

    @Override

    public void onSchedule() throws SchedulingException {

        ...

    }

 

    @Override

    public void initialize(ConfigTree configTree) throws ConfigurationException {

        try {

            BeanFactory beanFactory = Spring.getBeanFactory();

 

            this.purchaseOrderDao = (PurchaseOrderDao) beanFactory.getBean("purchaseOrderDao");

            this.purchaseOrderServiceInvoker = new ServiceInvoker("documents", "purchaseOrderService");

        } catch (Exception e) {

            throw new ConfigurationException(e);

        }

    }

 

    @Override

    public void uninitialize() {

    }

}

{code}

 

Hopefully this provides an alternative way to integrate Spring and Hibernate with JBoss ESB and bridge some gaps.