/*
* 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.integration.client;
import java.util.HashSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.artemis.api.core.client.ClientConsumer;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientProducer;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.MessageHandler;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.apache.activemq.artemis.jms.client.ActiveMQTextMessage;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.junit.Before;
import org.junit.Test;
public class MessageConsumerRollbackTest extends ActiveMQTestBase {
ActiveMQServer server;
ServerLocator locator;
ClientSessionFactory sf;
private static final String inQueue = "inqueue";
private static final String outQueue = "outQueue";
@Override
@Before
public void setUp() throws Exception {
super.setUp();
server = createServer(true, true);
AddressSettings settings = new AddressSettings().setRedeliveryDelay(100);
server.getConfiguration().getAddressesSettings().put("#", settings);
server.start();
locator = createNettyNonHALocator();
sf = createSessionFactory(locator);
ClientSession session = sf.createTransactedSession();
session.createQueue(inQueue, inQueue, true);
session.createQueue(outQueue, outQueue, true);
session.close();
}
// Constants -----------------------------------------------------
// Attributes ----------------------------------------------------
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
// Public --------------------------------------------------------
@Test
public void testRollbackMultipleConsumers() throws Exception {
int numberOfMessages = 3000;
int numberOfConsumers = 10;
ClientSession session = sf.createTransactedSession();
sendMessages(numberOfMessages, session);
AtomicInteger count = new AtomicInteger(0);
CountDownLatch commitLatch = new CountDownLatch(numberOfMessages);
LocalConsumer[] consumers = new LocalConsumer[numberOfConsumers];
for (int i = 0; i < numberOfConsumers; i++) {
consumers[i] = new LocalConsumer(count, commitLatch);
consumers[i].start();
}
commitLatch.await(2, TimeUnit.MINUTES);
for (LocalConsumer consumer : consumers) {
consumer.stop();
}
ClientConsumer consumer = session.createConsumer(outQueue);
session.start();
HashSet<Integer> values = new HashSet<>();
for (int i = 0; i < numberOfMessages; i++) {
ClientMessage msg = consumer.receive(1000);
assertNotNull(msg);
int value = msg.getIntProperty("out_msg");
msg.acknowledge();
assertFalse("msg " + value + " received in duplicate", values.contains(value));
values.add(value);
}
assertNull(consumer.receiveImmediate());
for (int i = 0; i < numberOfMessages; i++) {
assertTrue(values.contains(i));
}
assertEquals(numberOfMessages, values.size());
session.close();
}
/**
* @param numberOfMessages
* @param session
* @throws Exception
*/
private void sendMessages(int numberOfMessages, ClientSession session) throws Exception {
ClientProducer producer = session.createProducer(inQueue);
for (int i = 0; i < numberOfMessages; i++) {
ActiveMQTextMessage txt = new ActiveMQTextMessage(session);
txt.setIntProperty("msg", i);
txt.setText("Message Number (" + i + ")");
txt.doBeforeSend();
producer.send(txt.getCoreMessage());
}
session.commit();
}
private class LocalConsumer implements MessageHandler {
// One of the tests will need this
boolean rollbackFirstMessage = true;
ServerLocator consumerLocator;
ClientSessionFactory factoryLocator;
ClientSession session;
ClientConsumer consumer;
ClientProducer producer;
AtomicInteger counter;
CountDownLatch commitLatch;
private LocalConsumer(AtomicInteger counter, CountDownLatch commitLatch) {
this.counter = counter;
this.commitLatch = commitLatch;
}
public void stop() throws Exception {
session.close();
factoryLocator.close();
consumerLocator.close();
}
public void start() throws Exception {
consumerLocator = createNettyNonHALocator();
factoryLocator = createSessionFactory(consumerLocator);
session = factoryLocator.createTransactedSession();
consumer = session.createConsumer(inQueue);
producer = session.createProducer(outQueue);
consumer.setMessageHandler(this);
session.start();
}
@Override
public void onMessage(ClientMessage message) {
try {
message.acknowledge();
ClientMessage outmsg = session.createMessage(true);
outmsg.putIntProperty("out_msg", message.getIntProperty("msg"));
producer.send(outmsg);
if (rollbackFirstMessage) {
session.rollback();
rollbackFirstMessage = false;
return;
}
if (counter.incrementAndGet() % 200 == 0) {
System.out.println("rollback " + message);
session.rollback();
} else {
commitLatch.countDown();
session.commit();
}
} catch (Exception e) {
e.printStackTrace();
try {
session.rollback();
} catch (Exception ignored) {
ignored.printStackTrace();
}
}
}
}
}