This documentation is for WSO2 ESB version 4.0.2. View documentation for the latest release.

Transaction Mediator Example

A new synapse mediator (Transaction Mediator) has been added to support the distributed transactions using Java transaction API(JTA). JTA allows applications to perform a distributed transaction 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). This mediator can be used to perform a distributed transaction. The synapse configuration has been extended to add explicit transaction markers. This means that you can use the synapse configuration language to define the start, end etc. of your transaction. It is the responsibility of the user to define when to start, commit or rollback the transaction. For example, you can mark the start of a transaction at the start of a database commit and end of the transaction at the end of the database commit and you can mark rollback transaction if a failure occurs.



Transaction Mediator Configuration

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

The action attribute has the following meanings:

Value

Meaning

new

Creates a new JTA transaction. Generates a fault if a transaction already exist.

use-existing-or-new

Creates a new JTA transaction. Does nothing if a transaction exist.

fault-if-no-tx

Generates a fault if no transaction exist. Does nothing if a transaction exist.

commit

Commits transaction. Generates a fault if no transaction exist.

rollback

Rollbacks transaction. Generates a fault if no transaction exist.

suspend

Suspends transaction. Generates a fault if no transaction exist.

resume

Resumes transaction. Generates a fault if no transaction exist.

Note

To use the Transaction Mediator, you need to have a JTA provider in your environment, for example, JBoss.


Transaction Mediator Scenario

Use the following scenario to show how Transaction Mediator works. Assume we have a record in one database and we want to delete that record from the first database and add it to the second database (these two databases can be run on the same server or they can be in two remote servers). The database tables are defined in such a way that the same entry cannot be added twice. So in the successful case, the record will be deleted from the first table (of the first database) and will be added to the second table (of the second database) and in the failure case (in which case the record, which is going to add is already in the second database) no record will be deleted from first table and no record will be added into the second database.


System Requirements

Note

This scenario applies to version 3.0. In versions after, there is a Transaction Manager in Carbon itself, so you don't have to put ESB in an application server to get it working.

Since the Transaction Mediator is implemented using JTA, you need to have a JTA provider. Here is used JBoss J2EE application server (which implements the transaction support through Arjuna TS) as the JTA provider, so it is necessary to deploy WSO2 ESB in JBoss Application server(AS). Apache Derby as the database server.

Tip

See how you can deploy ESB on JBoss. An example is available in the "Deploying WSO2 ESB on JBoss-5.1.0.GA" article which demonstrates the distributed transaction support.

JBoss server and the Derby database server support the characteristics mentioned above.


Running the Example

1. Unzip WSO2 ESB distribution to a place of your choice. And then remove the geronimo-jta_1.1_spec-1.1.0.wso2v1.jar ( This JAR file can be found in $ESB_HOME/repository/components/plugins).

Tip

The reason is that the implementation of javax.transaction.UserTransaction of JTA provider (here JBoss) is used there and if they both are in class path, there is a classloading issue which causes the transaction mediator not to work.

2. Deploy WSO2 ESB on JBoss AS. The JBOSS installation path will be referred to as $JBOSS_HOME and the WSO2 ESB repo location as $CARBON_HOME.

3. Drop the derby client JARs (derby.jar, derbynet.jar and derbyclient.jar) into $CARBON_HOME/repository/components/lib folder and also into $JBOSS_HOME/server/default/lib (here is used the default JBoss configuration) folder.

4. Here is used a similar sample like #361, and the full Synapse configuration is shown below (you can directly paste the following configuration into synapse configuration in $ESB_HOME/repository/conf/synapse-config/synapse.xml). In the "In" sequence we will send a message to the service and in the "Out" sequence we will delete an entry from the first database and will update the second database with that entry. If we try to add an entry, which is already there in the second database, the whole transaction will be roll-backed.

<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>delete from company where name =?</sql>
                     <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>INSERT into company values (?,'c4',?)</sql>
                    <parameter expression="//m0:return/m1:symbol/child::text()"
         xmlns:m1="http://services.samples/xsd" xmlns:m0="http://services.samples"
                               type="VARCHAR"/>
                    <parameter expression="//m0:return/m1:last/child::text()"
         xmlns:m1="http://services.samples/xsd" xmlns:m0="http://services.samples"
                               type="DOUBLE"/>
                </statement>
            </dbreport>
            <transaction action="commit"/>
            <send/>
        </out>
    </sequence>
</definitions>

5. To run the sample, you need two distributed Derby databases ("esbdb" and "esbdb1") . Refer here on how to set up the databases. The database table was created using the following SQL query.

Note

The table schema in which we cannot have the same entry twice.

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

Add few records to the two database tables:

Database1:

INSERT into company values ('IBM','c1',0.0);
INSERT into company values ('SUN','c2',0.0);

Database2:

INSERT into company values ('SUN','c2',0.0);
INSERT into company values ('MSFT','c3',0.0);

Note

The order of the record matters.

6. Create two data source declarations for JBoss AS for the two distributed databases.

Datasource1:esb-derby-xa-ds.xml

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
    <xa-datasource>
        <jndi-name>jdbc/XADerbyDS</jndi-name>
        <isSameRM-override-value>false</isSameRM-override-value>
        <xa-datasource-class>org.apache.derby.jdbc.ClientXADataSource</xa-datasource-class>
        <xa-datasource-property name="portNumber">1527</xa-datasource-property>
        <xa-datasource-property name="DatabaseName">esbdb</xa-datasource-property>
        <xa-datasource-property name="User">esb</xa-datasource-property>
        <xa-datasource-property name="Password">esb</xa-datasource-property>
        <metadata>
            <type-mapping>Derby</type-mapping>
        </metadata>
    </xa-datasource>
</datasources>

Datasource2:esb-derby1-xa-ds.xml

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
    <xa-datasource>
        <jndi-name>jdbc/XADerbyDS1</jndi-name>
        <isSameRM-override-value>false</isSameRM-override-value>
        <xa-datasource-class>org.apache.derby.jdbc.ClientXADataSource</xa-datasource-class>
        <xa-datasource-property name="portNumber">1527</xa-datasource-property>
        <xa-datasource-property name="DatabaseName">esbdb1</xa-datasource-property>
        <xa-datasource-property name="User">esb</xa-datasource-property>
        <xa-datasource-property name="Password">esb</xa-datasource-property>
        <metadata>
            <type-mapping>Derby</type-mapping>
        </metadata>
    </xa-datasource>
</datasources>

Note

The two datasource file names should be *-xa-ds.xml.

Drop the above two datasource declarations into $JBOSS_HOME/server/default/deploy folder. Map the above jndi names: drop the following jboss-web.xml configuration into $JBOSS_HOME/serer/default/deploy/esb.war/WEB-INF/.

<!DOCTYPE jboss-web PUBLIC
         "-//JBoss//DTD Web Application 5.0//EN"
         "http://www.jboss.org/j2ee/dtd/jboss-web_5_0.dtd">
 <jboss-web>
     <resource-ref>
         <res-ref-name>jdbc/XADerbyDS</res-ref-name>
         <jndi-name>java:/XADerbyDS</jndi-name>
     </resource-ref>
     <resource-ref>
         <res-ref-name>jdbc/XADerbyDS1</res-ref-name>
         <jndi-name>java:/XADerbyDS1</jndi-name>
     </resource-ref>
 </jboss-web>

7. Go into $JBOSS_HOME/bin and start the server. Just run the run.sh (run.bat) script.

Note

You need to set the CARBON_HOME environment variable pointing to the Carbon repository location.

8. Try the samples. See sample set up guide to know how you can set up the server. Deploy the SimpleStockQuote service which comes with WSO2 ESB sampels.

Successful Scenario

1. To remove the IBM record from the first database and add it to the second database, run the sample with the following options.

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

2. Check both databases to see how the record is deleted from the first database and added into the second database.

Failure Scenario

1. Try to add an entry which is already there in the second database. This time use Symbol SUN.

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

2. You will see how the fault sequence is executed and the whole transaction is rollback. Check both database again: there is no record deleted from the first database and no record added into the second database.