<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<head>
<title>ActiveMQ Artemis JMS Failover With Transaction using Replication 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>JMS Failover With Transaction using Replication Example</h1>
<p>This example demonstrates two servers coupled as a live-backup pair for high availability (HA) using replication, and a client
connection failing over from live to backup when the live server is crashed.</p>
<p>Failover behavior differs whether the JMS session is transacter or not.</p>
<p>When a <em>transacted</em> JMS session is used, once-and-only once delivery is guaranteed.</p>
<ul>
<li>if the failover occurs while there is an in-flight transaction, the transaction will be flagged as <em>rollback only</em>. In that case, the JMS client
will need to retry the transaction work.</li>
<li>if the failover occurs while there is <em>no</em> in-flight transaction, the failover will be transparent to the user.</li>
</ul>
<p>ActiveMQ Artemis also provides an example for <a href="../non-transactional-failover/readme.html">non-transaction failover</a>.</p>
<p>For more information on ActiveMQ Artemis failover and HA, and clustering in general, please see the clustering
section of the user manual.</p>
<h2>Example step-by-step</h2>
<p><i>To run the example, simply type <code>mvn verify -Pexample</code> from this directory</i></p>
<p>In this example, the live server is server 1, and the backup server is server 0</p>
<p>The connection will initially be created to server1, server 1 will crash, and the client will carry on
seamlessly on server 0, the backup server.</p>
<ol>
<li>Get an initial context for looking up JNDI from server #1.</li>
<pre class="prettyprint">
initialContext = getContext(1);
</pre>
<li>Look up the JMS resources from JNDI on server #1.</li>
<pre class="prettyprint">
Queue queue = (Queue)initialContext.lookup("/queue/exampleQueue");
ConnectionFactory connectionFactory = (ConnectionFactory)initialContext.lookup("/ConnectionFactory");
</pre>
<li>Create a JMS Connection</li>
<pre class="prettyprint">
connection = connectionFactory.createConnection();
</pre>
<li>Create a JMS <em>transacted</em> Session</li>
<pre class="prettyprint">
Session session = connection.createSession(true, 0);
</pre>
<li>Start the connection to ensure delivery occurs</li>
<pre class="prettyprint">
connection.start();
</pre>
<li>Create a JMS MessageProducer</li>
<pre class="prettyprint">
MessageProducer producer = session.createProducer(queue);
</pre>
<li>Create a JMS MessageConsumer</li>
<pre class="prettyprint">
MessageConsumer consumer = session.createConsumer(queue);
</pre>
<li>Send half of the messages, kill the live server and send the remaining messages</li>
<pre class="prettyprint">
sendMessages(session, producer, numMessages, true);
</pre>
<p>When server #1 crashes, the client automatically detects the failure and automatically
fails over from server #1 to server #0 (in your real program you wouldn't need to sleep).
</p>
<li>As failover occurred during transaction, the session has been marked for <em>rollback only</em> and commit will fail</li>
<pre class="prettyprint">
try
{
session.commit();
} catch (TransactionRolledBackException e)
{
System.err.println("transaction has been rolled back: " + e.getMessage());
}
</pre>
<li>We resend all the messages</li>
<pre class="prettyprint">
sendMessages(session, producer, numMessages, false);
</pre>
<li>We commit the session successfully: the messages will be all delivered to the activated backup server</li>
<pre class="prettyprint">
session.commit();
</pre>
<li>We are now transparently reconnected to server #0, the backup server.
We consume the messages sent before the crash of the live server, commit the session, and check there are no other message on the queue</li>
<pre class="prettyprint">
for (int i = 0; i < numMessages; i++)
{
TextMessage message0 = (TextMessage)consumer.receive(5000);
System.out.println("Got message: " + message0.getText());
}
session.commit();
System.out.println("Other message on the server? " + consumer.receive(5000));
</pre>
<li>And finally, <strong>always</strong> remember to close your resources after use, in a <code>finally</code> block. Closing a JMS connection will automatically close all of its sessions, consumers, producer and browser objects</li>
<pre class="prettyprint">
finally
{
if (connection != null)
{
connection.close();
}
if (initialContext != null)
{
initialContext.close();
}
}
</pre>
</ol>
</body>
</html>