Transactions allow you to roll back data in the event of an error, which can save your business in the case of a system failure. This page provides examples of how you might implement transactions using the following approaches:
Transaction mediator scenario
We can 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 such 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 the first table and no record will be added into the second database.
System Requirements:
- Windows, Linux or Solaris operating systems;
- WO2 ESB;
- JBoss Application server;
- Derby Database;
- Apache ActiveMQ.
Since the transaction mediator is implemented using JTA you need to have a JTA provider. We will be using JBoss J2EE application server (which implements the transaction support through Arjuna TS) as the JTA provider, so we will have to deploy WSO2 ESB in JBoss Application server(AS). Apache Derby as the database server. The References section contain the links to download the required softwares.
Note
JBoss server and the Derby database server support the characteristics mentioned above.
Running the example
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
). The reason is using of the implementation of javax.transaction.UserTransaction
of JTA provider (here JBoss) and if we have both in class path, there is a classloading issue which causes the transaction mediator not to work.
We need to 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
.
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
(we'll use the default JBoss configuration) folder.
The full Synapse configuration is shown below (you can directly paste the following configuration into synapse configuration in the following file:
$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>
You need to have two distributed Derby databases ("esbdb" and "esbdb1") to run the sample 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 table
Database1:
INSERT into company values ('IBM','c1',0.0); INSERT into company values ('SUN','c2',0.0);
Database1:
INSERT into company values ('SUN','c2',0.0); INSERT into company values ('MSFT','c3',0.0);
Note the order of the record. You also need two data source declarations for JBoss AS for the two distributed database.
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>
Just drop the above two datasource declarations into (note that the two datasource file name should be *-xa-ds.xml
) $JBOSS_HOME/server/default/deploy
folder. You also need to map the above jndi
names, so 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>
Now go into $JBOSS_HOME/bin
and start the server. Just run the run.sh (run.bat) script. Note that you need to set the CARBON_HOME
enviornment varible pointing to the Carbon repository location. Now we can try the samples. We need to deploy the SimpleStockQuote
service which comes with WSO2 ESB sampels.
Successful scenario
First we will remove the IBM record from the first database and add it to the second database. Run the sample with following options.
ant stockquote -Daddurl=http://localhost:9000/services/SimpleStockQuoteService -Dtrpurl=http://localhost:8280/ -Dsymbol=IBM
Check both databases to see how the record is deleted from the first database and added into the second database.
Failure scenario
Now we will try adding 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
You will see how the fault sequence is executed and the whole transaction is rollback. Check both database again. You will notice that no record is deleted from the first database and no record is added into the second database.
JMS transport scenario
In addition to the transaction mediator, WSO2 ESB also has support for JMS transaction. WSO2 ESB ships with a JMS transport (which support JMS 1.1 and later) which supports both local and distributed JMS transactions. In a JMS client you can use local transactions to group message sends and receives. The JMS API Session interface provides commit and rollback methods that you can use in a JMS client.
In the following scenario, a message is read from a JMS queue and it is processed by a back end service. While executing one sequence ESB receives a fault and this causes the JMS transaction to rollback. In the successful scenario, the transaction is committed and the request is sent to the backend service. In the following configuration, there is a class mediator which will set a property called "MESSAGE_COUNT" to the message context and it will demonstrate this behavior. Depending on the value of this variable the decision to either commit or rollback the transaction will be taken. You can download the binary ZIP of the mediator from here. Drop this mediator to $ESB_HOME/repository/components/lib
.
The source of the mediator looks as shown below.
public class MessageCounterMediator extends AbstractMediator { private static int MESSAGE_COUNT = 0; public boolean mediate(MessageContext synCtx) { MESSAGE_COUNT++; synCtx.setProperty("MESSAGE_COUNT", MESSAGE_COUNT); return true; } }
ESB configuration is given below.
<proxy name="StockQuoteProxy" transports="jms" startOnLoad="true"> <target> <inSequence> <class name="org.wso2.carbon.mediator.MessageCounterMediator"/> <switch source="get-property('MESSAGE_COUNT')"> <case regex="1"> <property name="SET_ROLLBACK_ONLY" value="true" scope="axis2"/> <log level="custom"> <property name="Transaction Action" value="Rollbacked"/> </log> </case> <default> <log level="custom"> <property name="Transaction Action" value="Committed"/> </log> <send> <endpoint name="endpoint_urn_uuid_677F3EF4BC0AE1AF5B32399295906279-2025938318"> <address uri="http://localhost:9000/services/SimpleStockQuoteService"/> </endpoint> </send> </default> </switch> <property name="OUT_ONLY" value="true"/> </inSequence> </target> <publishWSDL uri="file:repository/samples/resources/proxy/sample_proxy_1.wsdl"/> <parameter name="transport.jms.ContentType"> <rules> <jmsProperty>contentType</jmsProperty> <default>application/xml</default> </rules> </parameter> </proxy>
To start a local JMS transaction, define the following property in JMS transport Listner in axis2.xml.
<parameter name="transport.jms.SessionTransacted">true</parameter>
By default, the session is not transacted and if you want to use JMS local transaction, set the above parameter to true. Also note the following property in the failure case which will roll back the local transaction.
<property name="SET_ROLLBACK_ONLY" value="true" scope="axis2"/>
Running the example
You do not need to deploy the ESB on JBOSS to run this sample since this does not require any distributed transaction manager as in other cases.
1. Copy the JMS client JARs (activemq-core-5.2.0.jar, geronimo-j2ee-management_1.0_spec-1.0.jar, geronimo-jms_1.1_spec-1.1.1.jar
) into $ESB_HOME/repository/components/lib
2. Start the activemq server.
3. Deploy and start the SimpleStockQuoteService
service. You need to get the SimpleStockQuoteService
service attach with this article and (SimpleStockQuoteService.aar
) and place it in $ESB_HOME/samples/axis2Server/repository/services
.
4. Start WSO2 ESB with above configuration
5. Run the JMS client with following command.
ant jmsclient -Djms_type=pox -Djms_dest=dynamicQueues/StockQuoteProxy -Djms_payload=MSFT
When you run the client, you can see two log lines saying that first time the traction was rolled back and in the second attempt the transaction was committed. And service will be served to the message received.
Distributed JMS transaction
WSO2 ESB has the support for distributed JMS transactions. In this case, you can use the transaction mediator to manage multiple distributed resources. An ideal candidate for this category would be handling a JMS queue and a data base server using a single transaction. A sample configuration will be very similar to the distributed transaction example configuration given in the section on distributed transactions.