/**
* 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.bugs;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.broker.jmx.QueueViewMBean;
import org.apache.activemq.broker.region.policy.PolicyEntry;
import org.apache.activemq.broker.region.policy.PolicyMap;
import org.apache.activemq.store.kahadb.KahaDBStore;
import org.apache.activemq.store.kahadb.plist.PListStoreImpl;
import org.apache.activemq.util.Wait;
import org.apache.activemq.util.Wait.Condition;
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;
/**
* Test behavior of senders when broker side producer flow control kicks in.
*/
public class AMQ5712Test {
private static final Logger LOG = LoggerFactory.getLogger(AMQ5712Test.class);
@Rule public TestName name = new TestName();
private BrokerService brokerService;
private Connection connection;
@Before
public void setUp() throws Exception {
brokerService = createBroker();
brokerService.start();
brokerService.waitUntilStarted();
}
@After
public void tearDown() throws Exception {
if (connection != null) {
try {
connection.close();
} catch (Exception e) {}
}
if (brokerService != null) {
brokerService.stop();
brokerService.waitUntilStopped();
brokerService = null;
}
}
private Connection createConnection() throws Exception {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://localhost?create=false");
factory.setAlwaysSyncSend(true);
return factory.createConnection();
}
@Test(timeout = 120000)
public void test() throws Exception {
connection = createConnection();
connection.start();
final int MSG_COUNT = 100;
final Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
final Queue queue = session.createQueue(name.getMethodName());
MessageProducer producer = session.createProducer(queue);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
final QueueViewMBean queueView = getProxyToQueue(name.getMethodName());
byte[] payload = new byte[65535];
Arrays.fill(payload, (byte) 255);
final CountDownLatch done = new CountDownLatch(1);
final AtomicInteger counter = new AtomicInteger();
Thread purge = new Thread(new Runnable() {
@Override
public void run() {
try {
while (!done.await(5, TimeUnit.SECONDS)) {
if (queueView.getBlockedSends() > 0 && queueView.getQueueSize() > 0) {
long queueSize = queueView.getQueueSize();
LOG.info("Queue send blocked at {} messages", queueSize);
MessageConsumer consumer = session.createConsumer(queue);
for (int i = 0; i < queueSize; i++) {
Message message = consumer.receive(60000);
if (message != null) {
counter.incrementAndGet();
message.acknowledge();
} else {
LOG.warn("Got null message when none as expected.");
}
}
consumer.close();
}
}
} catch (Exception ex) {
}
}
});
purge.start();
for (int i = 0; i < MSG_COUNT; i++) {
BytesMessage message = session.createBytesMessage();
message.writeBytes(payload);
producer.send(message);
LOG.info("sent message: {}", i);
}
done.countDown();
purge.join(60000);
if (purge.isAlive()) {
fail("Consumer thread should have read initial batch and completed.");
}
//wait for processed acked messages
assertTrue(Wait.waitFor(new Condition() {
@Override
public boolean isSatisified() throws Exception {
return queueView.getDequeueCount() == counter.get();
}
}));
long remainingQueued = queueView.getQueueSize();
LOG.info("Remaining messages to consume: {}", remainingQueued);
assertEquals(remainingQueued, MSG_COUNT - counter.get());
MessageConsumer consumer = session.createConsumer(queue);
for (int i = counter.get(); i < MSG_COUNT; i++) {
Message message = consumer.receive(5000);
assertNotNull("Should not get null message", consumer);
counter.incrementAndGet();
message.acknowledge();
LOG.info("Read message: {}", i);
}
assertEquals("Should consume all messages", MSG_COUNT, counter.get());
}
protected BrokerService createBroker() throws Exception {
BrokerService answer = new BrokerService();
KahaDBStore persistence = createStore(true);
persistence.setJournalMaxFileLength(1024 * 1024 * 1);
answer.setPersistent(true);
answer.setPersistenceAdapter(persistence);
answer.setDeleteAllMessagesOnStartup(true);
answer.getSystemUsage().getMemoryUsage().setLimit(1024 * 1024 * 6);
answer.getSystemUsage().getTempUsage().setLimit(1024 * 1024 * 5);
answer.getSystemUsage().getStoreUsage().setLimit(1024 * 1024 * 5);
answer.setUseJmx(true);
answer.getManagementContext().setCreateConnector(false);
answer.setSchedulerSupport(false);
answer.setAdvisorySupport(false);
PListStoreImpl tempStore = ((PListStoreImpl)answer.getSystemUsage().getTempUsage().getStore());
tempStore.setCleanupInterval(10000);
tempStore.setJournalMaxFileLength(1024 * 1024 * 2);
PolicyEntry policy = new PolicyEntry();
policy.setProducerFlowControl(false);
PolicyMap policyMap = new PolicyMap();
policyMap.setDefaultEntry(policy);
answer.setDestinationPolicy(policyMap);
return answer;
}
private KahaDBStore createStore(boolean delete) throws IOException {
KahaDBStore kaha = new KahaDBStore();
kaha.setDirectory(new File("target/activemq-data/kahadb"));
if( delete ) {
kaha.deleteAllMessages();
}
return kaha;
}
protected QueueViewMBean getProxyToQueue(String name) throws MalformedObjectNameException, JMSException {
ObjectName queueViewMBeanName = new ObjectName("org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName="+name);
QueueViewMBean proxy = (QueueViewMBean) brokerService.getManagementContext()
.newProxyInstance(queueViewMBeanName, QueueViewMBean.class, true);
return proxy;
}
}