/** * 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.assertTrue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import javax.jms.Connection; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Session; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.broker.BrokerService; import org.apache.activemq.broker.region.policy.PolicyEntry; import org.apache.activemq.broker.region.policy.PolicyMap; import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.store.kahadb.KahaDBPersistenceAdapter; import org.apache.activemq.util.Wait; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AMQ4368Test { private static final Logger LOG = LoggerFactory.getLogger(AMQ4368Test.class); private BrokerService broker; private ActiveMQConnectionFactory connectionFactory; private final Destination destination = new ActiveMQQueue("large_message_queue"); private String connectionUri; @Before public void setUp() throws Exception { broker = createBroker(); connectionUri = broker.addConnector("tcp://localhost:0").getPublishableConnectString(); broker.start(); connectionFactory = new ActiveMQConnectionFactory(connectionUri); } @After public void tearDown() throws Exception { broker.stop(); broker.waitUntilStopped(); } protected BrokerService createBroker() throws Exception { BrokerService broker = new BrokerService(); PolicyEntry policy = new PolicyEntry(); policy.setUseCache(false); broker.setDestinationPolicy(new PolicyMap()); broker.getDestinationPolicy().setDefaultEntry(policy); KahaDBPersistenceAdapter kahadb = new KahaDBPersistenceAdapter(); kahadb.setCheckForCorruptJournalFiles(true); kahadb.setCleanupInterval(1000); kahadb.deleteAllMessages(); broker.setPersistenceAdapter(kahadb); broker.getSystemUsage().getMemoryUsage().setLimit(1024*1024*100); broker.setUseJmx(false); return broker; } abstract class Client implements Runnable { private final String name; final AtomicBoolean done = new AtomicBoolean(); CountDownLatch startedLatch; CountDownLatch doneLatch = new CountDownLatch(1); Connection connection; Session session; final AtomicLong size = new AtomicLong(); Client(String name, CountDownLatch startedLatch) { this.name = name; this.startedLatch = startedLatch; } public void start() { LOG.info("Starting: " + name); new Thread(this, name).start(); } public void stopAsync() { done.set(true); } public void stop() throws InterruptedException { stopAsync(); if (!doneLatch.await(20, TimeUnit.MILLISECONDS)) { try { connection.close(); doneLatch.await(); } catch (Exception e) { } } } @Override public void run() { try { connection = createConnection(); connection.start(); try { session = createSession(); work(); } finally { try { connection.close(); } catch (JMSException ignore) { } LOG.info("Stopped: " + name); } } catch (Exception e) { e.printStackTrace(); done.set(true); } finally { doneLatch.countDown(); } } protected Session createSession() throws JMSException { return connection.createSession(false, Session.AUTO_ACKNOWLEDGE); } protected Connection createConnection() throws JMSException { return connectionFactory.createConnection(); } abstract protected void work() throws Exception; } class ProducingClient extends Client { ProducingClient(String name, CountDownLatch startedLatch) { super(name, startedLatch); } private String createMessage() { StringBuffer stringBuffer = new StringBuffer(); for (long i = 0; i < 1000000; i++) { stringBuffer.append("1234567890"); } return stringBuffer.toString(); } @Override protected void work() throws Exception { String data = createMessage(); MessageProducer producer = session.createProducer(destination); startedLatch.countDown(); while (!done.get()) { producer.send(session.createTextMessage(data)); long i = size.incrementAndGet(); if ((i % 1000) == 0) { LOG.info("produced " + i + "."); } } } } class ConsumingClient extends Client { public ConsumingClient(String name, CountDownLatch startedLatch) { super(name, startedLatch); } @Override protected void work() throws Exception { MessageConsumer consumer = session.createConsumer(destination); startedLatch.countDown(); while (!done.get()) { Message msg = consumer.receive(100); if (msg != null) { size.incrementAndGet(); } } } } @Test public void testENTMQ220() throws Exception { LOG.info("Start test."); CountDownLatch producer1Started = new CountDownLatch(1); CountDownLatch producer2Started = new CountDownLatch(1); CountDownLatch listener1Started = new CountDownLatch(1); final ProducingClient producer1 = new ProducingClient("1", producer1Started); final ProducingClient producer2 = new ProducingClient("2", producer2Started); final ConsumingClient listener1 = new ConsumingClient("subscriber-1", listener1Started); final AtomicLong lastSize = new AtomicLong(); try { producer1.start(); producer2.start(); listener1.start(); producer1Started.await(15, TimeUnit.SECONDS); producer2Started.await(15, TimeUnit.SECONDS); listener1Started.await(15, TimeUnit.SECONDS); lastSize.set(listener1.size.get()); for (int i = 0; i < 10; i++) { Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return listener1.size.get() > lastSize.get(); } }); long size = listener1.size.get(); LOG.info("Listener 1: consumed: " + (size - lastSize.get())); assertTrue("No messages received on iteration: " + i, size > lastSize.get()); lastSize.set(size); } } finally { LOG.info("Stopping clients"); producer1.stop(); producer2.stop(); listener1.stop(); } } }