/** * 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 java.lang.Thread.UncaughtExceptionHandler; import java.net.URI; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import javax.jms.Destination; import javax.jms.MessageConsumer; import junit.framework.Test; import org.apache.activemq.JmsMultipleBrokersTestSupport; import org.apache.activemq.broker.BrokerService; import org.apache.activemq.broker.jmx.ManagementContext; 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.network.ConditionalNetworkBridgeFilterFactory; import org.apache.activemq.network.NetworkConnector; import org.apache.activemq.util.Wait; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AMQ4607Test extends JmsMultipleBrokersTestSupport implements UncaughtExceptionHandler { private static final Logger LOG = LoggerFactory.getLogger(AMQ4607Test.class); public static final int BROKER_COUNT = 3; public static final int CONSUMER_COUNT = 1; public static final int MESSAGE_COUNT = 0; public static final boolean CONDUIT = true; public static final int TIMEOUT = 20000; public boolean duplex = true; protected Map<String, MessageConsumer> consumerMap; Map<Thread, Throwable> unhandeledExceptions = new HashMap<Thread, Throwable>(); private void assertNoUnhandeledExceptions() { for( Entry<Thread, Throwable> e: unhandeledExceptions.entrySet()) { LOG.error("Thread:" + e.getKey() + " Had unexpected: " + e.getValue()); } assertTrue("There are no unhandelled exceptions, see: log for detail on: " + unhandeledExceptions, unhandeledExceptions.isEmpty()); } public NetworkConnector bridge(String from, String to) throws Exception { NetworkConnector networkConnector = bridgeBrokers(from, to, true, -1, CONDUIT); networkConnector.setSuppressDuplicateQueueSubscriptions(true); networkConnector.setDecreaseNetworkConsumerPriority(true); networkConnector.setConsumerTTL(1); networkConnector.setDuplex(duplex); return networkConnector; } public static Test suite() { return suite(AMQ4607Test.class); } public void initCombos() { addCombinationValues("duplex", new Boolean[]{Boolean.TRUE, Boolean.FALSE}); } public void testMigratingConsumer() throws Exception { bridge("Broker0", "Broker1"); if (!duplex) bridge("Broker1", "Broker0"); bridge("Broker1", "Broker2"); if (!duplex) bridge("Broker2", "Broker1"); bridge("Broker0", "Broker2"); if (!duplex) bridge("Broker2", "Broker0"); startAllBrokers(); this.waitForBridgeFormation(); Destination dest = createDestination("TEST.FOO", false); sendMessages("Broker0", dest, 1); for (int i=0; i< BROKER_COUNT; i++) { MessageConsumer messageConsumer = createConsumer("Broker" + i, dest, "DoNotConsume = 'true'"); for (int J = 0; J < BROKER_COUNT; J++) { assertExactConsumersConnect("Broker" + J, dest, CONSUMER_COUNT, TIMEOUT); } assertNoUnhandeledExceptions(); assertExactMessageCount("Broker" + i, dest, 1, TIMEOUT); messageConsumer.close(); LOG.info("Check for no consumers.."); for (int J = 0; J < BROKER_COUNT; J++) { assertExactConsumersConnect("Broker" + J, dest, 0, TIMEOUT); } } // now consume the message final String brokerId = "Broker2"; MessageConsumer messageConsumer = createConsumer(brokerId, dest); assertTrue("Consumed ok", Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return brokers.get(brokerId).allMessages.getMessageIds().size() == 1; } })); messageConsumer.close(); } public void testMigratingConsumerFullCircle() throws Exception { bridge("Broker0", "Broker1"); if (!duplex) bridge("Broker1", "Broker0"); bridge("Broker1", "Broker2"); if (!duplex) bridge("Broker2", "Broker1"); bridge("Broker0", "Broker2"); if (!duplex) bridge("Broker2", "Broker0"); // allow full loop, immediate replay back to 0 from 2 ConditionalNetworkBridgeFilterFactory conditionalNetworkBridgeFilterFactory = new ConditionalNetworkBridgeFilterFactory(); conditionalNetworkBridgeFilterFactory.setReplayDelay(0); conditionalNetworkBridgeFilterFactory.setReplayWhenNoConsumers(true); brokers.get("Broker2").broker.getDestinationPolicy().getDefaultEntry().setNetworkBridgeFilterFactory(conditionalNetworkBridgeFilterFactory); startAllBrokers(); this.waitForBridgeFormation(); Destination dest = createDestination("TEST.FOO", false); sendMessages("Broker0", dest, 1); for (int i=0; i< BROKER_COUNT; i++) { MessageConsumer messageConsumer = createConsumer("Broker" + i, dest, "DoNotConsume = 'true'"); for (int J = 0; J < BROKER_COUNT; J++) { assertExactConsumersConnect("Broker" + J, dest, CONSUMER_COUNT, TIMEOUT); } assertNoUnhandeledExceptions(); // validate the message has been forwarded assertExactMessageCount("Broker" + i, dest, 1, TIMEOUT); messageConsumer.close(); LOG.info("Check for no consumers.."); for (int J = 0; J < BROKER_COUNT; J++) { assertExactConsumersConnect("Broker" + J, dest, 0, TIMEOUT); } } // now consume the message from the origin LOG.info("Consume from origin..."); final String brokerId = "Broker0"; MessageConsumer messageConsumer = createConsumer(brokerId, dest); assertTrue("Consumed ok", Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { return brokers.get(brokerId).allMessages.getMessageIds().size() == 1; } })); messageConsumer.close(); } protected void assertExactMessageCount(final String brokerName, Destination destination, final int count, long timeout) throws Exception { ManagementContext context = brokers.get(brokerName).broker.getManagementContext(); final QueueViewMBean queueViewMBean = (QueueViewMBean) context.newProxyInstance(brokers.get(brokerName).broker.getAdminView().getQueues()[0], QueueViewMBean.class, false); assertTrue("Excepected queue depth: " + count + " on: " + brokerName, Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { long currentCount = queueViewMBean.getQueueSize(); LOG.info("On " + brokerName + " current queue size for " + queueViewMBean + ", " + currentCount); if (count != currentCount) { LOG.info("Sub IDs: " + Arrays.asList(queueViewMBean.getSubscriptions())); } return currentCount == count; } }, timeout)); } protected void assertExactConsumersConnect(final String brokerName, Destination destination, final int count, long timeout) throws Exception { final ManagementContext context = brokers.get(brokerName).broker.getManagementContext(); assertTrue("Excepected consumers count: " + count + " on: " + brokerName, Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { try { QueueViewMBean queueViewMBean = (QueueViewMBean) context.newProxyInstance(brokers.get(brokerName).broker.getAdminView().getQueues()[0], QueueViewMBean.class, false); long currentCount = queueViewMBean.getConsumerCount(); LOG.info("On " + brokerName + " current consumer count for " + queueViewMBean + ", " + currentCount); if (count != currentCount) { LOG.info("Sub IDs: " + Arrays.asList(queueViewMBean.getSubscriptions())); } return currentCount == count; } catch (Exception e) { LOG.warn("Unexpected: " + e, e); return false; } } }, timeout)); } public void setUp() throws Exception { super.setUp(); unhandeledExceptions.clear(); Thread.setDefaultUncaughtExceptionHandler(this); // Setup n brokers for (int i = 0; i < BROKER_COUNT; i++) { createBroker(new URI("broker:(tcp://localhost:6161" + i + ")/Broker" + i + "?persistent=false&useJmx=true")); } consumerMap = new LinkedHashMap<String, MessageConsumer>(); } @Override protected void configureBroker(BrokerService brokerService) { PolicyEntry policyEntry = new PolicyEntry(); policyEntry.setExpireMessagesPeriod(0); PolicyMap policyMap = new PolicyMap(); policyMap.setDefaultEntry(policyEntry); brokerService.setDestinationPolicy(policyMap); } public void uncaughtException(Thread t, Throwable e) { synchronized(unhandeledExceptions) { unhandeledExceptions.put(t,e); } } }