activemq-artemis/examples/javaee/xarecovery
Clebert Suconic 3c44e26854 ACTIVEMQ6-3 renaming directories from activemq6 to activemq
https://issues.apache.org/jira/browse/ACTIVEMQ6-3

We are renaming packages from activemq6 to activemq as that's more generic and version independent
On this first commit I'm just renaming the directories otherwise the history would be lost. The next commit will rename the text on the directories.
If I squash these two commits git will make us delete / add again.
2014-11-17 09:33:36 -05:00
..
server/standalone/configuration ACTIVEMQ6-1 - Initial HornetQ Donation Commit 2014-11-10 10:31:25 -06:00
src ACTIVEMQ6-3 renaming directories from activemq6 to activemq 2014-11-17 09:33:36 -05:00
pom.xml ACTIVEMQ6-4 - Rename packages to ActiveMQ 2014-11-11 18:28:18 +00:00
readme.html Use new package names in config and classes 2014-11-11 18:28:18 +00:00

readme.html

<html>
  <head>
    <title>HornetQ 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 HornetQ 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.activemq6.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, HornetQ 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>