/** * 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.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Vector; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.MessageProducer; import javax.jms.QueueConnection; import javax.jms.QueueReceiver; import javax.jms.QueueSession; import javax.jms.Session; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.JmsMultipleBrokersTestSupport; import org.apache.activemq.broker.BrokerService; import org.apache.activemq.broker.region.RegionBroker; import org.apache.activemq.broker.region.policy.PolicyEntry; import org.apache.activemq.broker.region.policy.PolicyMap; import org.apache.activemq.command.ActiveMQDestination; import org.apache.activemq.command.ActiveMQMessage; import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.BrokerInfo; import org.apache.activemq.network.DiscoveryNetworkConnector; import org.apache.activemq.network.NetworkConnector; import org.apache.activemq.store.kahadb.KahaDBPersistenceAdapter; import org.apache.activemq.util.TimeUtils; import org.apache.activemq.util.Wait; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AMQ4485NetworkOfXBrokersWithNDestsFanoutTransactionTest extends JmsMultipleBrokersTestSupport { static final String payload = new String(new byte[10 * 1024]); private static final Logger LOG = LoggerFactory.getLogger(AMQ4485NetworkOfXBrokersWithNDestsFanoutTransactionTest.class); final int portBase = 61600; final int numBrokers = 4; final int numProducers = 10; final int numMessages = 800; final int consumerSleepTime = 20; StringBuilder brokersUrl = new StringBuilder(); HashMap<ActiveMQQueue, AtomicInteger> accumulators = new HashMap<ActiveMQQueue, AtomicInteger>(); private ArrayList<Throwable> exceptions = new ArrayList<Throwable>(); protected void buildUrlList() throws Exception { for (int i = 0; i < numBrokers; i++) { brokersUrl.append("tcp://localhost:" + (portBase + i)); if (i != numBrokers - 1) { brokersUrl.append(','); } } } protected BrokerService createBroker(int brokerid) throws Exception { BrokerService broker = new BrokerService(); broker.setPersistent(true); broker.setDeleteAllMessagesOnStartup(true); broker.getManagementContext().setCreateConnector(false); broker.setUseJmx(true); broker.setBrokerName("B" + brokerid); broker.addConnector(new URI("tcp://localhost:" + (portBase + brokerid))); addNetworkConnector(broker); broker.setSchedulePeriodForDestinationPurge(0); broker.getSystemUsage().setSendFailIfNoSpace(true); broker.getSystemUsage().getMemoryUsage().setLimit(512 * 1024 * 1024); PolicyMap policyMap = new PolicyMap(); PolicyEntry policyEntry = new PolicyEntry(); policyEntry.setExpireMessagesPeriod(0); policyEntry.setQueuePrefetch(1000); policyEntry.setMemoryLimit(1024 * 1024l); policyEntry.setOptimizedDispatch(false); policyEntry.setProducerFlowControl(false); policyEntry.setEnableAudit(true); policyEntry.setUseCache(true); policyMap.put(new ActiveMQQueue("GW.>"), policyEntry); broker.setDestinationPolicy(policyMap); KahaDBPersistenceAdapter kahaDBPersistenceAdapter = (KahaDBPersistenceAdapter) broker.getPersistenceAdapter(); kahaDBPersistenceAdapter.setConcurrentStoreAndDispatchQueues(false); brokers.put(broker.getBrokerName(), new BrokerItem(broker)); return broker; } private void addNetworkConnector(BrokerService broker) throws Exception { StringBuilder networkConnectorUrl = new StringBuilder("static:(").append(brokersUrl.toString()); networkConnectorUrl.append(')'); for (int i = 0; i < 2; i++) { NetworkConnector nc = new DiscoveryNetworkConnector(new URI(networkConnectorUrl.toString())); nc.setName("Bridge-" + i); nc.setNetworkTTL(1); nc.setDecreaseNetworkConsumerPriority(true); nc.setDynamicOnly(true); nc.setPrefetchSize(100); nc.setDynamicallyIncludedDestinations( Arrays.asList(new ActiveMQDestination[]{new ActiveMQQueue("GW.*")})); broker.addNetworkConnector(nc); } } public void testBrokers() throws Exception { buildUrlList(); for (int i = 0; i < numBrokers; i++) { createBroker(i); } startAllBrokers(); waitForBridgeFormation(numBrokers - 1); verifyPeerBrokerInfos(numBrokers - 1); final List<ConsumerState> consumerStates = startAllGWConsumers(numBrokers); startAllGWFanoutConsumers(numBrokers); LOG.info("Waiting for percolation of consumers.."); TimeUnit.SECONDS.sleep(5); LOG.info("Produce mesages.."); long startTime = System.currentTimeMillis(); // produce produce(numMessages); assertTrue("Got all sent", Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { for (ConsumerState tally : consumerStates) { final int expected = numMessages * (tally.destination.isComposite() ? tally.destination.getCompositeDestinations().length : 1); LOG.info("Tally for: " + tally.brokerName + ", dest: " + tally.destination + " - " + tally.accumulator.get()); if (tally.accumulator.get() != expected) { LOG.info("Tally for: " + tally.brokerName + ", dest: " + tally.destination + " - " + tally.accumulator.get() + " != " + expected + ", " + tally.expected); return false; } LOG.info("got tally on " + tally.brokerName); } return true; } }, 1000 * 60 * 1000l)); assertTrue("No exceptions:" + exceptions, exceptions.isEmpty()); LOG.info("done"); long duration = System.currentTimeMillis() - startTime; LOG.info("Duration:" + TimeUtils.printDuration(duration)); } private void startAllGWFanoutConsumers(int nBrokers) throws Exception { StringBuffer compositeDest = new StringBuffer(); for (int k = 0; k < nBrokers; k++) { compositeDest.append("GW." + k); if (k + 1 != nBrokers) { compositeDest.append(','); } } ActiveMQQueue compositeQ = new ActiveMQQueue(compositeDest.toString()); for (int id = 0; id < nBrokers; id++) { ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("failover:(tcp://localhost:" + (portBase + id) + ")"); connectionFactory.setWatchTopicAdvisories(false); QueueConnection queueConnection = connectionFactory.createQueueConnection(); queueConnection.start(); final QueueSession queueSession = queueConnection.createQueueSession(true, Session.SESSION_TRANSACTED); final MessageProducer producer = queueSession.createProducer(compositeQ); queueSession.createReceiver(new ActiveMQQueue("IN")).setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { try { producer.send(message); queueSession.commit(); } catch (Exception e) { LOG.error("Failed to fanout to GW: " + message, e); } } }); } } private List<ConsumerState> startAllGWConsumers(int nBrokers) throws Exception { List<ConsumerState> consumerStates = new LinkedList<ConsumerState>(); for (int id = 0; id < nBrokers; id++) { ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("failover:(tcp://localhost:" + (portBase + id) + ")"); connectionFactory.setWatchTopicAdvisories(false); QueueConnection queueConnection = connectionFactory.createQueueConnection(); queueConnection.start(); final QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); ActiveMQQueue destination = new ActiveMQQueue("GW." + id); QueueReceiver queueReceiver = queueSession.createReceiver(destination); final ConsumerState consumerState = new ConsumerState(); consumerState.brokerName = ((ActiveMQConnection) queueConnection).getBrokerName(); consumerState.receiver = queueReceiver; consumerState.destination = destination; for (int j = 0; j < numMessages * (consumerState.destination.isComposite() ? consumerState.destination.getCompositeDestinations().length : 1); j++) { consumerState.expected.add(j); } if (!accumulators.containsKey(destination)) { accumulators.put(destination, new AtomicInteger(0)); } consumerState.accumulator = accumulators.get(destination); queueReceiver.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { try { if (consumerSleepTime > 0) { TimeUnit.MILLISECONDS.sleep(consumerSleepTime); } } catch (InterruptedException e) { e.printStackTrace(); } try { consumerState.accumulator.incrementAndGet(); try { consumerState.expected.remove(((ActiveMQMessage) message).getProperty("NUM")); } catch (IOException e) { e.printStackTrace(); } } catch (Exception e) { LOG.error("Failed to commit slow receipt of " + message, e); } } }); consumerStates.add(consumerState); } return consumerStates; } private void produce(int numMessages) throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(numProducers); final AtomicInteger toSend = new AtomicInteger(numMessages); for (int i = 1; i <= numProducers; i++) { final int id = i % numBrokers; executorService.execute(new Runnable() { @Override public void run() { try { ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("failover:(tcp://localhost:" + (portBase + id) + ")"); connectionFactory.setWatchTopicAdvisories(false); QueueConnection queueConnection = connectionFactory.createQueueConnection(); queueConnection.start(); QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer producer = queueSession.createProducer(null); int val = 0; while ((val = toSend.decrementAndGet()) >= 0) { ActiveMQQueue compositeQ = new ActiveMQQueue("IN"); LOG.info("Send to: " + ((ActiveMQConnection) queueConnection).getBrokerName() + ", " + val + ", dest:" + compositeQ); Message textMessage = queueSession.createTextMessage(((ActiveMQConnection) queueConnection).getBrokerName() + "->" + val + " payload:" + payload); textMessage.setIntProperty("NUM", val); producer.send(compositeQ, textMessage); } queueConnection.close(); } catch (Throwable throwable) { throwable.printStackTrace(); exceptions.add(throwable); } } }); } } private void verifyPeerBrokerInfo(BrokerItem brokerItem, final int max) throws Exception { final BrokerService broker = brokerItem.broker; final RegionBroker regionBroker = (RegionBroker) broker.getRegionBroker(); Wait.waitFor(new Wait.Condition() { @Override public boolean isSatisified() throws Exception { LOG.info("verify infos " + broker.getBrokerName() + ", len: " + regionBroker.getPeerBrokerInfos().length); return max == regionBroker.getPeerBrokerInfos().length; } }); LOG.info("verify infos " + broker.getBrokerName() + ", len: " + regionBroker.getPeerBrokerInfos().length); List<String> missing = new ArrayList<String>(); for (int i = 0; i < max; i++) { missing.add("B" + i); } if (max != regionBroker.getPeerBrokerInfos().length) { for (BrokerInfo info : regionBroker.getPeerBrokerInfos()) { LOG.info(info.getBrokerName()); missing.remove(info.getBrokerName()); } LOG.info("Broker infos off.." + missing); } assertEquals(broker.getBrokerName(), max, regionBroker.getPeerBrokerInfos().length); } private void verifyPeerBrokerInfos(final int max) throws Exception { Collection<BrokerItem> brokerList = brokers.values(); for (Iterator<BrokerItem> i = brokerList.iterator(); i.hasNext(); ) { verifyPeerBrokerInfo(i.next(), max); } } protected void tearDown() throws Exception { super.tearDown(); } class ConsumerState { AtomicInteger accumulator; String brokerName; QueueReceiver receiver; ActiveMQDestination destination; Vector<Integer> expected = new Vector<Integer>(); } }