/* * 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 { } } }