Friday, August 21, 2009

Transactional ESB - Part 1, The transaction mediator

Update(27/08/09) - The original transaction mediator implementation had some problem with multiple data sources, basically it didn't work with more than one data source. The transaction mediator has been implemented using the TransactionManager interface. I submitted a patch for the transaction mediator to use UserTransaction interface instead. In addition to this there is another patch for DBReport mediator which lets you to use it together with transaction mediator.

ESB 2.1.0 is now out!. In this release there are few new features that you can try out. ESB comes with a number of pre-built samples which can be used to try out ESB easily.
One of the major enhancements is the transaction support built into ESB. Transaction plays a major role in today's business oriented world and when comes to SOA, transaction adds an important value to your platform. Having good transaction support in your business will definitely save you though you only see the value of a transactional system only at a failure. See Paul's blog to see a description of this new feature.

The transaction support in ESB is twofold.

1. The transaction mediator.
2. Transaction support in JMS transport.

This blog will be about transaction mediator. A separate blog will follow about using JMS transport transaction.

Transaction Mediator
A new synapse mediator has been added which adds the distributed transaction support using Java Transaction API(JTA) (The java transaction API allows application to perform distributed transactions that is, transactions that access and update data on two or more networked computer resources, an example would be to have two databases or a database and a message queue such as JMS). So if you perform distributed transactions this mediator is for you. The synapse configuration language has been extended to add explicit transaction markers. It's the responsibility of the user to define when to start, commit and rollback the transaction. For ex: we can mark start of a transaction at the start of a database commit and end of the transaction at the end of database commit and we can rollback the transaction in fault sequence(if an error occurs).

Transaction mediator has the following configuration.

<transaction action="new|use-existing-or-new|fault-if-no-tx|commit|rollback|suspend|resume">
</transaction>


The action attribute has the following meanings.
1. new - Create a new jta transaction. Generate a fault if a transaction already exist.
2. use-existing-or-new - Create a new jta transaction. Do nothing if a transaction exist.
3. fault-if-no-tx - Generate a fault if no transaction exist. Do nothing if a transaction exist.
4. commit - Commit transaction. Generate a fault if no transaction exist.
5. rollback - Rollback transaction. Generate a fault if no transaction exist.
6. suspend - Suspend transaction. Generate a fault if no transaction exist.
7. resume - Resume transaction. Generate a fault if no transaction exist.

Each of this can be use to mark the required action of the transaction process. Following is a step by step guide which describe the transaction mediator in action. It demonstrates how we can update a database table which is a part of a distributed transaction.

The transaction manager is the primary component of the distributed transaction support infrastructure, however the JDBC driver (the resource adapter) and the application server should have the following two characteristics.

1. The driver should implement the JDBC 2.0 API(including the optional package interfaces XADataSource and XAConnection) or higher and the JTA interface XAResource.

2. The application server should provide a DataSource class that is implemented to interact with the distributed transaction infrastructure and a connection pooling model for performance improvements.

In the following example we use the JBoss JTA provider, so we need to deploy WSO2 ESB in JBoss application server. See this post on deploying WSO2 ESB on JBoss app server. It hasn't tested yet with a standalone JTA provider such as[1], and a separate blog will follow on configuring a standalone JTA provider for transaction mediator.

1. Unzip a WSO2 ESB 2.1 distributionto a place of your choice. And then remove the geronimo-jta_1.1_spec-1.1.0.wso2v1.jar from the distribution. This is because we are using the implementation of the javax.transaction.TransactionManage interface) provided by JTA provider (here JBoss). If we have both in classpath there is a classloader problem which causes the transaction mediator to not to work.

2. Then run the ruby script which creates a war web app of WSO2 ESB which can deploy on Jboss. This will also copy all the required configuration files, libraries and others to run WSO2 ESB on Jboss successfully. See the post on deploying esb on JBOSS.

3. We are going to use a similar configuration like sample 361, so drop the derby client drivers(derby.jar, derbynet.jar, derbyclient.jar and derby-plygin.jar) driver into repository/components/extensions in the CARBON_HOME of step 2 and also into $JBOSS_HOME/server/default/lib. Following sql query was used to create the database.


CREATE table company(name varchar(10) primary key, id varchar(10), price double);


4. The synpase configuration is as follows. In the in-squence we will send a message to a service and in the out-sequence we will delete an entry from 1st database and will update the second database with that entry. If we try to add an entry which is already there in second database we'll rollback the whole transaction( No records will be deleted from 1st database and no recored will be added to second database).

<definitions xmlns="http://ws.apache.org/ns/synapse">

<sequence name="myFaultHandler">
<log level="custom">
<property name="text" value="** Rollback Transaction**"/>
</log>
<transaction action="rollback"/>
<send/>
</sequence>

<sequence name="main" onError="myFaultHandler">
<in>
<send>
<endpoint>
<address uri="http://localhost:9000/services/SimpleStockQuoteService"/>
</endpoint>
</send>
</in>

<out>
<transaction action="new"/>

<log level="custom">
<property name="text" value="** Reporting to the Database esbdb**"/>
</log>
<dbreport useTransaction="true" xmlns="http://ws.apache.org/ns/synapse">
<connection>
<pool>
<dsName>java:jdbc/XADerbyDS</dsName>
<icClass>org.jnp.interfaces.NamingContextFactory</icClass>
<url>localhost:1099</url>
<user>esb</user>
<password>esb</password>
</pool>
</connection>
<statement>
<!--<sql>update company set price=? where name =?</sql>-->
<sql>delete from company where name =?</sql>
<!--<parameter expression="//m0:return/m1:last/child::text()"-->
<!--xmlns:m0="http://services.samples" xmlns:m1="http://services.samples/xsd"-->
<!--type="DOUBLE"/>-->
<parameter expression="//m0:return/m1:symbol/child::text()"
xmlns:m0="http://services.samples" xmlns:m1="http://services.samples/xsd"
type="VARCHAR"/>
</statement>
</dbreport>

<log level="custom">
<property name="text" value="** Reporting to the Database esbdb1**"/>
</log>
<dbreport useTransaction="true" xmlns="http://ws.apache.org/ns/synapse">
<connection>
<pool>
<dsName>java:jdbc/XADerbyDS1</dsName>
<icClass>org.jnp.interfaces.NamingContextFactory</icClass>
<url>localhost:1099</url>
<user>esb</user>
<password>esb</password>
</pool>
</connection>
<statement>
<!--<sql>update company set price=? where name =?</sql>-->
<sql> INSERT into company values ('IBM','c4',12.0)</sql>
<!--<parameter expression="//m0:return/m1:last/child::text()"-->
<!--xmlns:m0="http://services.samples" xmlns:m1="http://services.samples/xsd"-->
<!--type="DOUBLE"/>-->
<!--<parameter expression="//m0:return/m1:symbol/child::text()"-->
<!--xmlns:m0="http://services.samples" xmlns:m1="http://services.samples/xsd"-->
<!--type="VARCHAR"/>-->
</statement>
</dbreport>
<transaction action="commit"/>
<send/>
</out>
</sequence>
</definitions>



5. You'll also need to define two datasources for derby in Jboss. Drop the following datasources deceleration for derby into $JBOSS_HOME/server/default/deploy.


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

<datasources>
<xa-datasource>
<jndi-name>jdbc/XADerbyDS</jndi-name>

<!-- uncomment to enable interleaving <interleaving/> -->

<isSameRM-override-value>false</isSameRM-override-value>
<xa-datasource-class>org.apache.derby.jdbc.ClientXADataSource</xa-datasource-class>

<!-- path to the database. (${jboss.server.data.dir}${/}derby${/}default didn't work on win) -->
<xa-datasource-property name="portNumber">1527</xa-datasource-property>
<xa-datasource-property name="DatabaseName">esbdb</xa-datasource-property>
<!--<xa-datasource-property name="portNumber">1527</xa-datasource-property>-->
<xa-datasource-property name="User">esb</xa-datasource-property>
<xa-datasource-property name="Password">esb</xa-datasource-property>

<!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml (optional) -->
<metadata>
<type-mapping>Derby</type-mapping>
</metadata>
</xa-datasource>
</datasources>




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

<datasources>
<xa-datasource>
<jndi-name>jdbc/XADerbyDS1</jndi-name>

<!-- uncomment to enable interleaving <interleaving/> -->

<isSameRM-override-value>false</isSameRM-override-value>
<xa-datasource-class>org.apache.derby.jdbc.ClientXADataSource</xa-datasource-class>

<!-- path to the database. (${jboss.server.data.dir}${/}derby${/}default didn't work on win) -->
<xa-datasource-property name="portNumber">1527</xa-datasource-property>
<xa-datasource-property name="DatabaseName">esbdb1</xa-datasource-property>
<!--<xa-datasource-property name="portNumber">1527</xa-datasource-property>-->
<xa-datasource-property name="User">esb</xa-datasource-property>
<xa-datasource-property name="Password">esb</xa-datasource-property>

<!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml (optional) -->
<metadata>
<type-mapping>Derby</type-mapping>
</metadata>
</xa-datasource>
</datasources>



6. We are using a variation of sample 361. So you can follow the same steps to and run the sample client.

ant stockquote -Daddurl=http://localhost:9000/services/SimpleStockQuoteService -Dtrpurl=http://localhost:8280/ -Dsymbol=SUN


7. Have a look at the database table, you can see the table entry has been updated successfully.

8. Now try to add an entry say symbol SUN again into the second database. You'll get an exception for duplicate entries and the whole transaction will be rolled back.
Note:
If you come across ClassCast exceptions make sure you don't have any *jta.jar in the app specially check the $CARBON_HOME/repository/components/dropins folder.
[1]-http://www.atomikos.com/

2 comments:

  1. Ah! I bought the exact same shelves from Target and had the EXACT same problem! I actually tore up my receipt from Target, rolled it into a few small balls and shoved it in the holes on the back of the shelf. Worked like a charm. Great minds.... :)

    ReplyDelete
  2. Oh my goodness, I just love those floral bags with the wood handles. Those doors are super too! Very cool! Great blog!That is a beautiful arbor!

    Susan Graham

    ReplyDelete