Combining ESB, JPA, Hibernate, JTA and Spring

I am currently involved in a project to replace many of the systems in financial institution with a JBoss ESB based architecture. This is my first experience with JBoss ESB so I have learned alot. My most recent task was to create a proof of concept (POC) that demonstrates the integration of hibernate and spring within the ESB, specifically looking at the transactional implications of this combination.

 

This article summarizes my findings, the resulting (working application) is also attached if you are interested.

 

Motivations

Our standard technology stack is simply JBoss ESB, JBoss AS, Hibernate and Postgres. I motivated for the introduction of spring into the mix, I also motivated for the use of maven as the build technology for our project. You can read that discussion here: http://community.jboss.org/wiki/JBossESBTrailblazerbuildingwithMaven2

 

There are two primary motivators for me for including spring, namely quality and flexibility. I will elaborate on each, if you don't feel like reading my ranting then skip over this motivations section...

 

Quality

When developing enterprise applications there will always complications when it comes to testing them, We tend to develop code which is reliant on the the datasources, the app server and various other infrastructure elements such as jndi. All this combines to create an environment where developing unit tests becomes extremely difficult. This has a profound effect on our product quality. If it is extremely difficult to write and maintain these tests, they don't get written, this means that the code is only really excersized for the first time during system and integration test.

We must accept that every developer doesn't actually write code, we actually write bugs. Furthermore we must also realize that later in the development cycle these bugs are found and fixed, the more costly and time consuming they become. Therefore we would ideally like to have mechanisms in place to catch/filter out bugs as early as possible. In highly mature environments this would be done through various review phases as well as unit testing. We aren't in a position to be able to do effective reviews because we have no data to drive that them, therefore we have to rely on unit testing as our only mechanism to catch bugs early in the development process. Much of the software industry has this reliance, and concepts like Test Drive Development (TDD) are a formalization of this.

 

The main concern here is that if we don't remove linkage to the bulky architectural elements within our business logic, it won't get unit tested. Therefore we will have way too many costly bugs escaping into system test, our test cycles will increase and we will never even get close to meeting our deadlines.

 

Flexibility

Now, ESB was selected as an element within our technology stack because messaging based systems are excellent at removing linkages between system elements. This allows us to deploy real SOA applications that will scale well and grow easily with changing business requirements. We have to accept certain levels on overhead within the ESB architecture and in some cases we won't be able to use deploy an ESB because of performance constraints or other yet un-identified constraints. We still want to have high re-use levels, high cohesion and low linkage within our code base and deployed systems.

 

If we write our business logic directly into ESB actions and services, that logic can only be exposed by a deployed ESB. If we write our business logic into EJBs, that logic can only be exposed within the context of a running and appropriately configured application server which can manage those resources.

 

I believe it would be a fatal mistake to sell our souls to any of these technologies at this stage because of our changing business requirements. We will have special cases where we can't use an ESB, or we would like to use the same business logic in a standard app server and an ESB. This will be impossible to achieve without choosing architectural stereotypes and choosing design approaches that enable the complete independence of the business logic from the underlying architecture within a given deployment.

 

Spring?

Given these motivations, I really like spring. I have used it in the past and the largest impact it has is to really simplify my testing, using it I can easily aim for 70% code coverage in unit tests. I am therefore mostly concerned with the IOC container, spring is a huge framework, I am interested at the moment with its IOC container. For a detailed description of dependency injection and IOC read these two excellent articles from Martin Fowler:

 

In terms of quality, we need to promote testability of our code, specifically the unit testability. IOC implemented the spring way means that our business logic is implemented in Plain Old Java Objects (POJOs), but more importantly these POJOs contains only business or domain logic, no architectural or dependency logic. This makes it possible to test such a class in complete isolation, no overhead. In order to clarify I will use an example:

 

A deposit service is needed, in order for it to do its job it must have access to a data store and some kind of notification service. Traditionally the implementation of this class would have a constructor containing lines like:

 

    private NotificationService ns;
    private EntityManagerFactory emf;

    public Deposit(){
        ns = new NotificationService();//Could also be some kind of lookup...
        emf = new EntityManagerFactory();//Could also be some kind of lookup...
    }

 

These simple, innocent looking lines of code create an explicit dependency on the ORM and notification service. Now in a typical enterprise app these would probably be session beans or similar. This means that if I want to test this class I have to do so inside a running app server with appropriately configured data sources and a database server.

 

There may be many testing frameworks that make this slightly easier, however nothing beets good old JUnit.

 

In contrast a spring POJO might look more like:

public class DepositServiceImpl implements DepositService {

     private NotificationService notif;
        private EntityManagerFactory emf;

     public NotificationService getNotif() {
          return notif;
     }

     public void setNotif(NotificationService notif) {
          this.notif = notif;
     }

        public EntityManagerFactory getEmf() {
          return emf;
     }

     public void setNotif(EntityManagerFactory emf) {
          this.emf = emf;
     }

     @Override
     public void performDeposit(int accountNo, int amount) {
          //perform some business logic
                //persist the data
                //notify the client
     }

}

What is important to note here is that this class does not contain any logic relating to actually getting hold of these dependencies. The control is no longer with the class, it has been handed over to spring. At runtime it is extremely simple to "wire" these dependencies into this POJO:

        <jee:jndi-lookup id="myEmf" jndi-name="jpa"/>

     <!-- Generic validator for Account objects, to be used for example by the Spring web tier -->
     <bean id="deposit" class="DepositServiceImpl">
          <property name="emf" ref="myEmf"/>     
     </bean>

This also makes it extremely easy for me to wire in "moc" versions of any dependency at test time, therefore making it possible for me to test this class in complete isolation. No fancy testing framework required, just plain old JUnit!

In terms of flexibility, the POJO is the best solution to the problem. Session beans only make sense within the context of a container, ESB actions only make sense within the context of an ESB. POJOs make sense within any java based architecture or framework, they are the lowest common denominator.

To illustrate the point, within the ESB environment. I implement some business logic for a deposit in a deposit POJO, I can then expose this POJO as a service on the ESB extremely easily:

        public Message processDepositAction(Message message) throws Exception {
          Integer accNo = (Integer)message.getBody().get("accountNo");
          Integer amount = (Integer)message.getBody().get("amount");

          DepositService ds = (DepositService) getBeanFactory().getBean("depositService");
          ds.performDeposit(accNo, amount);

          return message;
     } 

The same POJO can be easily exposed as a web service, or an RMI call, or whatever specific technology you want to use!

 

Levels of Abstraction

It is now important to note that systems always consists of different levels/layers. When we are designing we need to contextualize the design within a given level of abstraction. The design representation is not really important.

I am not suggesting that we don't use ESB, I am not suggesting that we use EJBs in a tradditional app, I am not suggesting any particular approach at all. I am saying that spring gives us the flexibility to design elements that are re-usable in different architectures and are testable, the decision of whether something is an ESB exposed service or something else needs to be taken on a case by case basis based on certain guidelines and lots of experience.

Transaction and Persistence Considerations

Importantly we need object persistence and transaction management in just about any system we deploy. In java terms that means we need some sort of implementation of the Java Persistance API (JPA) and Java Transaction API.

In our technology stack, hibernate provides the JPA+ implementation and Jboss provides a transaction manager within the context of the app server.

An EJB as a container managed resource has various mechanisms in place to play nicely with the transaction manager. Can a POJO achieve the same effectively?

The simple answer is yes, spring was never designed as a silver bullet. Spring is a way of doing things properly within various different environments. Instead of re-inventing the wheel, the spring guys made sure that their framework plays nicely with many existing ones. This means that when we inject the EntityManager into a given POJO, it is the container manager resource and is already managed by the transaction manager, therefore when a transaction rolls back, so does the commit that our POJO has performed using that EntityManager.

In most of our use cases the JPA integration will sufice, in special cases where we want a POJO to be a declarative resource, spring also provides various other Aspect Orientated Programming (AOP) approaches to be able to make our POJOs declaratively transactional without having to add any JTA code into our POJO.

 

The Solution

Ok, the real meat of this article is actually, how do we combine these technologies effectively to be able to include transactional persistence in the actions of our ESB service pipeline.

 

Firstly let me give you a high level overview of the application. The little application has been name aut for Application Under Test, just to be original. The application is invoked through servlet which places messages onto the bus to be consumed by a service. Two parameters can be passed to the servlet:

  1. Action: one of three values, "read", "write", "error"
  2. Message: an string of length n which will be persisted to a database.

The application will be used by us at a later stage to create performance testing scripts, thus we needed this basic functionality in place to check the performance of complete technology stack.

When the "read" action is requested, the service on the bus will simply query the database. When the "write" action is requested, the passed message will be persisted to the database. When the "error"  action is requested the service will persist the message and then throw an unchecked exception, this will then roll off the stack causing the ESB to roll the transaction back. The application therefore proves the transaction management is working, furthermore the actual persistence is performed within a spring wired POJO and not directly in the ESB action.

 

The ESB Service

This article assumes that you have a correctly configured ESB server to deploy to and that you understand at least the basics of JBoss ESB. Our deployment doesn't make use of the internal JMS provider, we opted instead for an external instance of OpenMQ, therefore the queues are configured manually, and the code assumes that they are in place.

 

Those assumptions in place, lets look at the actual service, the service consumes from a JMS queue and therefore the following providers are configured:

                         <jms-bus busid="autChannel">
                    <jms-message-filter dest-name="esbAutQueue"
                         dest-type="QUEUE"  />
               </jms-bus>

A single service is then defined:

 

               <service category="AutActions" description="Entrypoint with rollback"
               invmScope="NONE" invmTransacted="true" name="EntryPointAction">
               <listeners>
                    <jms-listener busidref="autChannel"
                         is-gateway="false" name="JMS-Gateway" />
               </listeners>
               <actions>
                    <action class="za.co.xx.aut.actions.entrypoint.AutAction" name="AutAction" process="process">
                         <property name="springContextXml" value="applicationContext.xml"/>
                    </action>
               </actions>
          </service>

 

You will notice an un-usual property added to the action, namely the "springContextXml". The lovely people at JBoss provided us with a nice little utility class which enable the spring integration called AbstractSpringAction. This extra property allows it to properly configure the bean factory and make it available to sub classes seamlessly.

 

The actual ESB action therefore simply extends from AbstractSpringAction and invokes all the services on the bean with provides various services:

public class AutAction extends AbstractSpringAction {

     @SuppressWarnings("deprecation")
     public AutAction(final ConfigTree config) throws ConfigurationException {
          configTree = config; 
     }

     public Message process(final Message message)
               throws ActionProcessingException, BeansException, ActionLifecycleException {
          
          AutService service =  (AutService)getBeanFactory().getBean("autService"); 
          if(service == null){
               System.out.println("Service is NULL!");
               throw new IllegalStateException("Spring context invalid!");
          }
          if(message.getProperties().getProperty("action").equals("error")){
               throw new IllegalStateException("Forced Failure");
          }
          if (message.getProperties().getProperty("action").equals("read")) {
               service.read();
          } else {
               service.write((String) message.getBody().get("testMessage"));
          }
          return message;
     }

}

 

Please note that the transaction will only roll back if you throw un-checked exceptions out the action!

The Spring Elements

Ok, firstly we need an interface for this magic AutService:

 

public interface AutService {
     
     public void read();
     
     public void write(String message);

}

No rocket science here...

 

The resulting implementation of this interface looks as follows:

public class AutServiceImpl implements AutService{

     @PersistenceContext
     private EntityManager em;

     public EntityManager getEm() {
          return em;
     }

     public void setEm(EntityManager em) {
          this.em = em;
     }

     public void read() {
          Query query = em.createQuery("select object(o) from SampleEntity as o");
          query.getResultList();
     }

     public void write(String message) {
          SampleEntity entity = new SampleEntity();
          entity.setMessage(message);
          em.persist(entity);
     }

}

 

There are a few important things to note from this implementation:

  • The Entity Manager is injected for us by spring, however we are using @PersistenceContext to annotate this field in the class (will clarify this later)
  • There is no transactional code in the class

 

This is made possible through spring injection, the spring context file(remember we specified it in the esb service definition) looks as follows:

<beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:aop="http://www.springframework.org/schema/aop"
          xmlns:tx="http://www.springframework.org/schema/tx"
          xmlns:jee="http://www.springframework.org/schema/jee"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
           http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">

     <jee:jndi-lookup id="myEmf" jndi-name="persistence-units/AutJPA"/>
     
     <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

     <!-- Generic validator for Account objects, to be used for example by the Spring web tier -->
     <bean id="autService" class="za.co.fnb.aut.AutServiceImpl" />

</beans>

We obviously need the various namespace definitions for spring, there important things to note are:

  • The jee jndi lookup which finds the entity manager factory using jndi
  • We make use of the PersistenceAnnotationBeanPostProcessor, this processor inject the entity manager from the factory (which we looked up) into the beans that are annotated with the @PersistenceContext annotation
  • The actual bean definition. The name is used by the action to action to get an instance of the bean which spring has already wired for us.

 

Cool stuff!

 

JPA and JTA Stuff

 

Ok, that seems simple enough, but where does the transactional magic come from?

 

Well, firstly we need to provide a data source definition for the container:

<datasources>

     <xa-datasource>
          <jndi-name>AutTransactedDB</jndi-name>
          <xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
          <xa-datasource-property name="ServerName">127.0.0.1</xa-datasource-property>
          <xa-datasource-property name="PortNumber">xx</xa-datasource-property>
          <xa-datasource-property name="DatabaseName">autdb</xa-datasource-property>
          <xa-datasource-property name="User">username</xa-datasource-property>
          <xa-datasource-property name="Password">password</xa-datasource-property>
          <track-connection-by-tx />
     </xa-datasource>
     
</datasources>

 

This is goes into a file called autdb-ds.xml, which gets deployed into the server deploy folder (e.g. /opt/jboss/server/default/deploy). Note the datasource class..

 

Next we need to configure hibernate:

<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
     <session-factory>
          <property name="show_sql">true</property>
          <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
          <property name="current_session_context_class">thread</property>
          <property name="hibernate.session_factory_name">SessionFactory</property>     
          <property name="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property>
                 <property name="hibernate.transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</property>     
          
          <mapping class="za.co.xx.aut.persistence.model.SampleEntity"/>
     </session-factory>
</hibernate-configuration>

 

This is a standard hibernate config except for the hibernate.transaction.factory_class and hibernate.transaction.manager_lookup_class. This is all put into a file named hibernate.cfg.xml which is included in the ESB archive at deployment time.

 

Finally we need to configure JPA, this is done through creating a file called persistence.xml and placing it in the META-INF folder:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
     version="1.0">
     <persistence-unit name="AutJPA" transaction-type="JTA">
     <jta-data-source>java:/AutTransactedDB</jta-data-source>
          <properties>
               <property name="jboss.entity.manager.factory.jndi.name" value="persistence-units/AutJPA"/>
               <property name="hibernate.ejb.cfgfile" value="/hibernate.cfg.xml" />
          </properties>
     </persistence-unit>
</persistence>

 

It is important to note that the "jboss.entity.manager.factory.jndi.name" name is explicitly bound here, this is because the JBoss app server does not do this by default, I can't talk for other containers. This jndi name is referenced by the jndi lookup in the spring context file.

 

And thats it! We have configured XA resources where we need to and injected them into the spring bean which does all the work. The rest happens in the background JTA manager within JBoss!

 

Pretty cool stuff...

 

Notes

  • I have mashed the package name for obvious reasons, I didn't re-compile after doing so but it will be quite simple to get the code to the compilation point.
  • Software versions:
    • Jboss AS 5.1.0.GA
    • JBoss ESB 4.7
    • Open MQ 4.4
    • Spring 2.5.6
    • Postgres 8.4 - Note that you will need to change the max_prepared_transactions to some reasonable value (e.g. 100) otherwise XA won't work
  • Once building (note that this is done with maven), three artifacts will be deployed to the server:
    • aut.esb
    • aut.war
    • autdb-ds.xml
  • To invoke the service simple open your web client (hopefully chrome) and type the following in the address bar:
  • You will notice many java properties referenced in the various config files, simply add these to your run.conf files before running the app server:
    • E.g: set "JAVA_OPTS=%JAVA_OPTS% -Dxx.jboss.db.dsclass=org.postgresql.xa.PGXADataSource"

Then just watch the logs and enjoy!