/*
* 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.
*/
package org.apache.activemq.artemis.tests.extras.jms.xa;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.XAConnection;
import javax.jms.XAConnectionFactory;
import javax.jms.XASession;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import com.arjuna.ats.arjuna.coordinator.TransactionReaper;
import com.arjuna.ats.arjuna.coordinator.TxControl;
import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple;
import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient;
import org.apache.activemq.artemis.tests.util.JMSTestBase;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* A JMSXDeliveryCountTest
*/
public class JMSXDeliveryCountTest extends JMSTestBase {
Queue queue1;
Topic topic1;
protected XAConnectionFactory xacf;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
xacf = ActiveMQJMSClient.createConnectionFactory("tcp://localhost:61616", "test");
queue1 = createQueue("queue1");
topic1 = createTopic("topic1");
TxControl.enable();
}
@Override
@After
public void tearDown() throws Exception {
TxControl.disable(true);
TransactionReaper.terminate(false);
super.tearDown();
}
@Test
public void testSimpleJMSXDeliveryCount() throws Exception {
Connection conn = null;
try {
conn = cf.createConnection();
Session s = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer p = s.createProducer(queue1);
p.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
p.send(s.createTextMessage("xoxo"));
s.close();
s = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
MessageConsumer c = s.createConsumer(queue1);
conn.start();
TextMessage tm = (TextMessage) c.receive(1000);
Assert.assertEquals("xoxo", tm.getText());
Assert.assertTrue("JMSXDeliveryCount is supposed to exist as a property", tm.propertyExists("JMSXDeliveryCount"));
Assert.assertEquals(1, tm.getIntProperty("JMSXDeliveryCount"));
s.recover();
tm = (TextMessage) c.receive(1000);
Assert.assertEquals("xoxo", tm.getText());
Assert.assertTrue("JMSXDeliveryCount is supposed to exist as a property", tm.propertyExists("JMSXDeliveryCount"));
Assert.assertEquals(2, tm.getIntProperty("JMSXDeliveryCount"));
tm.acknowledge();
conn.close();
} finally {
if (conn != null) {
conn.close();
}
}
}
@Test
public void testJMSXDeliveryCountNotDeliveredMessagesNotUpdated() throws Exception {
Connection conn = null;
try {
conn = cf.createConnection();
Session s = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
MessageProducer p = s.createProducer(queue1);
p.send(s.createTextMessage("message1"));
p.send(s.createTextMessage("message2"));
p.send(s.createTextMessage("message3"));
p.send(s.createTextMessage("message4"));
p.send(s.createTextMessage("message5"));
MessageConsumer c = s.createConsumer(queue1);
conn.start();
TextMessage tm = (TextMessage) c.receive(1000);
Assert.assertEquals("message1", tm.getText());
Assert.assertFalse(tm.getJMSRedelivered());
Assert.assertEquals(1, tm.getIntProperty("JMSXDeliveryCount"));
s.close();
s = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
c = s.createConsumer(queue1);
tm = (TextMessage) c.receive(1000);
Assert.assertEquals("message1", tm.getText());
Assert.assertTrue(tm.getJMSRedelivered());
Assert.assertEquals(2, tm.getIntProperty("JMSXDeliveryCount"));
tm = (TextMessage) c.receive(1000);
Assert.assertEquals("message2", tm.getText());
Assert.assertFalse(tm.getJMSRedelivered());
Assert.assertEquals(1, tm.getIntProperty("JMSXDeliveryCount"));
tm = (TextMessage) c.receive(1000);
Assert.assertEquals("message3", tm.getText());
Assert.assertFalse(tm.getJMSRedelivered());
Assert.assertEquals(1, tm.getIntProperty("JMSXDeliveryCount"));
tm = (TextMessage) c.receive(1000);
Assert.assertEquals("message4", tm.getText());
Assert.assertFalse(tm.getJMSRedelivered());
Assert.assertEquals(1, tm.getIntProperty("JMSXDeliveryCount"));
tm = (TextMessage) c.receive(1000);
Assert.assertEquals("message5", tm.getText());
Assert.assertFalse(tm.getJMSRedelivered());
Assert.assertEquals(1, tm.getIntProperty("JMSXDeliveryCount"));
tm.acknowledge();
} finally {
if (conn != null) {
conn.close();
}
}
}
@Test
public void testRedeliveryOnQueue() throws Exception {
Connection conn = null;
try {
conn = cf.createConnection();
Session sess1 = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer prod = sess1.createProducer(queue1);
prod.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
final int NUM_MESSAGES = 100;
final int NUM_RECOVERIES = 8;
for (int i = 0; i < NUM_MESSAGES; i++) {
TextMessage tm = sess1.createTextMessage();
tm.setText("testing" + i);
prod.send(tm);
}
Session sess2 = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
MessageConsumer cons = sess2.createConsumer(queue1);
conn.start();
TextMessage tm = null;
for (int j = 0; j < NUM_RECOVERIES; j++) {
for (int i = 0; i < NUM_MESSAGES; i++) {
tm = (TextMessage) cons.receive(3000);
Assert.assertNotNull(tm);
Assert.assertEquals("testing" + i, tm.getText());
Assert.assertTrue("JMSXDeliveryCount is supposed to exist as a property", tm.propertyExists("JMSXDeliveryCount"));
Assert.assertEquals(j + 1, tm.getIntProperty("JMSXDeliveryCount"));
}
if (j != NUM_RECOVERIES - 1) {
sess2.recover();
}
}
tm.acknowledge();
} finally {
if (conn != null) {
conn.close();
}
}
}
@Test
public void testRedeliveryOnTopic() throws Exception {
Connection conn = null;
try {
conn = cf.createConnection();
conn.setClientID("myclientid");
Session sess1 = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Session sess2 = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Session sess3 = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
MessageConsumer cons1 = sess1.createConsumer(topic1);
MessageConsumer cons2 = sess2.createConsumer(topic1);
MessageConsumer cons3 = sess3.createDurableSubscriber(topic1, "subxyz");
conn.start();
final int NUM_MESSAGES = 100;
final int NUM_RECOVERIES = 9;
Receiver r1 = new Receiver("R1", sess1, cons1, NUM_MESSAGES, NUM_RECOVERIES);
Receiver r2 = new Receiver("R2", sess2, cons2, NUM_MESSAGES, NUM_RECOVERIES);
Receiver r3 = new Receiver("R3", sess3, cons3, NUM_MESSAGES, NUM_RECOVERIES);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
Thread t3 = new Thread(r3);
t1.start();
t2.start();
t3.start();
Session sessSend = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer prod = sessSend.createProducer(topic1);
prod.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
for (int i = 0; i < NUM_MESSAGES; i++) {
TextMessage tm1 = sessSend.createTextMessage("testing" + i);
prod.send(tm1);
}
t1.join();
t2.join();
t3.join();
Assert.assertFalse(r1.failed);
Assert.assertFalse(r2.failed);
Assert.assertFalse(r3.failed);
cons3.close();
sess3.unsubscribe("subxyz");
} finally {
if (conn != null) {
conn.close();
}
}
}
@Test
public void testDeliveryCountUpdatedOnCloseTransacted() throws Exception {
Connection conn = null;
try {
conn = cf.createConnection();
Session producerSess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = producerSess.createProducer(queue1);
Session consumerSess = conn.createSession(true, Session.SESSION_TRANSACTED);
MessageConsumer consumer = consumerSess.createConsumer(queue1);
conn.start();
TextMessage tm = producerSess.createTextMessage("message1");
producer.send(tm);
TextMessage rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertTrue("JMSXDeliveryCount is supposed to exist as a property", tm.propertyExists("JMSXDeliveryCount"));
Assert.assertEquals(1, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertFalse(rm.getJMSRedelivered());
consumerSess.rollback();
rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertEquals(2, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertTrue(rm.getJMSRedelivered());
consumerSess.rollback();
rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertEquals(3, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertTrue(rm.getJMSRedelivered());
// Now close the session without committing
consumerSess.close();
consumerSess = conn.createSession(true, Session.SESSION_TRANSACTED);
consumer = consumerSess.createConsumer(queue1);
rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertEquals(4, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertTrue(rm.getJMSRedelivered());
consumerSess.commit();
} finally {
if (conn != null) {
conn.close();
}
}
}
@Test
public void testDeliveryCountUpdatedOnCloseClientAck() throws Exception {
Connection conn = null;
try {
conn = cf.createConnection();
Session producerSess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = producerSess.createProducer(queue1);
Session consumerSess = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
MessageConsumer consumer = consumerSess.createConsumer(queue1);
conn.start();
TextMessage tm = producerSess.createTextMessage("message1");
producer.send(tm);
TextMessage rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertEquals(1, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertFalse(rm.getJMSRedelivered());
consumerSess.recover();
rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertEquals(2, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertTrue(rm.getJMSRedelivered());
consumerSess.recover();
rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertEquals(3, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertTrue(rm.getJMSRedelivered());
// Now close the session without committing
consumerSess.close();
consumerSess = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
consumer = consumerSess.createConsumer(queue1);
rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertEquals(4, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertTrue(rm.getJMSRedelivered());
rm.acknowledge();
} finally {
if (conn != null) {
conn.close();
}
}
}
@Test
public void testDeliveryCountUpdatedOnCloseXA() throws Exception {
XAConnection xaConn = null;
Connection conn = null;
TransactionManager mgr = new TransactionManagerImple();
Transaction toResume = null;
Transaction tx = null;
try {
toResume = mgr.suspend();
conn = cf.createConnection();
// Send a message
Session producerSess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = producerSess.createProducer(queue1);
TextMessage tm = producerSess.createTextMessage("message1");
producer.send(tm);
xaConn = xacf.createXAConnection();
XASession consumerSess = xaConn.createXASession();
MessageConsumer consumer = consumerSess.createConsumer(queue1);
xaConn.start();
DummyXAResource res = new DummyXAResource();
mgr.begin();
tx = mgr.getTransaction();
tx.enlistResource(res);
tx.enlistResource(consumerSess.getXAResource());
TextMessage rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertEquals(1, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertFalse(rm.getJMSRedelivered());
tx.delistResource(res, XAResource.TMSUCCESS);
tx.delistResource(consumerSess.getXAResource(), XAResource.TMSUCCESS);
mgr.rollback();
mgr.begin();
tx = mgr.getTransaction();
tx.enlistResource(res);
tx.enlistResource(consumerSess.getXAResource());
rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertEquals(2, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertTrue(rm.getJMSRedelivered());
tx.delistResource(res, XAResource.TMSUCCESS);
tx.delistResource(consumerSess.getXAResource(), XAResource.TMSUCCESS);
mgr.rollback();
mgr.begin();
tx = mgr.getTransaction();
tx.enlistResource(res);
tx.enlistResource(consumerSess.getXAResource());
rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertEquals(3, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertTrue(rm.getJMSRedelivered());
tx.delistResource(res, XAResource.TMSUCCESS);
tx.delistResource(consumerSess.getXAResource(), XAResource.TMSUCCESS);
mgr.rollback();
// Must close consumer first
consumer.close();
consumerSess.close();
consumerSess = xaConn.createXASession();
consumer = consumerSess.createConsumer(queue1);
mgr.begin();
tx = mgr.getTransaction();
tx.enlistResource(res);
tx.enlistResource(consumerSess.getXAResource());
rm = (TextMessage) consumer.receive(1000);
Assert.assertNotNull(rm);
Assert.assertEquals(tm.getText(), rm.getText());
Assert.assertEquals(4, rm.getIntProperty("JMSXDeliveryCount"));
Assert.assertTrue(rm.getJMSRedelivered());
tx.delistResource(res, XAResource.TMSUCCESS);
tx.delistResource(consumerSess.getXAResource(), XAResource.TMSUCCESS);
} finally {
if (conn != null) {
conn.close();
}
if (tx != null) {
try {
mgr.commit();
} catch (Exception ignore) {
}
}
if (xaConn != null) {
xaConn.close();
}
if (toResume != null) {
try {
mgr.resume(toResume);
} catch (Exception ignore) {
}
}
}
}
class Receiver implements Runnable {
MessageConsumer cons;
int numMessages;
int numRecoveries;
boolean failed;
Session sess;
String name;
Receiver(final String name,
final Session sess,
final MessageConsumer cons,
final int numMessages,
final int numRecoveries) {
this.sess = sess;
this.cons = cons;
this.numMessages = numMessages;
this.numRecoveries = numRecoveries;
this.name = name;
}
@Override
public void run() {
try {
Message lastMessage = null;
for (int j = 0; j < numRecoveries; j++) {
for (int i = 0; i < numMessages; i++) {
TextMessage tm = (TextMessage) cons.receive();
lastMessage = tm;
if (tm == null) {
failed = true;
} else if (!tm.getText().equals("testing" + i)) {
failed = true;
} else if (tm.getIntProperty("JMSXDeliveryCount") != j + 1) {
failed = true;
}
}
if (j != numRecoveries - 1) {
sess.recover();
}
}
lastMessage.acknowledge();
} catch (Exception e) {
failed = true;
}
}
}
// Package protected ----------------------------------------------------------------------------
// Protected ------------------------------------------------------------------------------------
// Private --------------------------------------------------------------------------------------
// Inner classes --------------------------------------------------------------------------------
static class DummyXAResource implements XAResource {
DummyXAResource() {
}
@Override
public void commit(final Xid arg0, final boolean arg1) throws XAException {
}
@Override
public void end(final Xid arg0, final int arg1) throws XAException {
}
@Override
public void forget(final Xid arg0) throws XAException {
}
@Override
public int getTransactionTimeout() throws XAException {
return 0;
}
@Override
public boolean isSameRM(final XAResource arg0) throws XAException {
return false;
}
@Override
public int prepare(final Xid arg0) throws XAException {
return XAResource.XA_OK;
}
@Override
public Xid[] recover(final int arg0) throws XAException {
return null;
}
@Override
public void rollback(final Xid arg0) throws XAException {
}
@Override
public boolean setTransactionTimeout(final int arg0) throws XAException {
return false;
}
@Override
public void start(final Xid arg0, final int arg1) throws XAException {
}
}
}