mirror of https://github.com/apache/activemq.git
git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@1326298 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
50d3e8e09a
commit
1b9f5f6727
|
@ -16,20 +16,29 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.activemq.broker.region.virtual;
|
package org.apache.activemq.broker.region.virtual;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.activemq.broker.Broker;
|
import org.apache.activemq.broker.Broker;
|
||||||
import org.apache.activemq.broker.ProducerBrokerExchange;
|
import org.apache.activemq.broker.ProducerBrokerExchange;
|
||||||
import org.apache.activemq.broker.region.Destination;
|
import org.apache.activemq.broker.region.Destination;
|
||||||
import org.apache.activemq.broker.region.Subscription;
|
import org.apache.activemq.broker.region.Subscription;
|
||||||
import org.apache.activemq.command.ActiveMQDestination;
|
import org.apache.activemq.command.ActiveMQDestination;
|
||||||
import org.apache.activemq.command.Message;
|
import org.apache.activemq.command.Message;
|
||||||
|
import org.apache.activemq.filter.BooleanExpression;
|
||||||
import org.apache.activemq.filter.MessageEvaluationContext;
|
import org.apache.activemq.filter.MessageEvaluationContext;
|
||||||
import org.apache.activemq.filter.NonCachedMessageEvaluationContext;
|
import org.apache.activemq.filter.NonCachedMessageEvaluationContext;
|
||||||
|
import org.apache.activemq.plugin.SubQueueSelectorCacheBroker;
|
||||||
import java.io.IOException;
|
import org.apache.activemq.selector.SelectorParser;
|
||||||
import java.util.List;
|
import org.apache.activemq.util.LRUCache;
|
||||||
import java.util.Set;
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class SelectorAwareVirtualTopicInterceptor extends VirtualTopicInterceptor {
|
public class SelectorAwareVirtualTopicInterceptor extends VirtualTopicInterceptor {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(SelectorAwareVirtualTopicInterceptor.class);
|
||||||
|
LRUCache<String,BooleanExpression> expressionCache = new LRUCache<String,BooleanExpression>();
|
||||||
|
private SubQueueSelectorCacheBroker selectorCachePlugin;
|
||||||
|
|
||||||
public SelectorAwareVirtualTopicInterceptor(Destination next, String prefix, String postfix, boolean local) {
|
public SelectorAwareVirtualTopicInterceptor(Destination next, String prefix, String postfix, boolean local) {
|
||||||
super(next, prefix, postfix, local);
|
super(next, prefix, postfix, local);
|
||||||
|
@ -45,24 +54,81 @@ public class SelectorAwareVirtualTopicInterceptor extends VirtualTopicIntercepto
|
||||||
Set<Destination> destinations = broker.getDestinations(destination);
|
Set<Destination> destinations = broker.getDestinations(destination);
|
||||||
|
|
||||||
for (Destination dest : destinations) {
|
for (Destination dest : destinations) {
|
||||||
if (matchesSomeConsumer(message, dest)) {
|
if (matchesSomeConsumer(broker, message, dest)) {
|
||||||
dest.send(context, message.copy());
|
dest.send(context, message.copy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean matchesSomeConsumer(Message message, Destination dest) throws IOException {
|
private boolean matchesSomeConsumer(final Broker broker, Message message, Destination dest) throws IOException {
|
||||||
boolean matches = false;
|
boolean matches = false;
|
||||||
MessageEvaluationContext msgContext = new NonCachedMessageEvaluationContext();
|
MessageEvaluationContext msgContext = new NonCachedMessageEvaluationContext();
|
||||||
msgContext.setDestination(dest.getActiveMQDestination());
|
msgContext.setDestination(dest.getActiveMQDestination());
|
||||||
msgContext.setMessageReference(message);
|
msgContext.setMessageReference(message);
|
||||||
List<Subscription> subs = dest.getConsumers();
|
List<Subscription> subs = dest.getConsumers();
|
||||||
for (Subscription sub: subs) {
|
for (Subscription sub : subs) {
|
||||||
if (sub.matches(message, msgContext)) {
|
if (sub.matches(message, msgContext)) {
|
||||||
matches = true;
|
matches = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matches == false && subs.size() == 0) {
|
||||||
|
matches = tryMatchingCachedSubs(broker, dest, msgContext);
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryMatchingCachedSubs(final Broker broker, Destination dest, MessageEvaluationContext msgContext) {
|
||||||
|
boolean matches = false;
|
||||||
|
LOG.debug("No active consumer match found. Will try cache if configured...");
|
||||||
|
|
||||||
|
//retrieve the specific plugin class and lookup the selector for the destination.
|
||||||
|
final SubQueueSelectorCacheBroker cache = getSubQueueSelectorCacheBrokerPlugin(broker);
|
||||||
|
|
||||||
|
if (cache != null) {
|
||||||
|
final String selector = cache.getSelector(dest.getActiveMQDestination().getQualifiedName());
|
||||||
|
if (selector != null) {
|
||||||
|
try {
|
||||||
|
final BooleanExpression expression = getExpression(selector);
|
||||||
|
matches = expression.matches(msgContext);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BooleanExpression getExpression(String selector) throws Exception{
|
||||||
|
BooleanExpression result;
|
||||||
|
synchronized(expressionCache){
|
||||||
|
result = expressionCache.get(selector);
|
||||||
|
if (result == null){
|
||||||
|
result = compileSelector(selector);
|
||||||
|
expressionCache.put(selector,result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The SubQueueSelectorCacheBroker instance or null if no such broker is available.
|
||||||
|
*/
|
||||||
|
private SubQueueSelectorCacheBroker getSubQueueSelectorCacheBrokerPlugin(final Broker broker) {
|
||||||
|
if (selectorCachePlugin == null) {
|
||||||
|
selectorCachePlugin = (SubQueueSelectorCacheBroker) broker.getAdaptor(SubQueueSelectorCacheBroker.class);
|
||||||
|
} //if
|
||||||
|
|
||||||
|
return selectorCachePlugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-compile the JMS selector.
|
||||||
|
*
|
||||||
|
* @param selectorExpression The non-null JMS selector expression.
|
||||||
|
*/
|
||||||
|
private BooleanExpression compileSelector(final String selectorExpression) throws Exception {
|
||||||
|
return SelectorParser.parse(selectorExpression);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.apache.activemq.broker.region.DestinationFilter;
|
||||||
import org.apache.activemq.command.ActiveMQDestination;
|
import org.apache.activemq.command.ActiveMQDestination;
|
||||||
import org.apache.activemq.command.ActiveMQQueue;
|
import org.apache.activemq.command.ActiveMQQueue;
|
||||||
import org.apache.activemq.command.Message;
|
import org.apache.activemq.command.Message;
|
||||||
|
import org.apache.activemq.util.LRUCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Destination which implements <a
|
* A Destination which implements <a
|
||||||
|
@ -34,6 +35,7 @@ public class VirtualTopicInterceptor extends DestinationFilter {
|
||||||
private String prefix;
|
private String prefix;
|
||||||
private String postfix;
|
private String postfix;
|
||||||
private boolean local;
|
private boolean local;
|
||||||
|
private LRUCache<ActiveMQDestination,ActiveMQQueue> cache = new LRUCache<ActiveMQDestination,ActiveMQQueue>();
|
||||||
|
|
||||||
public VirtualTopicInterceptor(Destination next, String prefix, String postfix, boolean local) {
|
public VirtualTopicInterceptor(Destination next, String prefix, String postfix, boolean local) {
|
||||||
super(next);
|
super(next);
|
||||||
|
@ -51,6 +53,14 @@ public class VirtualTopicInterceptor extends DestinationFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ActiveMQDestination getQueueConsumersWildcard(ActiveMQDestination original) {
|
protected ActiveMQDestination getQueueConsumersWildcard(ActiveMQDestination original) {
|
||||||
return new ActiveMQQueue(prefix + original.getPhysicalName() + postfix);
|
ActiveMQQueue queue;
|
||||||
|
synchronized(cache){
|
||||||
|
queue = cache.get(original);
|
||||||
|
if (queue==null){
|
||||||
|
queue = new ActiveMQQueue(prefix + original.getPhysicalName() + postfix);
|
||||||
|
cache.put(original,queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return queue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
/**
|
||||||
|
* 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.plugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.apache.activemq.broker.Broker;
|
||||||
|
import org.apache.activemq.broker.BrokerFilter;
|
||||||
|
import org.apache.activemq.broker.ConnectionContext;
|
||||||
|
import org.apache.activemq.broker.region.Subscription;
|
||||||
|
import org.apache.activemq.command.ConsumerInfo;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A plugin which allows the caching of the selector from a subscription queue.
|
||||||
|
* <p/>
|
||||||
|
* This stops the build-up of unwanted messages, especially when consumers may
|
||||||
|
* disconnect from time to time when using virtual destinations.
|
||||||
|
* <p/>
|
||||||
|
* This is influenced by code snippets developed by Maciej Rakowicz
|
||||||
|
*
|
||||||
|
* @author Roelof Naude roelof(dot)naude(at)gmail.com
|
||||||
|
* @see https://issues.apache.org/activemq/browse/AMQ-3004
|
||||||
|
* @see http://mail-archives.apache.org/mod_mbox/activemq-users/201011.mbox/%3C8A013711-2613-450A-A487-379E784AF1D6@homeaway.co.uk%3E
|
||||||
|
*/
|
||||||
|
public class SubQueueSelectorCacheBroker extends BrokerFilter implements Runnable {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(SubQueueSelectorCacheBroker.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The subscription's selector cache. We cache compiled expressions keyed
|
||||||
|
* by the target destination.
|
||||||
|
*/
|
||||||
|
private ConcurrentHashMap<String, String> subSelectorCache = new ConcurrentHashMap<String, String>();
|
||||||
|
|
||||||
|
private final File persistFile;
|
||||||
|
|
||||||
|
private boolean running = true;
|
||||||
|
private Thread persistThread;
|
||||||
|
private static final long MAX_PERSIST_INTERVAL = 600000;
|
||||||
|
private static final String SELECTOR_CACHE_PERSIST_THREAD_NAME = "SelectorCachePersistThread";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public SubQueueSelectorCacheBroker(Broker next, final File persistFile) {
|
||||||
|
super(next);
|
||||||
|
this.persistFile = persistFile;
|
||||||
|
LOG.info("Using persisted selector cache from[" + persistFile + "]");
|
||||||
|
|
||||||
|
readCache();
|
||||||
|
|
||||||
|
persistThread = new Thread(this, SELECTOR_CACHE_PERSIST_THREAD_NAME);
|
||||||
|
persistThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() throws Exception {
|
||||||
|
running = false;
|
||||||
|
if (persistThread != null) {
|
||||||
|
persistThread.interrupt();
|
||||||
|
persistThread.join();
|
||||||
|
} //if
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
|
||||||
|
LOG.debug("Caching consumer selector [" + info.getSelector() + "] on a " + info.getDestination().getQualifiedName());
|
||||||
|
if (info.getSelector() != null) {
|
||||||
|
subSelectorCache.put(info.getDestination().getQualifiedName(), info.getSelector());
|
||||||
|
} //if
|
||||||
|
return super.addConsumer(context, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readCache() {
|
||||||
|
if (persistFile != null && persistFile.exists()) {
|
||||||
|
try {
|
||||||
|
FileInputStream fis = new FileInputStream(persistFile);
|
||||||
|
try {
|
||||||
|
ObjectInputStream in = new ObjectInputStream(fis);
|
||||||
|
try {
|
||||||
|
subSelectorCache = (ConcurrentHashMap<String, String>) in.readObject();
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
LOG.error("Invalid selector cache data found. Please remove file.", ex);
|
||||||
|
} finally {
|
||||||
|
in.close();
|
||||||
|
} //try
|
||||||
|
} finally {
|
||||||
|
fis.close();
|
||||||
|
} //try
|
||||||
|
} catch (IOException ex) {
|
||||||
|
LOG.error("Unable to read persisted selector cache...it will be ignored!", ex);
|
||||||
|
} //try
|
||||||
|
} //if
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persist the selector cache.
|
||||||
|
*/
|
||||||
|
private void persistCache() {
|
||||||
|
LOG.debug("Persisting selector cache....");
|
||||||
|
try {
|
||||||
|
FileOutputStream fos = new FileOutputStream(persistFile);
|
||||||
|
try {
|
||||||
|
ObjectOutputStream out = new ObjectOutputStream(fos);
|
||||||
|
try {
|
||||||
|
out.writeObject(subSelectorCache);
|
||||||
|
} finally {
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} //try
|
||||||
|
} catch (IOException ex) {
|
||||||
|
LOG.error("Unable to persist selector cache", ex);
|
||||||
|
} finally {
|
||||||
|
fos.close();
|
||||||
|
} //try
|
||||||
|
} catch (IOException ex) {
|
||||||
|
LOG.error("Unable to access file[" + persistFile + "]", ex);
|
||||||
|
} //try
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The JMS selector for the specified {@code destination}
|
||||||
|
*/
|
||||||
|
public String getSelector(final String destination) {
|
||||||
|
return subSelectorCache.get(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persist the selector cache every {@code MAX_PERSIST_INTERVAL}ms.
|
||||||
|
*
|
||||||
|
* @see java.lang.Runnable#run()
|
||||||
|
*/
|
||||||
|
public void run() {
|
||||||
|
while (running) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(MAX_PERSIST_INTERVAL);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
} //try
|
||||||
|
|
||||||
|
persistCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
* 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.plugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.apache.activemq.broker.Broker;
|
||||||
|
import org.apache.activemq.broker.BrokerPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A plugin which allows the caching of the selector from a subscription queue.
|
||||||
|
* <p/>
|
||||||
|
* This stops the build-up of unwanted messages, especially when consumers may
|
||||||
|
* disconnect from time to time when using virtual destinations.
|
||||||
|
* <p/>
|
||||||
|
* This is influenced by code snippets developed by Maciej Rakowicz
|
||||||
|
*
|
||||||
|
* @author Roelof Naude roelof(dot)naude(at)gmail.com
|
||||||
|
*@org.apache.xbean.XBean element="virtualSelectorCacheBrokerPlugin"
|
||||||
|
*/
|
||||||
|
public class SubQueueSelectorCacheBrokerPlugin implements BrokerPlugin {
|
||||||
|
|
||||||
|
|
||||||
|
private File persistFile;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Broker installPlugin(Broker broker) throws Exception {
|
||||||
|
return new SubQueueSelectorCacheBroker(broker, persistFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the location of the persistent cache
|
||||||
|
*/
|
||||||
|
public void setPersistFile(File persistFile) {
|
||||||
|
this.persistFile = persistFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getPersistFile() {
|
||||||
|
return persistFile;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,172 @@
|
||||||
|
/**
|
||||||
|
* 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.broker.virtual;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import javax.jms.Connection;
|
||||||
|
import javax.jms.Destination;
|
||||||
|
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 org.apache.activemq.EmbeddedBrokerTestSupport;
|
||||||
|
import org.apache.activemq.broker.BrokerService;
|
||||||
|
import org.apache.activemq.command.ActiveMQQueue;
|
||||||
|
import org.apache.activemq.command.ActiveMQTopic;
|
||||||
|
import org.apache.activemq.spring.ConsumerBean;
|
||||||
|
import org.apache.activemq.xbean.XBeanBrokerFactory;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for https://issues.apache.org/jira/browse/AMQ-3004
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class VirtualTopicDisconnectSelectorTest extends EmbeddedBrokerTestSupport {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(VirtualTopicDisconnectSelectorTest.class);
|
||||||
|
protected Connection connection;
|
||||||
|
protected int total = 3000;
|
||||||
|
protected String messageSelector;
|
||||||
|
|
||||||
|
public void testVirtualTopicDisconnect() throws Exception {
|
||||||
|
if (connection == null) {
|
||||||
|
connection = createConnection();
|
||||||
|
}
|
||||||
|
connection.start();
|
||||||
|
|
||||||
|
final ConsumerBean messageList = new ConsumerBean();
|
||||||
|
|
||||||
|
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
|
||||||
|
|
||||||
|
Destination producerDestination = getProducerDestination();
|
||||||
|
Destination destination = getConsumerDsetination();
|
||||||
|
|
||||||
|
LOG.info("Sending to: " + producerDestination);
|
||||||
|
LOG.info("Consuming from: " + destination );
|
||||||
|
|
||||||
|
MessageConsumer consumer = session.createConsumer(destination, messageSelector);
|
||||||
|
|
||||||
|
MessageListener listener = new MessageListener(){
|
||||||
|
public void onMessage(Message message){
|
||||||
|
messageList.onMessage(message);
|
||||||
|
try {
|
||||||
|
message.acknowledge();
|
||||||
|
} catch (JMSException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
consumer.setMessageListener(listener);
|
||||||
|
|
||||||
|
|
||||||
|
// create topic producer
|
||||||
|
MessageProducer producer = session.createProducer(producerDestination);
|
||||||
|
assertNotNull(producer);
|
||||||
|
|
||||||
|
int disconnectCount = total/3;
|
||||||
|
int reconnectCount = (total * 2)/3;
|
||||||
|
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
producer.send(createMessage(session, i));
|
||||||
|
|
||||||
|
if (i==disconnectCount){
|
||||||
|
consumer.close();
|
||||||
|
}
|
||||||
|
if (i==reconnectCount){
|
||||||
|
consumer = session.createConsumer(destination, messageSelector);
|
||||||
|
consumer.setMessageListener(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertMessagesArrived(messageList,total/2,10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Destination getConsumerDsetination() {
|
||||||
|
return new ActiveMQQueue("Consumer.VirtualTopic.TEST");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected Destination getProducerDestination() {
|
||||||
|
return new ActiveMQTopic("VirtualTopic.TEST");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
messageSelector = "odd = 'no'";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TextMessage createMessage(Session session, int i) throws JMSException {
|
||||||
|
TextMessage textMessage = session.createTextMessage("message: " + i);
|
||||||
|
if (i % 2 != 0) {
|
||||||
|
textMessage.setStringProperty("odd", "yes");
|
||||||
|
} else {
|
||||||
|
textMessage.setStringProperty("odd", "no");
|
||||||
|
}
|
||||||
|
textMessage.setIntProperty("i", i);
|
||||||
|
return textMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected void assertMessagesArrived(ConsumerBean messageList, int expected, long timeout) {
|
||||||
|
messageList.assertMessagesArrived(expected,timeout);
|
||||||
|
|
||||||
|
messageList.flushMessages();
|
||||||
|
|
||||||
|
|
||||||
|
LOG.info("validate no other messages on queues");
|
||||||
|
try {
|
||||||
|
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||||
|
|
||||||
|
Destination destination1 = getConsumerDsetination();
|
||||||
|
|
||||||
|
MessageConsumer c1 = session.createConsumer(destination1, null);
|
||||||
|
c1.setMessageListener(messageList);
|
||||||
|
|
||||||
|
|
||||||
|
LOG.info("send one simple message that should go to both consumers");
|
||||||
|
MessageProducer producer = session.createProducer(getProducerDestination());
|
||||||
|
assertNotNull(producer);
|
||||||
|
|
||||||
|
producer.send(session.createTextMessage("Last Message"));
|
||||||
|
|
||||||
|
messageList.assertMessagesArrived(1);
|
||||||
|
|
||||||
|
} catch (JMSException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail("unexpeced ex while waiting for last messages: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected String getBrokerConfigUri() {
|
||||||
|
return "org/apache/activemq/broker/virtual/disconnected-selector.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BrokerService createBroker() throws Exception {
|
||||||
|
XBeanBrokerFactory factory = new XBeanBrokerFactory();
|
||||||
|
BrokerService answer = factory.createBroker(new URI(getBrokerConfigUri()));
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -92,17 +92,21 @@ public class ConsumerBean extends Assert implements MessageListener {
|
||||||
*
|
*
|
||||||
* @param messageCount
|
* @param messageCount
|
||||||
*/
|
*/
|
||||||
public void waitForMessagesToArrive(int messageCount) {
|
|
||||||
|
public void waitForMessagesToArrive(int messageCount){
|
||||||
|
waitForMessagesToArrive(messageCount,120 * 1000);
|
||||||
|
}
|
||||||
|
public void waitForMessagesToArrive(int messageCount,long maxWaitTime) {
|
||||||
long maxRemainingMessageCount = Math.max(0, messageCount - messages.size());
|
long maxRemainingMessageCount = Math.max(0, messageCount - messages.size());
|
||||||
LOG.info("Waiting for (" + maxRemainingMessageCount + ") message(s) to arrive");
|
LOG.info("Waiting for (" + maxRemainingMessageCount + ") message(s) to arrive");
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
long maxWaitTime = start + 120 * 1000;
|
long endTime = start + maxWaitTime;
|
||||||
while (maxRemainingMessageCount > 0) {
|
while (maxRemainingMessageCount > 0) {
|
||||||
try {
|
try {
|
||||||
synchronized (messages) {
|
synchronized (messages) {
|
||||||
messages.wait(1000);
|
messages.wait(1000);
|
||||||
}
|
}
|
||||||
if (hasReceivedMessages(messageCount) || System.currentTimeMillis() > maxWaitTime) {
|
if (hasReceivedMessages(messageCount) || System.currentTimeMillis() > endTime) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
@ -123,6 +127,15 @@ public class ConsumerBean extends Assert implements MessageListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void assertMessagesArrived(int total, long maxWaitTime) {
|
||||||
|
waitForMessagesToArrive(total,maxWaitTime);
|
||||||
|
synchronized (messages) {
|
||||||
|
int count = messages.size();
|
||||||
|
|
||||||
|
assertEquals("Messages received", total, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isVerbose() {
|
public boolean isVerbose() {
|
||||||
return verbose;
|
return verbose;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- this file can only be parsed using the xbean-spring library -->
|
||||||
|
<!-- START SNIPPET: xbean -->
|
||||||
|
<beans
|
||||||
|
xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:amq="http://activemq.apache.org/schema/core"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||||
|
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">
|
||||||
|
|
||||||
|
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
|
||||||
|
|
||||||
|
<broker xmlns="http://activemq.apache.org/schema/core" persistent="false">
|
||||||
|
<destinationInterceptors>
|
||||||
|
<virtualDestinationInterceptor>
|
||||||
|
<virtualDestinations>
|
||||||
|
<virtualTopic name="VirtualTopic.>" prefix="Consumer." selectorAware="true"/>
|
||||||
|
</virtualDestinations>
|
||||||
|
</virtualDestinationInterceptor>
|
||||||
|
</destinationInterceptors>
|
||||||
|
<plugins>
|
||||||
|
<virtualSelectorCacheBrokerPlugin persistFile = "selectorcache.data"/>
|
||||||
|
</plugins>
|
||||||
|
</broker>
|
||||||
|
</beans>
|
||||||
|
<!-- END SNIPPET: xbean -->
|
Loading…
Reference in New Issue