2014-10-31 06:20:28 -04:00
< html >
< head >
2014-11-19 03:44:57 -05:00
< title > ActiveMQ XA Recovery Example< / title >
2014-10-31 06:20:28 -04:00
< link rel = "stylesheet" type = "text/css" href = "../../common/common.css" / >
< link rel = "stylesheet" type = "text/css" href = "../../common/prettify.css" / >
< script type = "text/javascript" src = "../../common/prettify.js" > < / script >
< / head >
< body onload = "prettyPrint()" >
< h1 > Java EE XA Recovery Example< / h1 >
2014-11-19 03:44:57 -05:00
< p > This example will demonstrate XA recovery in WildFly with a ActiveMQ XA resource and a "buggy" XA resource.< / p >
2014-10-31 06:20:28 -04:00
< p > The example application will invoke an EJB which will send a JMS message in a transaction.
The server will crash while the transaction has not been committed (it is in the prepared state).< br / >
On server restart, the transaction will be recovered and the JMS message will finally be sent.< br / >
The example application will then receive the message.< br / >
< p > The example leverages the JBoss Arquillian framework to run a WildFly instance and deploy the MDB.< / p >
< h3 > XA Recovery configuration< / h3 >
< p > In previous versions of JBoss Application Server (the precursor to WildFly) the XA recovery configuration was manual.
However, in WildFly the XA recovery configuration is completely automated.< / p >
< h2 > Example step-by-step< / h2 >
< p > < i > download WildFly 8.0.0.Final from < a href = "http://wildfly.org/downloads/" > here< / a > and install.< / i > < / p >
< p > < i > set the JBOSS_HOME property to point to the WildFly install directory< / i > < / p >
< p > < i > type < code > mvn verify< / code > from the example directory to run< / i > < / p >
< p > The example code is composed of 3 main classes:< / p >
< dl >
< dt > < code > XARecoveryExampleStepOne< / code > and < code > XARecoveryExampleStepTwo< / code > < / dt >
< dd > the client application to invoke the EJB and receive the message< / dd >
< dt > < code > XARecoveryExampleBean< / code > < / dt >
< dd > a Stateless EJB3 which performs all the XA logic< / dd >
< / dl >
< h3 > Example Application< / h3 >
< p > Let's take a look at XARecoveryExampleStepOne first.< / p >
< ol >
< li > First we need to get an initial context so we can look-up the JMS connection factory and destination objects from JNDI. This initial context will get it's properties from the < a href = "config/jndi.properties" > jndi.properties< / a > < / li >
< / li >
< pre class = "prettyprint" >
< code >
Properties env = new Properties();
env.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
initialContext = new InitialContext(env);
< / code >
< / pre >
< li > We look up the EJB< / li >
< pre class = "prettyprint" >
2014-11-17 09:23:06 -05:00
< code > XARecoveryExampleService service = (XARecoveryExampleService) initialContext.lookup("ejb:/test//XARecoveryExampleBean!org.apache.activemq.javaee.example.server.XARecoveryExampleService");< / code >
2014-10-31 06:20:28 -04:00
< / pre >
< li > We invoke the EJB's < code > send< / code > method. This method will send a JMS text message (with the text passed in parameter)
and crash the server when committing the transaction< / li >
< pre class = "prettyprint" >
< code > String message = "This is a text message sent at " + new Date();
System.out.println("invoking the EJB service with text: " + message);
try
{
service.send(message);
}
catch (Exception e)
{
System.out.println("#########################");
System.out.println("The server crashed: " + e.getMessage());
System.out.println("#########################");
}< / code >
< / pre >
< p > < em > At that time, the server is crashed and is automatically restarted by the test runner (i.e. XARecoveryRunnerTest).< / em > < / p >
< / ol >
< p > Let's take a look at XARecoveryExampleStepTwo now.< / p >
< ol >
< li > We will try to receive a message. Once the server is restarted, the message will be recovered and the consumer will receive it.< / li >
< pre class = "prettyprint" >
< code > boolean received = false;
while (!received)
{
try
{
Thread.sleep(15000);
receiveMessage();
received = true;
}
catch (Exception e)
{
System.out.println(".");
}
}< / code >
< / pre >
< p > The < code > receiveMessage()< / code > method contains code to receive a text message from the
JMS Queue and display it.< / p >
< li > And finally, < b > always< / b > remember to close your resources after use, in a < code > finally< / code > block.< / li >
< pre class = "prettyprint" >
< code > finally
{
if (initialContext != null)
{
initialContext.close();
}
}< / code >
< / pre >
< / ol >
< p > Let's now take a look at the EJB example< / p >
< p > In order to crash the server while a transaction is prepared, we will use a < em > failing< / em > < code > XAResource< / code >
which will crash the server (calling < code > Runtime.halt()< / code > ) in its commit phase.< / p >
< p > We will manage ourselves the transaction and its resources enlistment/delistment to be sure that the failing XAResource
will crash the server < em > after< / em > the JMS XA resources is prepared but < em > before< / em > it is committed.< / p >
< ol >
< li > First, we create a new initial context< / li >
< pre class = "prettyprint" >
< code > ic = new InitialContext();< / code >
< / pre >
< li > We look up the Transaction Manager< / li >
< pre class = "prettyprint" >
< code > TransactionManager tm = (TransactionManager)ic.lookup("java:/TransactionManager");< / code >
< / pre >
< li > We look up the JMS < em > XA< / em > Connection Factory (which is bound to < code > java:/JmsXA< / code > )< / li >
< pre class = "prettyprint" >
< code > XAConnectionFactory cf = (XAConnectionFactory)ic.lookup("java:/JmsXA");< / code >
< / pre >
< li > We look up the JMS Queue< / li >
< pre class = "prettyprint" >
< code > Queue queue = (Queue)ic.lookup("queue/testQueue");< / code >
< / pre >
< li > We create a JMS XA connection, a XA session and a message producer for the queue< / li >
< pre class = "prettyprint" >
< code > xaConnection = xacf.createXAConnection();
XASession session = xaConnection.createXASession();
MessageProducer messageProducer = session.createProducer(queue);< / code >
< / pre >
< li > We create a < code > FailingXAResource< / code > . For this example purpose, this XAResource implementation will
call < code > Runtime.halt()< / code > from its < code > commit()< / code > method< / li >
< pre class = "prettyprint" >
< code > XAResource failingXAResource = new FailingXAResource();< / code >
< / pre >
< li > We begin the transaction and retrieve it from the transaction manager< / li >
< pre class = "prettyprint" >
< code > tm.begin();
Transaction tx = tm.getTransaction();< / code >
< / pre >
< li > We enlist the failing XAResource< / li >
< pre class = "prettyprint" >
< code > tx.enlistResource(failingXAResource);< / code >
< / pre >
< li > We enlist the < em > JMS< / em > XA Resource< / li >
< pre class = "prettyprint" >
< code > tx.enlistResource(session.getXAResource());< / code >
< / pre >
< li > We create a text message with the text passed in parameter of the EJB method and send it< / li >
< pre class = "prettyprint" >
< code > TextMessage message = session.createTextMessage(text);
messageProducer.send(message);
System.out.format("Sent message: %s (%s)\n", message.getText(), message.getJMSMessageID());< / code >
< / pre >
< li > We delist the failing XAResource< / li >
< pre class = "prettyprint" >
< code > tx.delistResource(failingXAResource);< / code >
< / pre >
< li > We delist the < em > JMS< / em > XA Resource< / li >
< pre class = "prettyprint" >
< code > tx.delistResource(session.getXAResource());< / code >
< / pre >
< li > We commit the transaction< / li >
< pre class = "prettyprint" >
< code > System.out.println("committing the tx");
tx.commit();< / code >
< / pre >
< p > When the transaction is committed, it will prepare both XAResources and then commit them.< br / >
< p > The failing resources will crash the server leaving the JMS XA Resource < em > prepared< / em > but not < em > committed< / em > < / p >
2014-11-19 03:44:57 -05:00
< p > When WildFly is restarted, it will automatically trigger a recovery phase. During that phase, ActiveMQ resources will be
2014-10-31 06:20:28 -04:00
scanned and the < em > prepared< / em > transaction will be recovered and committed. It is then possible to consume this message< / p >
< / body >
< / html >