/* * 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 javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.SimpleString; 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.config.Configuration; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.core.transaction.impl.XidImpl; import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Test; public class MessageGroupingTest extends ActiveMQTestBase { private static final IntegrationTestLogger log = IntegrationTestLogger.LOGGER; private ActiveMQServer server; private ClientSession clientSession; private ClientSessionFactory clientSessionFactory; private final SimpleString qName = new SimpleString("MessageGroupingTestQueue"); private ServerLocator locator; @Test public void testBasicGrouping() throws Exception { doTestBasicGrouping(); } @Test public void testMultipleGrouping() throws Exception { doTestMultipleGrouping(); } @Test public void testMultipleGroupingSingleConsumerWithDirectDelivery() throws Exception { doTestMultipleGroupingSingleConsumer(true); } @Test public void testMultipleGroupingSingleConsumerWithoutDirectDelivery() throws Exception { doTestMultipleGroupingSingleConsumer(false); } @Test public void testMultipleGroupingTXCommit() throws Exception { doTestMultipleGroupingTXCommit(); } @Test public void testMultipleGroupingTXRollback() throws Exception { doTestMultipleGroupingTXRollback(); } @Test public void testMultipleGroupingXACommit() throws Exception { dotestMultipleGroupingXACommit(); } @Test public void testMultipleGroupingXARollback() throws Exception { doTestMultipleGroupingXARollback(); } @Test public void testLoadBalanceGroups() throws Exception { Assume.assumeFalse("only makes sense withOUT auto-group", clientSessionFactory.getServerLocator().isAutoGroup()); ClientProducer clientProducer = clientSession.createProducer(qName); ClientConsumer consumer1 = clientSession.createConsumer(qName); ClientConsumer consumer2 = clientSession.createConsumer(qName); ClientConsumer consumer3 = clientSession.createConsumer(qName); ClientConsumer[] consumers = new ClientConsumer[]{consumer1, consumer2, consumer3}; int[] counts = new int[consumers.length]; clientSession.start(); try { //Add all messages for a particular group before moving onto the next for (int group = 0; group < 10; group++) { for (int messageId = 0; messageId < 3; messageId++) { ClientMessage message = clientSession.createMessage(false); message.putStringProperty("_AMQ_GROUP_ID", "" + group); clientProducer.send(message); } } for (int c = 0; c < consumers.length; c++) { while (true) { ClientMessage msg = consumers[c].receiveImmediate(); if (msg == null) { break; } counts[c]++; } } for (int count : counts) { Assert.assertNotEquals("You shouldn't have all messages bound to a single consumer", 30, count); Assert.assertNotEquals("But you shouldn't have also a single consumer bound to none", 0, count); } } finally { consumer1.close(); consumer2.close(); consumer3.close(); } } private void doTestBasicGrouping() throws Exception { ClientProducer clientProducer = clientSession.createProducer(qName); ClientConsumer consumer = clientSession.createConsumer(qName); ClientConsumer consumer2 = clientSession.createConsumer(qName); clientSession.start(); SimpleString groupId = new SimpleString("grp1"); int numMessages = 100; for (int i = 0; i < numMessages; i++) { ClientMessage message = createTextMessage(clientSession, "m" + i); message.putStringProperty(Message.HDR_GROUP_ID, groupId); clientProducer.send(message); } CountDownLatch latch = new CountDownLatch(numMessages); DummyMessageHandler dummyMessageHandler = new DummyMessageHandler(latch, true); consumer.setMessageHandler(dummyMessageHandler); DummyMessageHandler dummyMessageHandler2 = new DummyMessageHandler(latch, true); consumer2.setMessageHandler(dummyMessageHandler2); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); Assert.assertEquals(100, dummyMessageHandler.list.size()); Assert.assertEquals(0, dummyMessageHandler2.list.size()); consumer.close(); consumer2.close(); } @Test public void testMultipleGroupingConsumeHalf() throws Exception { ClientProducer clientProducer = clientSession.createProducer(qName); ClientConsumer consumer = clientSession.createConsumer(qName); ClientConsumer consumer2 = clientSession.createConsumer(qName); clientSession.start(); //need to wait a bit or consumers might be busy Thread.sleep(200); SimpleString groupId = new SimpleString("grp1"); SimpleString groupId2 = new SimpleString("grp2"); int numMessages = 100; for (int i = 0; i < numMessages; i++) { ClientMessage message = createTextMessage(clientSession, "m" + i); if (i % 2 == 0 || i == 0) { message.putStringProperty(Message.HDR_GROUP_ID, groupId); } else { message.putStringProperty(Message.HDR_GROUP_ID, groupId2); } clientProducer.send(message); } for (int i = 0; i < numMessages / 2; i++) { ClientMessage cm = consumer.receive(500); Assert.assertNotNull(cm); Assert.assertEquals(cm.getBodyBuffer().readString(), "m" + i); i++; cm = consumer2.receive(500); Assert.assertNotNull(cm); Assert.assertEquals(cm.getBodyBuffer().readString(), "m" + i); } MessageGroupingTest.log.info("closing consumer2"); consumer2.close(); consumer.close(); } private void doTestMultipleGroupingSingleConsumer(final boolean directDelivery) throws Exception { ClientProducer clientProducer = clientSession.createProducer(qName); ClientConsumer consumer = clientSession.createConsumer(qName); if (directDelivery) { clientSession.start(); } SimpleString groupId = new SimpleString("grp1"); SimpleString groupId2 = new SimpleString("grp2"); int numMessages = 100; for (int i = 0; i < numMessages; i++) { ClientMessage message = createTextMessage(clientSession, "m" + i); if (i % 2 == 0 || i == 0) { message.putStringProperty(Message.HDR_GROUP_ID, groupId); } else { message.putStringProperty(Message.HDR_GROUP_ID, groupId2); } clientProducer.send(message); } if (!directDelivery) { clientSession.start(); } CountDownLatch latch = new CountDownLatch(numMessages); DummyMessageHandler dummyMessageHandler = new DummyMessageHandler(latch, true); consumer.setMessageHandler(dummyMessageHandler); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); Assert.assertEquals(dummyMessageHandler.list.size(), 100); int i = 0; for (ClientMessage message : dummyMessageHandler.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 1; } consumer.close(); } private void doTestMultipleGroupingTXCommit() throws Exception { ServerLocator locator = createInVMNonHALocator(); ClientSessionFactory sessionFactory = createSessionFactory(locator); ClientSession clientSession = sessionFactory.createSession(false, false, false); ClientProducer clientProducer = this.clientSession.createProducer(qName); clientSession.start(); ClientConsumer consumer = clientSession.createConsumer(qName); ClientConsumer consumer2 = clientSession.createConsumer(qName); //Wait a bit otherwise consumers might be busy Thread.sleep(200); SimpleString groupId = new SimpleString("grp1"); SimpleString groupId2 = new SimpleString("grp2"); int numMessages = 100; for (int i = 0; i < numMessages; i++) { ClientMessage message = createTextMessage(clientSession, "m" + i); if (i % 2 == 0 || i == 0) { message.putStringProperty(Message.HDR_GROUP_ID, groupId); } else { message.putStringProperty(Message.HDR_GROUP_ID, groupId2); } clientProducer.send(message); } CountDownLatch latch = new CountDownLatch(numMessages); DummyMessageHandler dummyMessageHandler = new DummyMessageHandler(latch, true); consumer.setMessageHandler(dummyMessageHandler); DummyMessageHandler dummyMessageHandler2 = new DummyMessageHandler(latch, true); consumer2.setMessageHandler(dummyMessageHandler2); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); clientSession.commit(); Assert.assertEquals(dummyMessageHandler.list.size(), 50); int i = 0; for (ClientMessage message : dummyMessageHandler.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } Assert.assertEquals(dummyMessageHandler2.list.size(), 50); i = 1; for (ClientMessage message : dummyMessageHandler2.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } consumer.close(); consumer2.close(); consumer = this.clientSession.createConsumer(qName); Assert.assertNull(consumer.receiveImmediate()); clientSession.close(); locator.close(); } private void doTestMultipleGroupingTXRollback() throws Exception { log.info("*** starting test"); ServerLocator locator = createInVMNonHALocator(); locator.setBlockOnAcknowledge(true); ClientSessionFactory sessionFactory = createSessionFactory(locator); ClientSession clientSession = sessionFactory.createSession(false, false, false); ClientProducer clientProducer = this.clientSession.createProducer(qName); ClientConsumer consumer = clientSession.createConsumer(qName); ClientConsumer consumer2 = clientSession.createConsumer(qName); clientSession.start(); //need to wait a bit or consumers might be busy Thread.sleep(200); SimpleString groupId = new SimpleString("grp1"); SimpleString groupId2 = new SimpleString("grp2"); int numMessages = 100; for (int i = 0; i < numMessages; i++) { ClientMessage message = createTextMessage(clientSession, "m" + i); if (i % 2 == 0 || i == 0) { message.putStringProperty(Message.HDR_GROUP_ID, groupId); } else { message.putStringProperty(Message.HDR_GROUP_ID, groupId2); } clientProducer.send(message); } CountDownLatch latch = new CountDownLatch(numMessages); DummyMessageHandler dummyMessageHandler = new DummyMessageHandler(latch, true); consumer.setMessageHandler(dummyMessageHandler); DummyMessageHandler dummyMessageHandler2 = new DummyMessageHandler(latch, true); consumer2.setMessageHandler(dummyMessageHandler2); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); Assert.assertEquals(50, dummyMessageHandler.list.size(), dummyMessageHandler.list.size()); int i = 0; for (ClientMessage message : dummyMessageHandler.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } Assert.assertEquals(dummyMessageHandler2.list.size(), 50); i = 1; for (ClientMessage message : dummyMessageHandler2.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } latch = new CountDownLatch(numMessages); dummyMessageHandler.reset(latch); dummyMessageHandler2.reset(latch); clientSession.rollback(); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); Assert.assertEquals(dummyMessageHandler.list.size(), 50); i = 0; for (ClientMessage message : dummyMessageHandler.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } Assert.assertEquals(dummyMessageHandler2.list.size(), 50); i = 1; for (ClientMessage message : dummyMessageHandler2.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } consumer = this.clientSession.createConsumer(qName); Assert.assertNull(consumer.receiveImmediate()); clientSession.close(); locator.close(); } private void dotestMultipleGroupingXACommit() throws Exception { ServerLocator locator = createInVMNonHALocator(); ClientSessionFactory sessionFactory = createSessionFactory(locator); ClientSession clientSession = sessionFactory.createSession(true, false, false); ClientProducer clientProducer = this.clientSession.createProducer(qName); ClientConsumer consumer = clientSession.createConsumer(qName); ClientConsumer consumer2 = clientSession.createConsumer(qName); clientSession.start(); Xid xid = new XidImpl("bq".getBytes(), 4, "gtid".getBytes()); clientSession.start(xid, XAResource.TMNOFLAGS); SimpleString groupId = new SimpleString("grp1"); SimpleString groupId2 = new SimpleString("grp2"); int numMessages = 100; for (int i = 0; i < numMessages; i++) { ClientMessage message = createTextMessage(clientSession, "m" + i); if (i % 2 == 0 || i == 0) { message.putStringProperty(Message.HDR_GROUP_ID, groupId); } else { message.putStringProperty(Message.HDR_GROUP_ID, groupId2); } clientProducer.send(message); } CountDownLatch latch = new CountDownLatch(numMessages); DummyMessageHandler dummyMessageHandler = new DummyMessageHandler(latch, true); consumer.setMessageHandler(dummyMessageHandler); DummyMessageHandler dummyMessageHandler2 = new DummyMessageHandler(latch, true); consumer2.setMessageHandler(dummyMessageHandler2); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); clientSession.end(xid, XAResource.TMSUCCESS); clientSession.prepare(xid); clientSession.commit(xid, false); Assert.assertEquals(dummyMessageHandler.list.size(), 50); int i = 0; for (ClientMessage message : dummyMessageHandler.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } Assert.assertEquals(dummyMessageHandler2.list.size(), 50); i = 1; for (ClientMessage message : dummyMessageHandler2.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } consumer.close(); consumer2.close(); consumer = this.clientSession.createConsumer(qName); Assert.assertNull(consumer.receiveImmediate()); clientSession.close(); locator.close(); } private void doTestMultipleGroupingXARollback() throws Exception { ServerLocator locator = createInVMNonHALocator(); locator.setBlockOnAcknowledge(true); ClientSessionFactory sessionFactory = createSessionFactory(locator); ClientSession clientSession = sessionFactory.createSession(true, false, false); ClientProducer clientProducer = this.clientSession.createProducer(qName); clientSession.start(); ClientConsumer consumer = clientSession.createConsumer(qName); ClientConsumer consumer2 = clientSession.createConsumer(qName); Xid xid = new XidImpl("bq".getBytes(), 4, "gtid".getBytes()); clientSession.start(xid, XAResource.TMNOFLAGS); SimpleString groupId = new SimpleString("grp1"); SimpleString groupId2 = new SimpleString("grp2"); int numMessages = 100; for (int i = 0; i < numMessages; i++) { ClientMessage message = createTextMessage(clientSession, "m" + i); if (i % 2 == 0 || i == 0) { message.putStringProperty(Message.HDR_GROUP_ID, groupId); } else { message.putStringProperty(Message.HDR_GROUP_ID, groupId2); } clientProducer.send(message); } CountDownLatch latch = new CountDownLatch(numMessages); DummyMessageHandler dummyMessageHandler = new DummyMessageHandler(latch, true); consumer.setMessageHandler(dummyMessageHandler); DummyMessageHandler dummyMessageHandler2 = new DummyMessageHandler(latch, true); consumer2.setMessageHandler(dummyMessageHandler2); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); clientSession.end(xid, XAResource.TMSUCCESS); Assert.assertEquals(dummyMessageHandler.list.size(), 50); int i = 0; for (ClientMessage message : dummyMessageHandler.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } Assert.assertEquals(dummyMessageHandler2.list.size(), 50); i = 1; for (ClientMessage message : dummyMessageHandler2.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } latch = new CountDownLatch(numMessages); dummyMessageHandler.reset(latch); dummyMessageHandler2.reset(latch); clientSession.rollback(xid); clientSession.start(xid, XAResource.TMNOFLAGS); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); clientSession.end(xid, XAResource.TMSUCCESS); clientSession.prepare(xid); clientSession.commit(xid, false); Assert.assertEquals(dummyMessageHandler.list.size(), 50); i = 0; for (ClientMessage message : dummyMessageHandler.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } Assert.assertEquals(dummyMessageHandler2.list.size(), 50); i = 1; for (ClientMessage message : dummyMessageHandler2.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } consumer = this.clientSession.createConsumer(qName); Assert.assertNull(consumer.receiveImmediate()); clientSession.close(); locator.close(); } private void doTestMultipleGrouping() throws Exception { ClientProducer clientProducer = clientSession.createProducer(qName); ClientConsumer consumer = clientSession.createConsumer(qName); ClientConsumer consumer2 = clientSession.createConsumer(qName); clientSession.start(); SimpleString groupId = new SimpleString("grp1"); SimpleString groupId2 = new SimpleString("grp2"); int numMessages = 4; for (int i = 0; i < numMessages; i++) { ClientMessage message = createTextMessage(clientSession, "m" + i); if (i % 2 == 0 || i == 0) { message.putStringProperty(Message.HDR_GROUP_ID, groupId); } else { message.putStringProperty(Message.HDR_GROUP_ID, groupId2); } clientProducer.send(message); } CountDownLatch latch = new CountDownLatch(numMessages); DummyMessageHandler dummyMessageHandler = new DummyMessageHandler(latch, true); consumer.setMessageHandler(dummyMessageHandler); DummyMessageHandler dummyMessageHandler2 = new DummyMessageHandler(latch, true); consumer2.setMessageHandler(dummyMessageHandler2); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); Assert.assertEquals(numMessages / 2, dummyMessageHandler.list.size()); int i = 0; for (ClientMessage message : dummyMessageHandler.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } Assert.assertEquals(numMessages / 2, dummyMessageHandler2.list.size()); i = 1; for (ClientMessage message : dummyMessageHandler2.list) { Assert.assertEquals(message.getBodyBuffer().readString(), "m" + i); i += 2; } consumer.close(); consumer2.close(); } @Override @Before public void setUp() throws Exception { super.setUp(); Configuration configuration = createDefaultInVMConfig(); server = addServer(ActiveMQServers.newActiveMQServer(configuration, false)); server.start(); locator = createInVMNonHALocator(); clientSessionFactory = createSessionFactory(locator); clientSession = addClientSession(clientSessionFactory.createSession(false, true, true)); clientSession.createQueue(qName, qName, null, false); } private static class DummyMessageHandler implements MessageHandler { ArrayList<ClientMessage> list = new ArrayList<>(); private CountDownLatch latch; private final boolean acknowledge; private DummyMessageHandler(final CountDownLatch latch, final boolean acknowledge) { this.latch = latch; this.acknowledge = acknowledge; } @Override public void onMessage(final ClientMessage message) { list.add(message); if (acknowledge) { try { message.acknowledge(); } catch (ActiveMQException e) { // ignore } } latch.countDown(); } public void reset(final CountDownLatch latch) { list.clear(); this.latch = latch; } } }