mirror of https://github.com/apache/activemq.git
test case and logging improvements relating to cyclic/multicast discovery network of size 3 and topic message duplication with nteworkTTL > 1
git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@718224 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
0bfb28a0bc
commit
6d1f57b137
|
@ -1739,7 +1739,7 @@ public class ActiveMQConnection implements Connection, TopicConnection, QueueCon
|
||||||
/**
|
/**
|
||||||
* Handles async client internal exceptions.
|
* Handles async client internal exceptions.
|
||||||
* A client internal exception is usually one that has been thrown
|
* A client internal exception is usually one that has been thrown
|
||||||
* by a container runtie component during asynchronous processing of a
|
* by a container runtime component during asynchronous processing of a
|
||||||
* message that does not affect the connection itself.
|
* message that does not affect the connection itself.
|
||||||
* This method notifies the <code>ClientInternalExceptionListener</code> by invoking
|
* This method notifies the <code>ClientInternalExceptionListener</code> by invoking
|
||||||
* its <code>onException</code> method, if one has been registered with this connection.
|
* its <code>onException</code> method, if one has been registered with this connection.
|
||||||
|
|
|
@ -282,9 +282,6 @@ public abstract class PrefetchSubscription extends AbstractSubscription {
|
||||||
}else if (ack.isDeliveredAck()) {
|
}else if (ack.isDeliveredAck()) {
|
||||||
// Message was delivered but not acknowledged: update pre-fetch
|
// Message was delivered but not acknowledged: update pre-fetch
|
||||||
// counters.
|
// counters.
|
||||||
// Acknowledge all dispatched messages up till the message id of
|
|
||||||
// the
|
|
||||||
// acknowledgment.
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (Iterator<MessageReference> iter = dispatched.iterator(); iter.hasNext(); index++) {
|
for (Iterator<MessageReference> iter = dispatched.iterator(); iter.hasNext(); index++) {
|
||||||
final MessageReference node = iter.next();
|
final MessageReference node = iter.next();
|
||||||
|
|
|
@ -500,7 +500,7 @@ public abstract class DemandForwardingBridgeSupport implements NetworkBridge {
|
||||||
BrokerId[] path = info.getBrokerPath();
|
BrokerId[] path = info.getBrokerPath();
|
||||||
if (path != null && path.length >= networkTTL) {
|
if (path != null && path.length >= networkTTL) {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug(configuration.getBrokerName() + " Ignoring Subscription " + info + " restricted to " + networkTTL + " network hops only");
|
LOG.debug(configuration.getBrokerName() + " Ignoring sub from " + remoteBrokerName + ", restricted to " + networkTTL + " network hops only : " + info);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -508,14 +508,14 @@ public abstract class DemandForwardingBridgeSupport implements NetworkBridge {
|
||||||
// Ignore this consumer as it's a consumer we locally sent to
|
// Ignore this consumer as it's a consumer we locally sent to
|
||||||
// the broker.
|
// the broker.
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug(configuration.getBrokerName() + " Ignoring sub " + info + " already routed through this broker once");
|
LOG.debug(configuration.getBrokerName() + " Ignoring sub from " + remoteBrokerName + ", already routed through this broker once : " + info);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isPermissableDestination(info.getDestination())) {
|
if (!isPermissableDestination(info.getDestination())) {
|
||||||
// ignore if not in the permited or in the excluded list
|
// ignore if not in the permitted or in the excluded list
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug(configuration.getBrokerName() + " Ignoring sub " + info + " destination " + info.getDestination() + " is not permiited");
|
LOG.debug(configuration.getBrokerName() + " Ignoring sub from " + remoteBrokerName + ", destination " + info.getDestination() + " is not permiited :" + info);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -525,7 +525,7 @@ public abstract class DemandForwardingBridgeSupport implements NetworkBridge {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug(configuration.getBrokerName() + " Ignoring sub " + info + " already subscribed to matching destination");
|
LOG.debug(configuration.getBrokerName() + " Ignoring sub from " + remoteBrokerName + " as already subscribed to matching destination : " + info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (data.getClass() == DestinationInfo.class) {
|
} else if (data.getClass() == DestinationInfo.class) {
|
||||||
|
|
|
@ -79,10 +79,11 @@ public class DiscoveryNetworkConnector extends NetworkConnector implements Disco
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( localURI.equals(uri) || (connectionFilter != null && !connectionFilter.connectTo(uri))) {
|
if ( localURI.equals(uri) || (connectionFilter != null && !connectionFilter.connectTo(uri))) {
|
||||||
|
LOG.debug("not connecting loopback: " + uri);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
URI connectUri = uri;
|
URI connectUri = uri;
|
||||||
LOG.info("Establishing network connection between from " + localURIName + " to " + connectUri);
|
LOG.info("Establishing network connection from " + localURIName + " to " + connectUri);
|
||||||
|
|
||||||
Transport remoteTransport;
|
Transport remoteTransport;
|
||||||
Transport localTransport;
|
Transport localTransport;
|
||||||
|
@ -213,10 +214,13 @@ public class DiscoveryNetworkConnector extends NetworkConnector implements Disco
|
||||||
String name = super.getName();
|
String name = super.getName();
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
name = discoveryAgent.toString();
|
name = discoveryAgent.toString();
|
||||||
;
|
|
||||||
super.setName(name);
|
super.setName(name);
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "DiscoveryNetworkConnector:" + getName() + ":" + getBrokerService();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,14 +176,7 @@ public class MulticastDiscoveryAgent implements DiscoveryAgent, Runnable {
|
||||||
private long lastAdvertizeTime;
|
private long lastAdvertizeTime;
|
||||||
private AtomicBoolean started = new AtomicBoolean(false);
|
private AtomicBoolean started = new AtomicBoolean(false);
|
||||||
private boolean reportAdvertizeFailed = true;
|
private boolean reportAdvertizeFailed = true;
|
||||||
|
private Executor executor = null;
|
||||||
private final Executor executor = new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
|
|
||||||
public Thread newThread(Runnable runable) {
|
|
||||||
Thread t = new Thread(runable, "Multicast Discovery Agent Notifier");
|
|
||||||
t.setDaemon(true);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the discovery listener
|
* Set the discovery listener
|
||||||
|
@ -304,7 +297,7 @@ public class MulticastDiscoveryAgent implements DiscoveryAgent, Runnable {
|
||||||
mcast.joinGroup(inetAddress);
|
mcast.joinGroup(inetAddress);
|
||||||
mcast.setSoTimeout((int)keepAliveInterval);
|
mcast.setSoTimeout((int)keepAliveInterval);
|
||||||
runner = new Thread(this);
|
runner = new Thread(this);
|
||||||
runner.setName("MulticastDiscovery: " + selfService);
|
runner.setName(this.toString() + ":" + runner.getName());
|
||||||
runner.setDaemon(true);
|
runner.setDaemon(true);
|
||||||
runner.start();
|
runner.start();
|
||||||
doAdvertizeSelf();
|
doAdvertizeSelf();
|
||||||
|
@ -410,10 +403,8 @@ public class MulticastDiscoveryAgent implements DiscoveryAgent, Runnable {
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
data = new RemoteBrokerData(brokerName, service);
|
data = new RemoteBrokerData(brokerName, service);
|
||||||
brokersByService.put(service, data);
|
brokersByService.put(service, data);
|
||||||
|
|
||||||
fireServiceAddEvent(data);
|
fireServiceAddEvent(data);
|
||||||
doAdvertizeSelf();
|
doAdvertizeSelf();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
data.updateHeartBeat();
|
data.updateHeartBeat();
|
||||||
if (data.doRecovery()) {
|
if (data.doRecovery()) {
|
||||||
|
@ -467,7 +458,7 @@ public class MulticastDiscoveryAgent implements DiscoveryAgent, Runnable {
|
||||||
// Have the listener process the event async so that
|
// Have the listener process the event async so that
|
||||||
// he does not block this thread since we are doing time sensitive
|
// he does not block this thread since we are doing time sensitive
|
||||||
// processing of events.
|
// processing of events.
|
||||||
executor.execute(new Runnable() {
|
getExecutor().execute(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
DiscoveryListener discoveryListener = MulticastDiscoveryAgent.this.discoveryListener;
|
DiscoveryListener discoveryListener = MulticastDiscoveryAgent.this.discoveryListener;
|
||||||
if (discoveryListener != null) {
|
if (discoveryListener != null) {
|
||||||
|
@ -486,7 +477,7 @@ public class MulticastDiscoveryAgent implements DiscoveryAgent, Runnable {
|
||||||
// Have the listener process the event async so that
|
// Have the listener process the event async so that
|
||||||
// he does not block this thread since we are doing time sensitive
|
// he does not block this thread since we are doing time sensitive
|
||||||
// processing of events.
|
// processing of events.
|
||||||
executor.execute(new Runnable() {
|
getExecutor().execute(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
DiscoveryListener discoveryListener = MulticastDiscoveryAgent.this.discoveryListener;
|
DiscoveryListener discoveryListener = MulticastDiscoveryAgent.this.discoveryListener;
|
||||||
if (discoveryListener != null) {
|
if (discoveryListener != null) {
|
||||||
|
@ -497,6 +488,20 @@ public class MulticastDiscoveryAgent implements DiscoveryAgent, Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Executor getExecutor() {
|
||||||
|
if (executor == null) {
|
||||||
|
final String threadName = "Notifier-" + this.toString();
|
||||||
|
executor = new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
|
||||||
|
public Thread newThread(Runnable runable) {
|
||||||
|
Thread t = new Thread(runable, threadName);
|
||||||
|
t.setDaemon(true);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
public long getBackOffMultiplier() {
|
public long getBackOffMultiplier() {
|
||||||
return backOffMultiplier;
|
return backOffMultiplier;
|
||||||
}
|
}
|
||||||
|
@ -540,4 +545,10 @@ public class MulticastDiscoveryAgent implements DiscoveryAgent, Runnable {
|
||||||
public void setGroup(String group) {
|
public void setGroup(String group) {
|
||||||
this.group = group;
|
this.group = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MulticastDiscoveryAgent-"
|
||||||
|
+ (selfService != null ? "advertise:" + selfService : "listener:" + this.discoveryListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,229 @@
|
||||||
|
package org.apache.activemq.usecases;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.jms.Connection;
|
||||||
|
import javax.jms.JMSException;
|
||||||
|
import javax.jms.Message;
|
||||||
|
import javax.jms.MessageConsumer;
|
||||||
|
import javax.jms.MessageListener;
|
||||||
|
import javax.jms.MessageProducer;
|
||||||
|
import javax.jms.Session;
|
||||||
|
import javax.jms.TextMessage;
|
||||||
|
import javax.jms.Topic;
|
||||||
|
|
||||||
|
import org.apache.activemq.ActiveMQConnectionFactory;
|
||||||
|
import org.apache.activemq.broker.BrokerService;
|
||||||
|
import org.apache.activemq.network.NetworkConnector;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class NoDuplicateOnTopicNetworkTest extends TestCase {
|
||||||
|
private static final Log LOG = LogFactory
|
||||||
|
.getLog(NoDuplicateOnTopicNetworkTest.class);
|
||||||
|
|
||||||
|
private static final String MULTICAST_DEFAULT = "multicast://default";
|
||||||
|
private static final String BROKER_1 = "tcp://localhost:61626";
|
||||||
|
private static final String BROKER_2 = "tcp://localhost:61636";
|
||||||
|
private static final String BROKER_3 = "tcp://localhost:61646";
|
||||||
|
private BrokerService broker1;
|
||||||
|
private BrokerService broker2;
|
||||||
|
private BrokerService broker3;
|
||||||
|
|
||||||
|
private boolean dynamicOnly = false;
|
||||||
|
// no duplicates in cyclic network if networkTTL <=1
|
||||||
|
// when > 1, subscriptions perculate around resulting in duplicates as there is no
|
||||||
|
// memory of the original subscription.
|
||||||
|
// solution for 6.0 using org.apache.activemq.command.ConsumerInfo.getNetworkConsumerIds()
|
||||||
|
private int ttl = 1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
broker3 = createAndStartBroker("broker3", BROKER_3);
|
||||||
|
Thread.sleep(3000);
|
||||||
|
broker2 = createAndStartBroker("broker2", BROKER_2);
|
||||||
|
Thread.sleep(3000);
|
||||||
|
broker1 = createAndStartBroker("broker1", BROKER_1);
|
||||||
|
Thread.sleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BrokerService createAndStartBroker(String name, String addr)
|
||||||
|
throws Exception {
|
||||||
|
BrokerService broker = new BrokerService();
|
||||||
|
broker.setBrokerName(name);
|
||||||
|
broker.addConnector(addr).setDiscoveryUri(new URI(MULTICAST_DEFAULT));
|
||||||
|
broker.setUseJmx(false);
|
||||||
|
|
||||||
|
NetworkConnector networkConnector = broker
|
||||||
|
.addNetworkConnector(MULTICAST_DEFAULT);
|
||||||
|
networkConnector.setDecreaseNetworkConsumerPriority(true);
|
||||||
|
networkConnector.setDynamicOnly(dynamicOnly);
|
||||||
|
networkConnector.setNetworkTTL(ttl);
|
||||||
|
|
||||||
|
broker.start();
|
||||||
|
|
||||||
|
return broker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
broker1.stop();
|
||||||
|
broker2.stop();
|
||||||
|
broker3.stop();
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testProducerConsumerTopic() throws Exception {
|
||||||
|
final String topicName = "broadcast";
|
||||||
|
Thread producerThread = new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
TopicWithDuplicateMessages producer = new TopicWithDuplicateMessages();
|
||||||
|
producer.setBrokerURL(BROKER_1);
|
||||||
|
producer.setTopicName(topicName);
|
||||||
|
try {
|
||||||
|
producer.produce();
|
||||||
|
} catch (JMSException e) {
|
||||||
|
fail("Unexpected " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final TopicWithDuplicateMessages consumer = new TopicWithDuplicateMessages();
|
||||||
|
Thread consumerThread = new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
consumer.setBrokerURL(BROKER_2);
|
||||||
|
consumer.setTopicName(topicName);
|
||||||
|
try {
|
||||||
|
consumer.consumer();
|
||||||
|
consumer.getLatch().await(60, TimeUnit.SECONDS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail("Unexpected " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
consumerThread.start();
|
||||||
|
Thread.sleep(1000);
|
||||||
|
producerThread.start();
|
||||||
|
producerThread.join();
|
||||||
|
consumerThread.join();
|
||||||
|
|
||||||
|
Map<String, String> map = new HashMap<String, String>();
|
||||||
|
for (String msg : consumer.getMessageStrings()) {
|
||||||
|
assertTrue("is not a duplicate: " + msg, !map.containsKey(msg));
|
||||||
|
map.put(msg, msg);
|
||||||
|
}
|
||||||
|
assertEquals("got all required messages: " + map.size(), consumer
|
||||||
|
.getNumMessages(), map.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
class TopicWithDuplicateMessages {
|
||||||
|
private String brokerURL;
|
||||||
|
private String topicName;
|
||||||
|
private Connection connection;
|
||||||
|
private Session session;
|
||||||
|
private Topic topic;
|
||||||
|
private MessageProducer producer;
|
||||||
|
private MessageConsumer consumer;
|
||||||
|
|
||||||
|
private List<String> receivedStrings = new ArrayList<String>();
|
||||||
|
private int numMessages = 10;
|
||||||
|
private CountDownLatch recievedLatch = new CountDownLatch(numMessages);
|
||||||
|
|
||||||
|
public CountDownLatch getLatch() {
|
||||||
|
return recievedLatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getMessageStrings() {
|
||||||
|
return receivedStrings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBrokerURL() {
|
||||||
|
return brokerURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBrokerURL(String brokerURL) {
|
||||||
|
this.brokerURL = brokerURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTopicName() {
|
||||||
|
return topicName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTopicName(String topicName) {
|
||||||
|
this.topicName = topicName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createConnection() throws JMSException {
|
||||||
|
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(
|
||||||
|
brokerURL);
|
||||||
|
connection = factory.createConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTopic() throws JMSException {
|
||||||
|
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||||
|
topic = session.createTopic(topicName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createProducer() throws JMSException {
|
||||||
|
producer = session.createProducer(topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createConsumer() throws JMSException {
|
||||||
|
consumer = session.createConsumer(topic);
|
||||||
|
consumer.setMessageListener(new MessageListener() {
|
||||||
|
|
||||||
|
public void onMessage(Message arg0) {
|
||||||
|
TextMessage msg = (TextMessage) arg0;
|
||||||
|
try {
|
||||||
|
LOG.debug("Received message [" + msg.getText() + "]");
|
||||||
|
receivedStrings.add(msg.getText());
|
||||||
|
recievedLatch.countDown();
|
||||||
|
} catch (JMSException e) {
|
||||||
|
fail("Unexpected :" + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void publish() throws JMSException {
|
||||||
|
for (int i = 0; i < numMessages; i++) {
|
||||||
|
TextMessage textMessage = session.createTextMessage();
|
||||||
|
String message = "message: " + i;
|
||||||
|
LOG.debug("Sending message[" + message + "]");
|
||||||
|
textMessage.setText(message);
|
||||||
|
producer.send(textMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void produce() throws JMSException {
|
||||||
|
createConnection();
|
||||||
|
createTopic();
|
||||||
|
createProducer();
|
||||||
|
connection.start();
|
||||||
|
publish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void consumer() throws JMSException {
|
||||||
|
createConnection();
|
||||||
|
createTopic();
|
||||||
|
createConsumer();
|
||||||
|
connection.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumMessages() {
|
||||||
|
return numMessages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue