/*
* 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.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
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.client.impl.ClientConsumerInternal;
import org.apache.activemq.artemis.core.postoffice.Binding;
import org.apache.activemq.artemis.core.postoffice.Bindings;
import org.apache.activemq.artemis.core.postoffice.QueueBinding;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.Consumer;
import org.apache.activemq.artemis.core.server.impl.ServerConsumerImpl;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class ConsumerWindowSizeTest extends ActiveMQTestBase {
private final SimpleString addressA = new SimpleString("addressA");
private final SimpleString queueA = new SimpleString("queueA");
private final int TIMEOUT = 5;
private static final IntegrationTestLogger log = IntegrationTestLogger.LOGGER;
private static final boolean isTrace = ConsumerWindowSizeTest.log.isTraceEnabled();
private ServerLocator locator;
protected boolean isNetty() {
return false;
}
@Override
@Before
public void setUp() throws Exception {
super.setUp();
locator = createFactory(isNetty());
}
private int getMessageEncodeSize(final SimpleString address) throws Exception {
ServerLocator locator = createInVMNonHALocator();
ClientSessionFactory cf = createSessionFactory(locator);
ClientSession session = cf.createSession(false, true, true);
ClientMessage message = session.createMessage(false);
// we need to set the destination so we can calculate the encodesize correctly
message.setAddress(address);
int encodeSize = message.getEncodeSize();
session.close();
cf.close();
return encodeSize;
}
// https://jira.jboss.org/jira/browse/HORNETQ-385
@Test
public void testReceiveImmediateWithZeroWindow() throws Exception {
ActiveMQServer server = createServer(false, isNetty());
server.start();
locator.setConsumerWindowSize(0);
ClientSessionFactory sf = createSessionFactory(locator);
ClientSession session = sf.createSession(false, false, false);
session.createQueue("testWindow", "testWindow", true);
session.close();
int numConsumers = 5;
ArrayList<ClientConsumer> consumers = new ArrayList<>();
ArrayList<ClientSession> sessions = new ArrayList<>();
for (int i = 0; i < numConsumers; i++) {
ClientSession session1 = sf.createSession();
ClientConsumer consumer = session1.createConsumer("testWindow");
consumers.add(consumer);
session1.start();
sessions.add(session1);
consumer.receiveImmediate();
}
ClientSession senderSession = sf.createSession(false, false);
ClientProducer producer = senderSession.createProducer("testWindow");
ClientMessage sent = senderSession.createMessage(true);
sent.putStringProperty("hello", "world");
producer.send(sent);
senderSession.commit();
senderSession.start();
ClientConsumer consumer = consumers.get(2);
ClientMessage received = consumer.receive(1000);
Assert.assertNotNull(received);
for (ClientSession tmpSess : sessions) {
tmpSess.close();
}
senderSession.close();
}
// https://jira.jboss.org/jira/browse/HORNETQ-385
@Test
public void testReceiveImmediateWithZeroWindow2() throws Exception {
ActiveMQServer server = createServer(true);
try (ServerLocator locator = createInVMNonHALocator()) {
server.start();
locator.setConsumerWindowSize(0);
ClientSessionFactory sf = createSessionFactory(locator);
ClientSession session = sf.createSession(false, false, false);
session.createQueue("testReceive", "testReceive", true);
session.close();
ClientSession sessionProd = sf.createSession(false, false);
ClientMessage msg = sessionProd.createMessage(true);
msg.putStringProperty("hello", "world");
ClientProducer prod = sessionProd.createProducer("testReceive");
prod.send(msg);
sessionProd.commit();
ClientSession session1 = sf.createSession();
ClientConsumer consumer = session1.createConsumer("testReceive");
session1.start();
Thread.sleep(1000);
ClientMessage message = null;
message = consumer.receiveImmediate();
// message = consumer.receive(1000); // the test will pass if used receive(1000) instead of receiveImmediate
Assert.assertNotNull(message);
message.acknowledge();
prod.send(msg);
sessionProd.commit();
message = consumer.receive(10000);
Assert.assertNotNull(message);
message.acknowledge();
session.close();
session1.close();
sessionProd.close();
}
}
// https://jira.jboss.org/jira/browse/HORNETQ-385
@Test
public void testReceiveImmediateWithZeroWindow3() throws Exception {
ActiveMQServer server = createServer(false, isNetty());
server.start();
locator.setConsumerWindowSize(0);
ClientSessionFactory sf = createSessionFactory(locator);
ClientSession session = sf.createSession(false, false, false);
session.createQueue("testWindow", "testWindow", true);
session.close();
int numConsumers = 5;
ArrayList<ClientConsumer> consumers = new ArrayList<>();
ArrayList<ClientSession> sessions = new ArrayList<>();
for (int i = 0; i < numConsumers; i++) {
ClientSession session1 = sf.createSession();
ClientConsumer consumer = session1.createConsumer("testWindow");
consumers.add(consumer);
session1.start();
sessions.add(session1);
consumer.receive(10);
}
ClientSession senderSession = sf.createSession(false, false);
ClientProducer producer = senderSession.createProducer("testWindow");
ClientMessage sent = senderSession.createMessage(true);
sent.putStringProperty("hello", "world");
producer.send(sent);
senderSession.commit();
senderSession.start();
ClientConsumer consumer = consumers.get(2);
ClientMessage received = consumer.receive(1000);
Assert.assertNotNull(received);
for (ClientSession tmpSess : sessions) {
tmpSess.close();
}
senderSession.close();
}
@Test
public void testReceiveImmediateWithZeroWindow4() throws Exception {
ActiveMQServer server = createServer(false, isNetty());
server.start();
locator.setConsumerWindowSize(0);
ClientSessionFactory sf = createSessionFactory(locator);
ClientSession session = sf.createSession(false, false, false);
session.createQueue("testWindow", "testWindow", true);
session.close();
int numConsumers = 5;
ArrayList<ClientConsumer> consumers = new ArrayList<>();
ArrayList<ClientSession> sessions = new ArrayList<>();
for (int i = 0; i < numConsumers; i++) {
ClientSession session1 = sf.createSession();
ClientConsumer consumer = session1.createConsumer("testWindow");
consumers.add(consumer);
session1.start();
sessions.add(session1);
Assert.assertNull(consumer.receive(10));
}
ClientSession senderSession = sf.createSession(false, false);
ClientProducer producer = senderSession.createProducer("testWindow");
ClientMessage sent = senderSession.createMessage(true);
sent.putStringProperty("hello", "world");
producer.send(sent);
senderSession.commit();
senderSession.start();
ClientConsumer consumer = consumers.get(2);
ClientMessage received = consumer.receive(5000);
Assert.assertNotNull(received);
for (ClientSession tmpSess : sessions) {
tmpSess.close();
}
senderSession.close();
}
@Test
public void testMultipleImmediate() throws Exception {
final int NUMBER_OF_MESSAGES = 200;
ActiveMQServer server = createServer(false, isNetty());
server.start();
locator.setConsumerWindowSize(0);
final ClientSessionFactory sf = createSessionFactory(locator);
{
ClientSession session = sf.createSession(false, false, false);
session.createQueue("testWindow", "testWindow", true);
session.close();
}
Thread[] threads = new Thread[10];
final AtomicInteger errors = new AtomicInteger(0);
final CountDownLatch latchStart = new CountDownLatch(1);
final AtomicInteger received = new AtomicInteger(0);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread() {
@Override
public void run() {
try {
ClientSession session = sf.createSession(false, false);
ClientConsumer consumer = session.createConsumer("testWindow");
session.start();
latchStart.await(10, TimeUnit.SECONDS);
while (true) {
ClientMessage msg = consumer.receiveImmediate();
if (msg == null) {
break;
}
msg.acknowledge();
session.commit();
received.incrementAndGet();
}
} catch (Throwable e) {
e.printStackTrace();
errors.incrementAndGet();
}
}
};
threads[i].start();
}
ClientSession senderSession = sf.createSession(false, false);
ClientProducer producer = senderSession.createProducer("testWindow");
ClientMessage sent = senderSession.createMessage(true);
sent.putStringProperty("hello", "world");
for (int i = 0; i < NUMBER_OF_MESSAGES; i++) {
producer.send(sent);
senderSession.commit();
}
latchStart.countDown();
for (Thread t : threads) {
t.join();
}
Assert.assertEquals(0, errors.get());
Assert.assertEquals(NUMBER_OF_MESSAGES, received.get());
}
@Test
public void testSingleImmediate() throws Exception {
final int NUMBER_OF_MESSAGES = 200;
ActiveMQServer server = createServer(false, isNetty());
server.start();
locator.setConsumerWindowSize(0);
final ClientSessionFactory sf = createSessionFactory(locator);
{
ClientSession session = sf.createSession(false, false, false);
session.createQueue("testWindow", "testWindow", true);
session.close();
}
final AtomicInteger received = new AtomicInteger(0);
ClientSession senderSession = sf.createSession(false, false);
ClientProducer producer = senderSession.createProducer("testWindow");
ClientMessage sent = senderSession.createMessage(true);
sent.putStringProperty("hello", "world");
for (int i = 0; i < NUMBER_OF_MESSAGES; i++) {
producer.send(sent);
}
senderSession.commit();
ClientSession session = sf.createSession(false, false);
ClientConsumer consumer = session.createConsumer("testWindow");
session.start();
while (true) {
ClientMessage msg = consumer.receiveImmediate();
if (msg == null) {
System.out.println("Returning null");
break;
}
msg.acknowledge();
session.commit();
received.incrementAndGet();
}
Assert.assertEquals(NUMBER_OF_MESSAGES, received.get());
}
/*
* tests send window size. we do this by having 2 receivers on the q. since we roundrobin the consumer for delivery we
* know if consumer 1 has received n messages then consumer 2 must have also have received n messages or at least up
* to its window size
* */
@Test
public void testSendWindowSize() throws Exception {
ActiveMQServer messagingService = createServer(false, isNetty());
locator.setBlockOnNonDurableSend(false);
messagingService.start();
int numMessage = 100;
locator.setConsumerWindowSize(numMessage * getMessageEncodeSize(addressA));
ClientSessionFactory cf = createSessionFactory(locator);
ClientSession sendSession = cf.createSession(false, true, true);
ClientSession receiveSession = cf.createSession(false, true, true);
sendSession.createQueue(addressA, queueA, false);
ClientConsumer receivingConsumer = receiveSession.createConsumer(queueA);
ClientSession session = cf.createSession(false, true, true);
ClientProducer cp = sendSession.createProducer(addressA);
ClientConsumer cc = session.createConsumer(queueA);
session.start();
receiveSession.start();
for (int i = 0; i < numMessage * 4; i++) {
cp.send(sendSession.createMessage(false));
}
for (int i = 0; i < numMessage * 2; i++) {
ClientMessage m = receivingConsumer.receive(5000);
Assert.assertNotNull(m);
m.acknowledge();
}
receiveSession.close();
for (int i = 0; i < numMessage * 2; i++) {
ClientMessage m = cc.receive(5000);
Assert.assertNotNull(m);
m.acknowledge();
}
session.close();
sendSession.close();
Assert.assertEquals(0, getMessageCount(messagingService, queueA.toString()));
}
@Test
public void testSlowConsumerBufferingOne() throws Exception {
ActiveMQServer server = createServer(false, isNetty());
ClientSession sessionB = null;
ClientSession session = null;
try {
final int numberOfMessages = 100;
server.start();
locator.setConsumerWindowSize(1);
ClientSessionFactory sf = createSessionFactory(locator);
session = sf.createSession(false, true, true);
SimpleString ADDRESS = addressA;
session.createQueue(ADDRESS, ADDRESS, true);
sessionB = sf.createSession(false, true, true);
sessionB.start();
session.start();
ClientConsumer consNeverUsed = sessionB.createConsumer(ADDRESS);
ClientConsumer cons1 = session.createConsumer(ADDRESS);
ClientProducer prod = session.createProducer(ADDRESS);
for (int i = 0; i < numberOfMessages; i++) {
prod.send(createTextMessage(session, "Msg" + i));
}
for (int i = 0; i < numberOfMessages - 1; i++) {
ClientMessage msg = cons1.receive(1000);
Assert.assertNotNull("expected message at i = " + i, msg);
msg.acknowledge();
}
ClientMessage msg = consNeverUsed.receive(500);
Assert.assertNotNull(msg);
msg.acknowledge();
session.close();
session = null;
sessionB.close();
sessionB = null;
Assert.assertEquals(0, getMessageCount(server, ADDRESS.toString()));
} finally {
try {
if (session != null) {
session.close();
}
if (sessionB != null) {
sessionB.close();
}
} catch (Exception ignored) {
}
}
}
@Test
public void testSlowConsumerNoBuffer() throws Exception {
internalTestSlowConsumerNoBuffer(false);
}
// I believe this test became invalid after we started using another thread to deliver the large message
public void disabled_testSlowConsumerNoBufferLargeMessages() throws Exception {
internalTestSlowConsumerNoBuffer(true);
}
private void internalTestSlowConsumerNoBuffer(final boolean largeMessages) throws Exception {
ActiveMQServer server = createServer(false, isNetty());
ClientSession sessionB = null;
ClientSession session = null;
try {
final int numberOfMessages = 100;
server.start();
locator.setConsumerWindowSize(0);
ClientSessionFactory sf = createSessionFactory(locator);
if (largeMessages) {
sf.getServerLocator().setMinLargeMessageSize(100);
}
session = sf.createSession(false, true, true);
SimpleString ADDRESS = addressA;
session.createQueue(ADDRESS, ADDRESS, true);
sessionB = sf.createSession(false, true, true);
sessionB.start();
session.start();
ClientConsumerInternal consNeverUsed = (ClientConsumerInternal) sessionB.createConsumer(ADDRESS);
ClientProducer prod = session.createProducer(ADDRESS);
// This will force a credit to be sent, but if the message wasn't received we need to take out that credit from
// the server
// or the client will be buffering messages
Assert.assertNull(consNeverUsed.receive(1));
ClientMessage msg = createTextMessage(session, "This one will expire");
if (largeMessages) {
msg.getBodyBuffer().writeBytes(new byte[600]);
}
msg.setExpiration(System.currentTimeMillis() + 100);
prod.send(msg);
msg = createTextMessage(session, "First-on-non-buffered");
prod.send(msg);
Thread.sleep(110);
// It will be able to receive another message, but it shouldn't send a credit again, as the credit was already
// sent
msg = consNeverUsed.receive(TIMEOUT * 1000);
Assert.assertNotNull(msg);
Assert.assertEquals("First-on-non-buffered", getTextMessage(msg));
msg.acknowledge();
ClientConsumer cons1 = session.createConsumer(ADDRESS);
for (int i = 0; i < numberOfMessages; i++) {
msg = createTextMessage(session, "Msg" + i);
if (largeMessages) {
msg.getBodyBuffer().writeBytes(new byte[600]);
}
prod.send(msg);
}
for (int i = 0; i < numberOfMessages; i++) {
msg = cons1.receive(1000);
Assert.assertNotNull("expected message at i = " + i, msg);
Assert.assertEquals("Msg" + i, getTextMessage(msg));
msg.acknowledge();
}
Assert.assertEquals(0, consNeverUsed.getBufferSize());
session.close();
session = null;
sessionB.close();
sessionB = null;
Assert.assertEquals(0, getMessageCount(server, ADDRESS.toString()));
} finally {
try {
if (session != null) {
session.close();
}
if (sessionB != null) {
sessionB.close();
}
} catch (Exception ignored) {
}
}
}
@Test
public void testSlowConsumerNoBuffer2() throws Exception {
internalTestSlowConsumerNoBuffer2(false);
}
@Test
public void testSlowConsumerNoBuffer2LargeMessages() throws Exception {
internalTestSlowConsumerNoBuffer2(true);
}
private void internalTestSlowConsumerNoBuffer2(final boolean largeMessages) throws Exception {
ActiveMQServer server = createServer(false, isNetty());
ClientSession session1 = null;
ClientSession session2 = null;
try {
final int numberOfMessages = 10;
server.start();
locator.setConsumerWindowSize(0);
if (largeMessages) {
locator.setMinLargeMessageSize(100);
}
ClientSessionFactory sf = createSessionFactory(locator);
session1 = sf.createSession(false, true, true);
session2 = sf.createSession(false, true, true);
session1.start();
session2.start();
SimpleString ADDRESS = new SimpleString("some-queue");
session1.createQueue(ADDRESS, ADDRESS, true);
ClientConsumerInternal cons1 = (ClientConsumerInternal) session1.createConsumer(ADDRESS);
// Note we make sure we send the messages *before* cons2 is created
ClientProducer prod = session1.createProducer(ADDRESS);
for (int i = 0; i < numberOfMessages; i++) {
ClientMessage msg = createTextMessage(session1, "Msg" + i);
if (largeMessages) {
msg.getBodyBuffer().writeBytes(new byte[600]);
}
prod.send(msg);
}
ClientConsumerInternal cons2 = (ClientConsumerInternal) session2.createConsumer(ADDRESS);
for (int i = 0; i < numberOfMessages / 2; i++) {
ClientMessage msg = cons1.receive(1000);
Assert.assertNotNull("expected message at i = " + i, msg);
String str = getTextMessage(msg);
Assert.assertEquals("Msg" + i, str);
log.info("got msg " + str);
msg.acknowledge();
Assert.assertEquals("A slow consumer shouldn't buffer anything on the client side!", 0, cons1.getBufferSize());
}
for (int i = numberOfMessages / 2; i < numberOfMessages; i++) {
ClientMessage msg = cons2.receive(1000);
Assert.assertNotNull("expected message at i = " + i, msg);
String str = getTextMessage(msg);
log.info("got msg " + str);
Assert.assertEquals("Msg" + i, str);
msg.acknowledge();
Assert.assertEquals("A slow consumer shouldn't buffer anything on the client side!", 0, cons2.getBufferSize());
}
session1.close(); // just to make sure everything is flushed and no pending packets on the sending buffer, or
// the getMessageCount would fail
session2.close();
session1 = sf.createSession(false, true, true);
session1.start();
session2 = sf.createSession(false, true, true);
session2.start();
prod = session1.createProducer(ADDRESS);
Assert.assertEquals(0, getMessageCount(server, ADDRESS.toString()));
// This should also work the other way around
cons1.close();
cons2.close();
cons1 = (ClientConsumerInternal) session1.createConsumer(ADDRESS);
// Note we make sure we send the messages *before* cons2 is created
for (int i = 0; i < numberOfMessages; i++) {
ClientMessage msg = createTextMessage(session1, "Msg" + i);
if (largeMessages) {
msg.getBodyBuffer().writeBytes(new byte[600]);
}
prod.send(msg);
}
cons2 = (ClientConsumerInternal) session2.createConsumer(ADDRESS);
// Now we receive on cons2 first
for (int i = 0; i < numberOfMessages / 2; i++) {
ClientMessage msg = cons2.receive(1000);
Assert.assertNotNull("expected message at i = " + i, msg);
Assert.assertEquals("Msg" + i, msg.getBodyBuffer().readString());
msg.acknowledge();
Assert.assertEquals("A slow consumer shouldn't buffer anything on the client side!", 0, cons2.getBufferSize());
}
for (int i = numberOfMessages / 2; i < numberOfMessages; i++) {
ClientMessage msg = cons1.receive(1000);
Assert.assertNotNull("expected message at i = " + i, msg);
Assert.assertEquals("Msg" + i, msg.getBodyBuffer().readString());
msg.acknowledge();
Assert.assertEquals("A slow consumer shouldn't buffer anything on the client side!", 0, cons1.getBufferSize());
}
session1.close();
session1 = null;
session2.close();
session2 = null;
Assert.assertEquals(0, getMessageCount(server, ADDRESS.toString()));
} finally {
try {
if (session1 != null) {
session1.close();
}
if (session2 != null) {
session2.close();
}
} catch (Exception ignored) {
}
}
}
@Test
public void testSaveBuffersOnLargeMessage() throws Exception {
ActiveMQServer server = createServer(false, isNetty());
ClientSession session1 = null;
try {
final int numberOfMessages = 10;
server.start();
locator.setConsumerWindowSize(0).setMinLargeMessageSize(100);
ClientSessionFactory sf = createSessionFactory(locator);
session1 = sf.createSession(false, true, true);
session1.start();
SimpleString ADDRESS = new SimpleString("some-queue");
session1.createQueue(ADDRESS, ADDRESS, true);
ClientConsumerInternal cons1 = (ClientConsumerInternal) session1.createConsumer(ADDRESS);
// Note we make sure we send the messages *before* cons2 is created
ClientProducer prod = session1.createProducer(ADDRESS);
for (int i = 0; i < numberOfMessages; i++) {
ClientMessage msg = session1.createMessage(true);
msg.getBodyBuffer().writeBytes(new byte[600]);
prod.send(msg);
}
for (int i = 0; i < numberOfMessages; i++) {
ClientMessage msg = cons1.receive(1000);
Assert.assertNotNull("expected message at i = " + i, msg);
msg.saveToOutputStream(new FakeOutputStream());
msg.acknowledge();
Assert.assertEquals("A slow consumer shouldn't buffer anything on the client side!", 0, cons1.getBufferSize());
}
session1.close(); // just to make sure everything is flushed and no pending packets on the sending buffer, or
session1.close();
session1 = null;
Assert.assertEquals(0, getMessageCount(server, ADDRESS.toString()));
} finally {
try {
if (session1 != null) {
session1.close();
}
} catch (Exception ignored) {
}
}
}
class FakeOutputStream extends OutputStream {
/* (non-Javadoc)
* @see java.io.OutputStream#write(int)
*/
@Override
public void write(int b) throws IOException {
}
}
@Test
public void testSlowConsumerOnMessageHandlerNoBuffers() throws Exception {
internalTestSlowConsumerOnMessageHandlerNoBuffers(false);
}
@Test
public void testSlowConsumerOnMessageHandlerNoBuffersLargeMessage() throws Exception {
internalTestSlowConsumerOnMessageHandlerNoBuffers(true);
}
@Test
public void testFlowControl() throws Exception {
internalTestFlowControlOnRollback(false);
}
@Test
public void testFlowControlLargeMessage() throws Exception {
internalTestFlowControlOnRollback(true);
}
private void internalTestFlowControlOnRollback(final boolean isLargeMessage) throws Exception {
ActiveMQServer server = createServer(false, isNetty());
AddressSettings settings = new AddressSettings().setMaxDeliveryAttempts(-1);
server.getAddressSettingsRepository().addMatch("#", settings);
ClientSession session = null;
try {
final int numberOfMessages = 100;
server.start();
locator.setConsumerWindowSize(300000);
if (isLargeMessage) {
// something to ensure we are using large messages
locator.setMinLargeMessageSize(100);
} else {
// To make sure large messages won't kick in, we set anything large
locator.setMinLargeMessageSize(Integer.MAX_VALUE);
}
ClientSessionFactory sf = createSessionFactory(locator);
session = sf.createSession(false, false, false);
SimpleString ADDRESS = new SimpleString("some-queue");
session.createQueue(ADDRESS, ADDRESS, true);
ClientProducer producer = session.createProducer(ADDRESS);
for (int i = 0; i < numberOfMessages; i++) {
ClientMessage msg = session.createMessage(true);
msg.putIntProperty("count", i);
msg.getBodyBuffer().writeBytes(new byte[1024]);
producer.send(msg);
}
session.commit();
ClientConsumerInternal consumer = (ClientConsumerInternal) session.createConsumer(ADDRESS);
session.start();
for (int repeat = 0; repeat < 100; repeat++) {
long timeout = System.currentTimeMillis() + 2000;
// At least 10 messages on the buffer
while (timeout > System.currentTimeMillis() && consumer.getBufferSize() <= 10) {
Thread.sleep(10);
}
Assert.assertTrue(consumer.getBufferSize() >= 10);
ClientMessage msg = consumer.receive(500);
msg.getBodyBuffer().readByte();
Assert.assertNotNull(msg);
msg.acknowledge();
session.rollback();
}
for (int i = 0; i < numberOfMessages; i++) {
ClientMessage msg = consumer.receive(5000);
Assert.assertNotNull(msg);
msg.getBodyBuffer().readByte();
msg.acknowledge();
session.commit();
}
} finally {
try {
if (session != null) {
session.close();
}
} catch (Exception ignored) {
}
}
}
public void internalTestSlowConsumerOnMessageHandlerNoBuffers(final boolean largeMessages) throws Exception {
ActiveMQServer server = createServer(false, isNetty());
ClientSession sessionB = null;
ClientSession session = null;
try {
final int numberOfMessages = 100;
server.start();
locator.setConsumerWindowSize(0);
if (largeMessages) {
locator.setMinLargeMessageSize(100);
}
ClientSessionFactory sf = createSessionFactory(locator);
session = sf.createSession(false, true, true);
SimpleString ADDRESS = new SimpleString("some-queue");
session.createQueue(ADDRESS, ADDRESS, true);
sessionB = sf.createSession(false, true, true);
sessionB.start();
session.start();
ClientConsumerInternal consReceiveOneAndHold = (ClientConsumerInternal) sessionB.createConsumer(ADDRESS);
final CountDownLatch latchReceived = new CountDownLatch(2);
final CountDownLatch latchDone = new CountDownLatch(1);
// It can't close the session while the large message is being read
final CountDownLatch latchRead = new CountDownLatch(1);
// It should receive two messages and then give up
class LocalHandler implements MessageHandler {
boolean failed = false;
int count = 0;
/* (non-Javadoc)
* @see MessageHandler#onMessage(ClientMessage)
*/
@Override
public synchronized void onMessage(final ClientMessage message) {
try {
String str = getTextMessage(message);
failed = failed || !str.equals("Msg" + count);
message.acknowledge();
latchReceived.countDown();
if (count++ == 1) {
// it will hold here for a while
if (!latchDone.await(TIMEOUT, TimeUnit.SECONDS)) {
// a timed wait, so if the test fails, one less thread around
new Exception("ClientConsuemrWindowSizeTest Handler couldn't receive signal in less than 5 seconds").printStackTrace();
failed = true;
}
if (largeMessages) {
message.getBodyBuffer().readBytes(new byte[600]);
}
latchRead.countDown();
}
} catch (Exception e) {
e.printStackTrace(); // Hudson / JUnit report
failed = true;
}
}
}
LocalHandler handler = new LocalHandler();
ClientConsumer cons1 = session.createConsumer(ADDRESS);
ClientProducer prod = session.createProducer(ADDRESS);
for (int i = 0; i < numberOfMessages; i++) {
ClientMessage msg = createTextMessage(session, "Msg" + i);
if (largeMessages) {
msg.getBodyBuffer().writeBytes(new byte[600]);
}
prod.send(msg);
}
consReceiveOneAndHold.setMessageHandler(handler);
Assert.assertTrue(latchReceived.await(TIMEOUT, TimeUnit.SECONDS));
Assert.assertEquals(0, consReceiveOneAndHold.getBufferSize());
for (int i = 2; i < numberOfMessages; i++) {
ClientMessage msg = cons1.receive(1000);
Assert.assertNotNull("expected message at i = " + i, msg);
Assert.assertEquals("Msg" + i, getTextMessage(msg));
msg.acknowledge();
}
Assert.assertEquals(0, consReceiveOneAndHold.getBufferSize());
latchDone.countDown();
// The test can' t close the session while the message is still being read, or it could interrupt the data
Assert.assertTrue(latchRead.await(10, TimeUnit.SECONDS));
session.close();
session = null;
sessionB.close();
sessionB = null;
Assert.assertEquals(0, getMessageCount(server, ADDRESS.toString()));
Assert.assertFalse("MessageHandler received a failure", handler.failed);
} finally {
try {
if (session != null) {
session.close();
}
if (sessionB != null) {
sessionB.close();
}
} catch (Exception ignored) {
}
}
}
@Test
public void testSlowConsumerOnMessageHandlerBufferOne() throws Exception {
internalTestSlowConsumerOnMessageHandlerBufferOne(false);
}
private void internalTestSlowConsumerOnMessageHandlerBufferOne(final boolean largeMessage) throws Exception {
ActiveMQServer server = createServer(false, isNetty());
ClientSession sessionB = null;
ClientSession session = null;
try {
final int numberOfMessages = 100;
server.start();
locator.setConsumerWindowSize(1);
if (largeMessage) {
locator.setMinLargeMessageSize(100);
}
ClientSessionFactory sf = createSessionFactory(locator);
session = sf.createSession(false, true, true);
SimpleString ADDRESS = new SimpleString("some-queue");
session.createQueue(ADDRESS, ADDRESS, true);
sessionB = sf.createSession(false, true, true);
sessionB.start();
session.start();
ClientConsumerInternal consReceiveOneAndHold = (ClientConsumerInternal) sessionB.createConsumer(ADDRESS);
final CountDownLatch latchReceived = new CountDownLatch(2);
final CountDownLatch latchReceivedBuffered = new CountDownLatch(3);
final CountDownLatch latchDone = new CountDownLatch(1);
// It should receive two messages and then give up
class LocalHandler implements MessageHandler {
boolean failed = false;
int count = 0;
/* (non-Javadoc)
* @see MessageHandler#onMessage(ClientMessage)
*/
@Override
public synchronized void onMessage(final ClientMessage message) {
try {
log.info("received msg " + message);
String str = getTextMessage(message);
if (ConsumerWindowSizeTest.isTrace) {
ConsumerWindowSizeTest.log.trace("Received message " + str);
}
ConsumerWindowSizeTest.log.info("Received message " + str);
failed = failed || !str.equals("Msg" + count);
message.acknowledge();
latchReceived.countDown();
latchReceivedBuffered.countDown();
if (count++ == 1) {
// it will hold here for a while
if (!latchDone.await(TIMEOUT, TimeUnit.SECONDS)) {
new Exception("ClientConsuemrWindowSizeTest Handler couldn't receive signal in less than 5 seconds").printStackTrace();
failed = true;
}
}
} catch (Exception e) {
e.printStackTrace(); // Hudson / JUnit report
failed = true;
}
}
}
LocalHandler handler = new LocalHandler();
ClientProducer prod = session.createProducer(ADDRESS);
for (int i = 0; i < numberOfMessages; i++) {
ClientMessage msg = createTextMessage(session, "Msg" + i);
if (largeMessage) {
msg.getBodyBuffer().writeBytes(new byte[600]);
}
prod.send(msg);
}
consReceiveOneAndHold.setMessageHandler(handler);
Assert.assertTrue(latchReceived.await(TIMEOUT, TimeUnit.SECONDS));
log.info("bs " + consReceiveOneAndHold.getBufferSize());
long timeout = System.currentTimeMillis() + 1000 * TIMEOUT;
while (consReceiveOneAndHold.getBufferSize() == 0 && System.currentTimeMillis() < timeout) {
log.info("bs " + consReceiveOneAndHold.getBufferSize());
Thread.sleep(10);
}
Assert.assertEquals(1, consReceiveOneAndHold.getBufferSize());
ClientConsumer cons1 = session.createConsumer(ADDRESS);
for (int i = 3; i < numberOfMessages; i++) {
ClientMessage msg = cons1.receive(1000);
Assert.assertNotNull("expected message at i = " + i, msg);
String text = getTextMessage(msg);
Assert.assertEquals("Msg" + i, text);
msg.acknowledge();
}
latchDone.countDown();
Assert.assertTrue(latchReceivedBuffered.await(TIMEOUT, TimeUnit.SECONDS));
session.close();
session = null;
sessionB.close();
sessionB = null;
Assert.assertEquals(0, getMessageCount(server, ADDRESS.toString()));
Assert.assertFalse("MessageHandler received a failure", handler.failed);
} finally {
try {
if (session != null) {
session.close();
}
if (sessionB != null) {
sessionB.close();
}
} catch (Exception ignored) {
ignored.printStackTrace();
}
}
}
@Test
public void testNoWindowRoundRobin() throws Exception {
testNoWindowRoundRobin(false);
}
private void testNoWindowRoundRobin(final boolean largeMessages) throws Exception {
ActiveMQServer server = createServer(false, isNetty());
ClientSession sessionA = null;
ClientSession sessionB = null;
try {
final int numberOfMessages = 100;
server.start();
locator.setConsumerWindowSize(-1);
if (largeMessages) {
locator.setMinLargeMessageSize(100);
}
ClientSessionFactory sf = createSessionFactory(locator);
sessionA = sf.createSession(false, true, true);
SimpleString ADDRESS = new SimpleString("some-queue");
sessionA.createQueue(ADDRESS, ADDRESS, true);
sessionB = sf.createSession(false, true, true);
sessionA.start();
sessionB.start();
ClientConsumerInternal consA = (ClientConsumerInternal) sessionA.createConsumer(ADDRESS);
ClientConsumerInternal consB = (ClientConsumerInternal) sessionB.createConsumer(ADDRESS);
{
// We can only guarantee round robing with WindowSize = -1, after the ServerConsumer object received
// SessionConsumerFlowCreditMessage(-1)
// Since that is done asynchronously we verify that the information was received before we proceed on
// sending messages or else the distribution won't be
// even as expected by the test
Bindings bindings = server.getPostOffice().getBindingsForAddress(ADDRESS);
Assert.assertEquals(1, bindings.getBindings().size());
for (Binding binding : bindings.getBindings()) {
Collection<Consumer> consumers = ((QueueBinding) binding).getQueue().getConsumers();
for (Consumer consumer : consumers) {
ServerConsumerImpl consumerImpl = (ServerConsumerImpl) consumer;
long timeout = System.currentTimeMillis() + 5000;
while (timeout > System.currentTimeMillis() && consumerImpl.getAvailableCredits() != null) {
Thread.sleep(10);
}
Assert.assertNull(consumerImpl.getAvailableCredits());
}
}
}
ClientProducer prod = sessionA.createProducer(ADDRESS);
for (int i = 0; i < numberOfMessages; i++) {
ClientMessage msg = createTextMessage(sessionA, "Msg" + i);
if (largeMessages) {
msg.getBodyBuffer().writeBytes(new byte[600]);
}
prod.send(msg);
}
long timeout = System.currentTimeMillis() + TIMEOUT * 1000;
boolean foundA = false;
boolean foundB = false;
do {
foundA = consA.getBufferSize() == numberOfMessages / 2;
foundB = consB.getBufferSize() == numberOfMessages / 2;
Thread.sleep(10);
} while ((!foundA || !foundB) && System.currentTimeMillis() < timeout);
Assert.assertTrue("ConsumerA didn't receive the expected number of messages on buffer (consA=" + consA.getBufferSize() +
", consB=" +
consB.getBufferSize() +
") foundA = " +
foundA +
" foundB = " +
foundB, foundA);
Assert.assertTrue("ConsumerB didn't receive the expected number of messages on buffer (consA=" + consA.getBufferSize() +
", consB=" +
consB.getBufferSize() +
") foundA = " +
foundA +
" foundB = " +
foundB, foundB);
} finally {
try {
if (sessionA != null) {
sessionA.close();
}
if (sessionB != null) {
sessionB.close();
}
} catch (Exception ignored) {
}
}
}
}