activemq-artemis/examples/javaee/xarecovery
Howard Gao 293b242ffc ACTIVEMQ6-43 Replace License Headers on codebase
upgrade mycila plugin to 2.6
2014-12-01 10:20:24 +08:00
..
server/standalone/configuration ACTIVEMQ6-43 Replace License Headers on codebase 2014-12-01 10:20:24 +08:00
src ACTIVEMQ6-43 Replace License Headers on codebase 2014-12-01 10:20:24 +08:00
pom.xml ACTIVEMQ6-3 renaming package names from activemq6 to activemq 2014-11-17 09:33:53 -05:00
readme.html Remove references to HornetQ in doc and Comments 2014-11-19 15:15:35 +00:00

readme.html

<html>
  <head>
    <title>ActiveMQ XA Recovery Example</title>
    <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>
     
     <p>This example will demonstrate XA recovery in WildFly with a ActiveMQ XA resource and a "buggy" XA resource.</p>

     <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">
             <code>XARecoveryExampleService service = (XARecoveryExampleService) initialContext.lookup("ejb:/test//XARecoveryExampleBean!org.apache.activemq.javaee.example.server.XARecoveryExampleService");</code>
         </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>
         
         <p>When WildFly is restarted, it will automatically trigger a recovery phase. During that phase, ActiveMQ resources will be
         scanned and the <em>prepared</em> transaction will be recovered and committed. It is then possible to consume this message</p>
  </body>
</html>