content: /*
   * 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;
  
  import javax.jms.Connection;
  import javax.jms.ConnectionFactory;
  import javax.jms.Destination;
  import javax.jms.JMSException;
  import javax.jms.MessageConsumer;
  import javax.jms.MessageProducer;
  import javax.jms.Session;
  import javax.jms.TextMessage;
  import java.util.Map;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.atomic.AtomicInteger;
  
  import org.apache.activemq.broker.BrokerService;
  import org.junit.After;
  import org.junit.Before;
  import org.junit.Rule;
  import org.junit.Test;
  import org.junit.rules.TestName;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertTrue;
  
  public class JmsRollbackRedeliveryTest {
  
     @Rule
     public TestName testName = new TestName();
  
     protected static final Logger LOG = LoggerFactory.getLogger(JmsRollbackRedeliveryTest.class);
     final int nbMessages = 10;
     final String destinationName = "Destination";
     final String brokerUrl = "vm://localhost?create=false";
     boolean consumerClose = true;
     boolean rollback = true;
     BrokerService broker;
  
     @Before
     public void setUp() throws Exception {
        LOG.debug("Starting " + testName.getMethodName());
        broker = new BrokerService();
        broker.setPersistent(false);
        broker.setUseJmx(false);
        broker.start();
        broker.waitUntilStarted();
     }
  
     @After
     public void tearDown() throws Exception {
        if (broker != null) {
           broker.stop();
           broker.waitUntilStopped();
        }
        LOG.debug("Finishing " + testName.getMethodName());
        Thread.sleep(100);
     }
  
     @Test
     public void testRedelivery() throws Exception {
        doTestRedelivery(brokerUrl, false);
     }
  
     @Test
     public void testRedeliveryWithInterleavedProducer() throws Exception {
        doTestRedelivery(brokerUrl, true);
     }
  
     @Test
     public void testRedeliveryWithPrefetch0() throws Exception {
        doTestRedelivery(brokerUrl + "?jms.prefetchPolicy.queuePrefetch=0", true);
     }
  
     @Test
     public void testRedeliveryWithPrefetch1() throws Exception {
        doTestRedelivery(brokerUrl + "?jms.prefetchPolicy.queuePrefetch=1", true);
     }
  
     public void doTestRedelivery(String brokerUrl, boolean interleaveProducer) throws Exception {
        LOG.debug("entering doTestRedelivery interleaveProducer is " + interleaveProducer);
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
  
        Connection connection = connectionFactory.createConnection();
        connection.start();
  
        if (interleaveProducer) {
           populateDestinationWithInterleavedProducer(nbMessages, destinationName, connection);
        } else {
           populateDestination(nbMessages, destinationName, connection);
        }
  
        // Consume messages and rollback transactions
        {
           AtomicInteger received = new AtomicInteger();
           Map<String, Boolean> rolledback = new ConcurrentHashMap<>();
           while (received.get() < nbMessages) {
              Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
              Destination destination = session.createQueue(destinationName);
              MessageConsumer consumer = session.createConsumer(destination);
              TextMessage msg = (TextMessage) consumer.receive(5000);
              if (msg != null) {
                 if (rolledback.put(msg.getText(), Boolean.TRUE) != null) {
                    LOG.info("Received message " + msg.getText() + " (" + received.getAndIncrement() + ")" + msg.getJMSMessageID());
                    assertTrue(msg.getJMSRedelivered());
                    assertEquals(2, msg.getLongProperty("JMSXDeliveryCount"));
                    session.commit();
                 } else {
                    LOG.info("Rollback message " + msg.getText() + " id: " + msg.getJMSMessageID());
                    assertFalse("should not have redelivery flag set, id: " + msg.getJMSMessageID(), msg.getJMSRedelivered());
                    session.rollback();
                 }
              }
              consumer.close();
              session.close();
           }
        }
     }
  
     @Test
     public void testRedeliveryOnSingleConsumer() throws Exception {
  
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
        Connection connection = connectionFactory.createConnection();
        connection.start();
  
        populateDestinationWithInterleavedProducer(nbMessages, destinationName, connection);
  
        // Consume messages and rollback transactions
        {
           AtomicInteger received = new AtomicInteger();
           Map<String, Boolean> rolledback = new ConcurrentHashMap<>();
           Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
           Destination destination = session.createQueue(destinationName);
           MessageConsumer consumer = session.createConsumer(destination);
           while (received.get() < nbMessages) {
              TextMessage msg = (TextMessage) consumer.receive(6000000);
              if (msg != null) {
                 if (rolledback.put(msg.getText(), Boolean.TRUE) != null) {
                    LOG.info("Received message " + msg.getText() + " (" + received.getAndIncrement() + ")" + msg.getJMSMessageID());
                    assertTrue(msg.getJMSRedelivered());
                    session.commit();
                 } else {
                    LOG.info("Rollback message " + msg.getText() + " id: " + msg.getJMSMessageID());
                    session.rollback();
                 }
              }
           }
           consumer.close();
           session.close();
        }
     }
  
     @Test
     public void testRedeliveryOnSingleSession() throws Exception {
  
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
        Connection connection = connectionFactory.createConnection();
        connection.start();
  
        populateDestination(nbMessages, destinationName, connection);
  
        // Consume messages and rollback transactions
        {
           AtomicInteger received = new AtomicInteger();
           Map<String, Boolean> rolledback = new ConcurrentHashMap<>();
           Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
           Destination destination = session.createQueue(destinationName);
           while (received.get() < nbMessages) {
              MessageConsumer consumer = session.createConsumer(destination);
              TextMessage msg = (TextMessage) consumer.receive(6000000);
              if (msg != null) {
                 if (rolledback.put(msg.getText(), Boolean.TRUE) != null) {
                    LOG.info("Received message " + msg.getText() + " (" + received.getAndIncrement() + ")" + msg.getJMSMessageID());
                    assertTrue(msg.getJMSRedelivered());
                    session.commit();
                 } else {
                    LOG.info("Rollback message " + msg.getText() + " id: " + msg.getJMSMessageID());
                    session.rollback();
                 }
              }
              consumer.close();
           }
           session.close();
        }
     }
  
     // AMQ-1593
     @Test
     public void testValidateRedeliveryCountOnRollback() throws Exception {
  
        final int numMessages = 1;
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
        Connection connection = connectionFactory.createConnection();
        connection.start();
  
        populateDestination(numMessages, destinationName, connection);
  
        {
           AtomicInteger received = new AtomicInteger();
           final int maxRetries = new RedeliveryPolicy().getMaximumRedeliveries();
           while (received.get() < maxRetries) {
              Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
              Destination destination = session.createQueue(destinationName);
  
              MessageConsumer consumer = session.createConsumer(destination);
              TextMessage msg = (TextMessage) consumer.receive(1000);
              if (msg != null) {
                 LOG.info("Received message " + msg.getText() + " (" + received.getAndIncrement() + ")" + msg.getJMSMessageID());
                 assertEquals("redelivery property matches deliveries", received.get(), msg.getLongProperty("JMSXDeliveryCount"));
                 session.rollback();
              }
              session.close();
           }
           consumeMessage(connection, maxRetries + 1);
        }
     }
  
     // AMQ-1593
     @Test
     public void testValidateRedeliveryCountOnRollbackWithPrefetch0() throws Exception {
  
        final int numMessages = 1;
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl + "?jms.prefetchPolicy.queuePrefetch=0");
        Connection connection = connectionFactory.createConnection();
        connection.start();
  
        populateDestination(numMessages, destinationName, connection);
  
        {
           AtomicInteger received = new AtomicInteger();
           final int maxRetries = new RedeliveryPolicy().getMaximumRedeliveries();
           while (received.get() < maxRetries) {
              Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
              Destination destination = session.createQueue(destinationName);
  
              MessageConsumer consumer = session.createConsumer(destination);
              TextMessage msg = (TextMessage) consumer.receive(1000);
              if (msg != null) {
                 LOG.info("Received message " + msg.getText() + " (" + received.getAndIncrement() + ")" + msg.getJMSMessageID());
                 assertEquals("redelivery property matches deliveries", received.get(), msg.getLongProperty("JMSXDeliveryCount"));
                 session.rollback();
              }
              session.close();
           }
  
           consumeMessage(connection, maxRetries + 1);
        }
     }
  
     private void consumeMessage(Connection connection, final int deliveryCount) throws JMSException {
        Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
        Destination destination = session.createQueue(destinationName);
        MessageConsumer consumer = session.createConsumer(destination);
        TextMessage msg = (TextMessage) consumer.receive(1000);
        assertNotNull(msg);
        assertEquals("redelivery property matches deliveries", deliveryCount, msg.getLongProperty("JMSXDeliveryCount"));
        session.commit();
        session.close();
     }
  
     @Test
     public void testRedeliveryPropertyWithNoRollback() throws Exception {
        final int numMessages = 1;
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
        Connection connection = connectionFactory.createConnection();
        connection.start();
  
        populateDestination(numMessages, destinationName, connection);
        connection.close();
  
        {
           AtomicInteger received = new AtomicInteger();
           final int maxRetries = new RedeliveryPolicy().getMaximumRedeliveries();
           while (received.get() < maxRetries) {
              connection = connectionFactory.createConnection();
              connection.start();
              Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
              Destination destination = session.createQueue(destinationName);
  
              MessageConsumer consumer = session.createConsumer(destination);
              TextMessage msg = (TextMessage) consumer.receive(2000);
              if (msg != null) {
                 LOG.info("Received message " + msg.getText() + " (" + received.getAndIncrement() + ")" + msg.getJMSMessageID());
                 assertEquals("redelivery property matches deliveries", received.get(), msg.getLongProperty("JMSXDeliveryCount"));
              }
              session.close();
              connection.close();
           }
           connection = connectionFactory.createConnection();
           connection.start();
           consumeMessage(connection, maxRetries + 1);
        }
     }
  
     private void populateDestination(final int nbMessages,
                                      final String destinationName,
                                      Connection connection) throws JMSException {
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Destination destination = session.createQueue(destinationName);
        MessageProducer producer = session.createProducer(destination);
        for (int i = 1; i <= nbMessages; i++) {
           producer.send(session.createTextMessage("<hello id='" + i + "'/>"));
        }
        producer.close();
        session.close();
     }
  
     private void populateDestinationWithInterleavedProducer(final int nbMessages,
                                                             final String destinationName,
                                                             Connection connection) throws JMSException {
        Session session1 = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Destination destination1 = session1.createQueue(destinationName);
        MessageProducer producer1 = session1.createProducer(destination1);
        Session session2 = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Destination destination2 = session2.createQueue(destinationName);
        MessageProducer producer2 = session2.createProducer(destination2);
  
        for (int i = 1; i <= nbMessages; i++) {
           if (i % 2 == 0) {
              producer1.send(session1.createTextMessage("<hello id='" + i + "'/>"));
           } else {
              producer2.send(session2.createTextMessage("<hello id='" + i + "'/>"));
           }
        }
        producer1.close();
        session1.close();
        producer2.close();
        session2.close();
     }
  
  }
  