diff options
author | Robert Godfrey <rgodfrey@apache.org> | 2008-05-11 15:22:03 +0000 |
---|---|---|
committer | Robert Godfrey <rgodfrey@apache.org> | 2008-05-11 15:22:03 +0000 |
commit | 531f130c20f73c08ffc963c1e15c7b98cea05d8f (patch) | |
tree | d30c93bde8eb6515c076568102a008702c98567a | |
parent | 1a7004fd9dab8306ec31b96d9d5e2d5a44256d98 (diff) | |
download | qpid-python-531f130c20f73c08ffc963c1e15c7b98cea05d8f.tar.gz |
Updates on the refactoring work
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/branches/broker-queue-refactor@655323 13f79535-47bb-0310-9956-ffa450edef68
98 files changed, 3603 insertions, 4649 deletions
diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java index cf1af650b7..03b02707b2 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java @@ -176,7 +176,8 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr ownerShortString = new AMQShortString(owner); } - queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), durable, ownerShortString, false, getVirtualHost()); + queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), durable, ownerShortString, false, getVirtualHost(), + null); if (queue.isDurable() && !queue.isAutoDelete()) { _messageStore.createQueue(queue); diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java index 0b8913f650..deb6ac8d94 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java +++ b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java @@ -44,6 +44,8 @@ import org.apache.qpid.server.queue.MessageHandleFactory; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; +import org.apache.qpid.server.subscription.ClientDeliveryMethod; +import org.apache.qpid.server.subscription.RecordDeliveryMethod; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.StoreContext; import org.apache.qpid.server.txn.LocalTransactionalContext; @@ -435,10 +437,8 @@ public class AMQChannel } } - synchronized (_unacknowledgedMessageMap.getLock()) - { - _unacknowledgedMessageMap.add(deliveryTag, entry); - } + _unacknowledgedMessageMap.add(deliveryTag, entry); + } private final String id = "(" + System.identityHashCode(this) + ")"; @@ -807,22 +807,7 @@ public class AMQChannel */ public void acknowledgeMessage(long deliveryTag, boolean multiple) throws AMQException { - synchronized (_unacknowledgedMessageMap.getLock()) - { - if (_log.isDebugEnabled()) - { - _log.debug("Unacked (PreAck) Size:" + _unacknowledgedMessageMap.size()); - } - - _unacknowledgedMessageMap.acknowledgeMessage(deliveryTag, multiple, _txnContext); - - if (_log.isDebugEnabled()) - { - _log.debug("Unacked (PostAck) Size:" + _unacknowledgedMessageMap.size()); - } - - } - + _unacknowledgedMessageMap.acknowledgeMessage(deliveryTag, multiple, _txnContext); } /** @@ -952,4 +937,33 @@ public class AMQChannel { return _messageStore; } + + private final ClientDeliveryMethod _clientDeliveryMethod = new ClientDeliveryMethod() + { + + public void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag) + throws AMQException + { + getProtocolSession().getProtocolOutputConverter().writeDeliver(entry.getMessage(), getChannelId(), deliveryTag, sub.getConsumerTag()); + } + }; + + public ClientDeliveryMethod getClientDeliveryMethod() + { + return _clientDeliveryMethod; + } + + private final RecordDeliveryMethod _recordDeliveryMethod = new RecordDeliveryMethod() + { + + public void recordMessageDelivery(final Subscription sub, final QueueEntry entry, final long deliveryTag) + { + addUnacknowledgedMessage(entry, deliveryTag, sub); + } + }; + + public RecordDeliveryMethod getRecordDeliveryMethod() + { + return _recordDeliveryMethod; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java index 431eeb38fc..8e5b631f96 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java +++ b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java @@ -45,8 +45,6 @@ public interface UnacknowledgedMessageMap void visit(Visitor visitor) throws AMQException; - Object getLock(); - void add(long deliveryTag, QueueEntry message); void collect(long deliveryTag, boolean multiple, Map<Long, QueueEntry> msgs); diff --git a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java index fcce6ff44e..79208ab426 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java @@ -113,11 +113,6 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } } - public Object getLock() - { - return _lock; - } - public void add(long deliveryTag, QueueEntry message) { synchronized (_lock) diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java index c9598436e0..bd3e5b1f72 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java @@ -30,6 +30,7 @@ import org.apache.commons.configuration.XMLConfiguration; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.exchange.ExchangeFactory; @@ -177,11 +178,22 @@ public class VirtualHostConfiguration boolean durable = queueConfiguration.getBoolean("durable" ,false); boolean autodelete = queueConfiguration.getBoolean("autodelete", false); String owner = queueConfiguration.getString("owner", null); + FieldTable arguments = null; + Integer priorities = queueConfiguration.getInteger("priorities", null); + if(priorities != null && priorities.intValue() > 1) + { + if(arguments == null) + { + arguments = new FieldTable(); + } + arguments.put(new AMQShortString("x-qpid-priorities"), priorities); + } + queue = AMQQueueFactory.createAMQQueueImpl(queueName, durable, owner == null ? null : new AMQShortString(owner) /* These queues will have no owner */, - autodelete /* Therefore autodelete makes no sence */, virtualHost); + autodelete /* Therefore autodelete makes no sence */, virtualHost, arguments); if (queue.isDurable()) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java index 72286cb387..8d24626b73 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java @@ -38,7 +38,6 @@ import org.apache.qpid.server.management.Managable; import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.management.ManagedObjectRegistry; import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.queue.AMQQueueImpl; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -192,9 +191,7 @@ public abstract class AbstractExchange implements Exchange, Managable { _exchangeMbean.unregister(); } - } - - abstract public Map<AMQShortString, List<AMQQueue>> getBindings(); + } public String toString() { diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java index 31c66694ec..06209c5458 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java @@ -93,6 +93,6 @@ public interface Exchange */ boolean hasBindings(); - Map<AMQShortString, List<AMQQueue>> getBindings(); + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java index 8d97e9ef25..441f88b9b6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java @@ -31,7 +31,6 @@ import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.management.MBeanConstructor; import org.apache.qpid.server.management.MBeanDescription; -import org.apache.qpid.server.queue.AMQQueueImpl; import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.virtualhost.VirtualHost; diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java index 3ade1ee7f0..8d3110ef18 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java @@ -22,6 +22,7 @@ package org.apache.qpid.server.exchange; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; +import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; @@ -31,7 +32,12 @@ import org.apache.qpid.server.management.MBeanConstructor; import org.apache.qpid.server.management.MBeanDescription; import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQMessage; import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.exchange.topic.TopicParser; +import org.apache.qpid.server.exchange.topic.TopicMatcherResult; +import org.apache.qpid.server.filter.MessageFilter; +import org.apache.qpid.server.filter.JMSSelectorFilter; import javax.management.JMException; import javax.management.MBeanException; @@ -43,6 +49,9 @@ import javax.management.openmbean.TabularDataSupport; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.atomic.AtomicInteger; +import java.lang.ref.WeakReference; public class TopicExchange extends AbstractExchange { @@ -80,22 +89,204 @@ public class TopicExchange extends AbstractExchange private static final Logger _logger = Logger.getLogger(TopicExchange.class); +/* private final ConcurrentHashMap<AMQShortString, List<AMQQueue>> _bindingKey2queues = new ConcurrentHashMap<AMQShortString, List<AMQQueue>>(); private final ConcurrentHashMap<AMQShortString, List<AMQQueue>> _simpleBindingKey2queues = new ConcurrentHashMap<AMQShortString, List<AMQQueue>>(); private final ConcurrentHashMap<AMQShortString, List<AMQQueue>> _wildCardBindingKey2queues = new ConcurrentHashMap<AMQShortString, List<AMQQueue>>(); +*/ // private ConcurrentHashMap<AMQShortString, AMQQueue> _routingKey2queue = new ConcurrentHashMap<AMQShortString, AMQQueue>(); private static final byte TOPIC_SEPARATOR = (byte)'.'; private static final AMQShortString TOPIC_SEPARATOR_AS_SHORTSTRING = new AMQShortString("."); private static final AMQShortString AMQP_STAR_TOKEN = new AMQShortString("*"); private static final AMQShortString AMQP_HASH_TOKEN = new AMQShortString("#"); - private ConcurrentHashMap<AMQShortString, AMQShortString[]> _bindingKey2Tokenized = - new ConcurrentHashMap<AMQShortString, AMQShortString[]>(); + private static final byte HASH_BYTE = (byte)'#'; private static final byte STAR_BYTE = (byte)'*'; + private final TopicParser _parser = new TopicParser(); + + private final Map<AMQShortString, TopicExchangeResult> _topicExchangeResults = + new ConcurrentHashMap<AMQShortString, TopicExchangeResult>(); + + private final Map<Binding, FieldTable> _bindings = new HashMap<Binding, FieldTable>(); + + private final Map<String, WeakReference<JMSSelectorFilter<RuntimeException>>> _selectorCache = new WeakHashMap<String, WeakReference<JMSSelectorFilter<RuntimeException>>>(); + + public static class Binding + { + private final AMQShortString _bindingKey; + private final AMQQueue _queue; + + public Binding(AMQShortString bindingKey, AMQQueue queue) + { + _bindingKey = bindingKey; + _queue = queue; + } + + public AMQShortString getBindingKey() + { + return _bindingKey; + } + + public AMQQueue getQueue() + { + return _queue; + } + + public int hashCode() + { + return (_bindingKey == null ? 1 : _bindingKey.hashCode())*31 + _queue.hashCode(); + } + + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o instanceof Binding) + { + Binding other = (Binding) o; + return (_queue == other._queue) + && ((_bindingKey == null) ? other._bindingKey == null : _bindingKey.equals(other._bindingKey)); + } + return false; + } + } + + + + private final class TopicExchangeResult implements TopicMatcherResult + { + private final Map<AMQQueue, Integer> _unfilteredQueues = new ConcurrentHashMap<AMQQueue, Integer>(); + private final ConcurrentHashMap<AMQQueue, Map<MessageFilter<RuntimeException>,Integer>> _filteredQueues = new ConcurrentHashMap<AMQQueue, Map<MessageFilter<RuntimeException>, Integer>>(); + + public void addUnfilteredQueue(AMQQueue queue) + { + Integer instances = _unfilteredQueues.get(queue); + if(instances == null) + { + _unfilteredQueues.put(queue, 1); + } + else + { + _unfilteredQueues.put(queue, instances + 1); + } + } + + public void removeUnfilteredQueue(AMQQueue queue) + { + Integer instances = _unfilteredQueues.get(queue); + if(instances == 1) + { + _unfilteredQueues.remove(queue); + } + else + { + _unfilteredQueues.put(queue,instances - 1); + } + + } + + + public void addFilteredQueue(AMQQueue queue, MessageFilter<RuntimeException> filter) + { + Map<MessageFilter<RuntimeException>,Integer> filters = _filteredQueues.get(queue); + if(filters == null) + { + filters = new ConcurrentHashMap<MessageFilter<RuntimeException>,Integer>(); + _filteredQueues.put(queue, filters); + } + Integer instances = filters.get(filter); + if(instances == null) + { + filters.put(filter,1); + } + else + { + filters.put(filter, instances + 1); + } + + } + + public void removeFilteredQueue(AMQQueue queue, MessageFilter<RuntimeException> filter) + { + Map<MessageFilter<RuntimeException>,Integer> filters = _filteredQueues.get(queue); + if(filters != null) + { + Integer instances = filters.get(filter); + if(instances == 1) + { + filters.remove(filter); + if(filters.isEmpty()) + { + _filteredQueues.remove(queue); + } + } + else if(instances != null) + { + filters.put(filter, instances - 1); + } + + } + + } + + public void replaceQueueFilter(AMQQueue queue, + MessageFilter<RuntimeException> oldFilter, + MessageFilter<RuntimeException> newFilter) + { + Map<MessageFilter<RuntimeException>,Integer> filters = _filteredQueues.get(queue); + Map<MessageFilter<RuntimeException>,Integer> newFilters = new ConcurrentHashMap<MessageFilter<RuntimeException>,Integer>(filters); + Integer oldFilterInstances = filters.get(oldFilter); + if(oldFilterInstances == 1) + { + newFilters.remove(oldFilter); + } + else + { + newFilters.put(oldFilter, oldFilterInstances-1); + } + Integer newFilterInstances = filters.get(newFilter); + if(newFilterInstances == null) + { + newFilters.put(newFilter, 1); + } + else + { + newFilters.put(newFilter, newFilterInstances+1); + } + _filteredQueues.put(queue,newFilters); + } + + public Set<AMQQueue> processMessage(IncomingMessage msg, Set<AMQQueue> queues) + { + queues.addAll(_unfilteredQueues.keySet()); + if(!_filteredQueues.isEmpty()) + { + for(Map.Entry<AMQQueue, Map<MessageFilter<RuntimeException>, Integer>> entry : _filteredQueues.entrySet()) + { + if(!queues.contains(entry.getKey())) + { + for(MessageFilter<RuntimeException> filter : entry.getValue().keySet()) + { + if(filter.matches(msg)) + { + queues.add(entry.getKey()); + } + } + } + } + } + return queues; + } + + } + + /** TopicExchangeMBean class implements the management interface for the Topic exchanges. */ @MBeanDescription("Management Bean for Topic Exchange") private final class TopicExchangeMBean extends ExchangeMBean @@ -112,20 +303,24 @@ public class TopicExchange extends AbstractExchange public TabularData bindings() throws OpenDataException { _bindingList = new TabularDataSupport(_bindinglistDataType); - for (Map.Entry<AMQShortString, List<AMQQueue>> entry : _bindingKey2queues.entrySet()) + Map<String, List<String>> bindingData = new HashMap<String, List<String>>(); + for (Binding binding : _bindings.keySet()) { - AMQShortString key = entry.getKey(); - List<String> queueList = new ArrayList<String>(); - - List<AMQQueue> queues = getMatchedQueues(key); - for (AMQQueue q : queues) + String key = binding.getBindingKey().toString(); + List<String> queueNames = bindingData.get(key); + if(queueNames == null) { - queueList.add(q.getName().toString()); + queueNames = new ArrayList<String>(); + bindingData.put(key, queueNames); } + queueNames.add(binding.getQueue().getName().toString()); - Object[] bindingItemValues = {key.toString(), queueList.toArray(new String[queueList.size()])}; - CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues); - _bindingList.put(bindingData); + } + for(Map.Entry<String, List<String>> entry : bindingData.entrySet()) + { + Object[] bindingItemValues = {entry.getKey(), entry.getValue().toArray(new String[entry.getValue().size()]) }; + CompositeData bindingCompositeData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues); + _bindingList.put(bindingCompositeData); } return _bindingList; @@ -163,73 +358,106 @@ public class TopicExchange extends AbstractExchange _logger.debug("Registering queue " + queue.getName() + " with routing key " + rKey); - // we need to use putIfAbsent, which is an atomic operation, to avoid a race condition - List<AMQQueue> queueList = _bindingKey2queues.putIfAbsent(rKey, new CopyOnWriteArrayList<AMQQueue>()); - - + AMQShortString routingKey; - - - - // if we got null back, no previous value was associated with the specified routing key hence - // we need to read back the new value just put into the map - if (queueList == null) + if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE)) { - queueList = _bindingKey2queues.get(rKey); + routingKey = normalize(rKey); + } + else + { + routingKey = rKey; } + Binding binding = new Binding(rKey, queue); - - if (!queueList.contains(queue)) + if(_bindings.containsKey(binding)) { - queueList.add(queue); + FieldTable oldArgs = _bindings.get(binding); + TopicExchangeResult result = _topicExchangeResults.get(routingKey); - - if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE)) + if(argumentsContainSelector(args)) { - AMQShortString routingKey = normalize(rKey); - List<AMQQueue> queueList2 = _wildCardBindingKey2queues.putIfAbsent(routingKey, new CopyOnWriteArrayList<AMQQueue>()); - - if(queueList2 == null) + if(argumentsContainSelector(oldArgs)) { - queueList2 = _wildCardBindingKey2queues.get(routingKey); - AMQShortStringTokenizer keyTok = routingKey.tokenize(TOPIC_SEPARATOR); - - ArrayList<AMQShortString> keyTokList = new ArrayList<AMQShortString>(keyTok.countTokens()); - - while (keyTok.hasMoreTokens()) - { - keyTokList.add(keyTok.nextToken()); - } - - _bindingKey2Tokenized.put(routingKey, keyTokList.toArray(new AMQShortString[keyTokList.size()])); + result.replaceQueueFilter(queue,createSelectorFilter(oldArgs), createSelectorFilter(args)); + } + else + { + result.addFilteredQueue(queue,createSelectorFilter(args)); + result.removeUnfilteredQueue(queue); } - queueList2.add(queue); - } else { - List<AMQQueue> queueList2 = _simpleBindingKey2queues.putIfAbsent(rKey, new CopyOnWriteArrayList<AMQQueue>()); - if(queueList2 == null) + if(argumentsContainSelector(oldArgs)) + { + result.addUnfilteredQueue(queue); + result.removeFilteredQueue(queue, createSelectorFilter(oldArgs)); + } + else { - queueList2 = _simpleBindingKey2queues.get(rKey); + // TODO - fix control flow + return; } - queueList2.add(queue); + } + } + else + { + + TopicExchangeResult result = _topicExchangeResults.get(routingKey); + if(result == null) + { + result = new TopicExchangeResult(); + if(argumentsContainSelector(args)) + { + result.addFilteredQueue(queue, createSelectorFilter(args)); + } + else + { + result.addUnfilteredQueue(queue); + } + _parser.addBinding(routingKey, result); + _topicExchangeResults.put(routingKey,result); } + else + { + if(argumentsContainSelector(args)) + { + result.addFilteredQueue(queue, createSelectorFilter(args)); + } + else + { + result.addUnfilteredQueue(queue); + } + } + _bindings.put(binding, args); + } + } + private JMSSelectorFilter<RuntimeException> createSelectorFilter(final FieldTable args) + throws AMQException + { - } - else if (_logger.isDebugEnabled()) + final String selectorString = args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()); + WeakReference<JMSSelectorFilter<RuntimeException>> selectorRef = _selectorCache.get(selectorString); + JMSSelectorFilter selector = null; + + if(selectorRef == null || (selector = selectorRef.get())==null) { - _logger.debug("Queue " + queue + " is already registered with routing key " + rKey); + selector = new JMSSelectorFilter<RuntimeException>(selectorString); + _selectorCache.put(selectorString, new WeakReference<JMSSelectorFilter<RuntimeException>>(selector)); } + return selector; + } - - + private static boolean argumentsContainSelector(final FieldTable args) + { + return args != null && args.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue()) && args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()).trim().length() != 0; } private AMQShortString normalize(AMQShortString routingKey) @@ -279,16 +507,6 @@ public class TopicExchange extends AbstractExchange AMQShortString normalizedString = AMQShortString.join(subscriptionList, TOPIC_SEPARATOR_AS_SHORTSTRING); -/* - StringBuilder sb = new StringBuilder(); - for (AMQShortString s : subscriptionList) - { - sb.append(s); - sb.append(TOPIC_SEPARATOR); - } - - sb.deleteCharAt(sb.length() - 1); -*/ return normalizedString; } @@ -298,11 +516,11 @@ public class TopicExchange extends AbstractExchange final AMQShortString routingKey = payload.getRoutingKey(); - List<AMQQueue> queues = getMatchedQueues(routingKey); + Collection<AMQQueue> queues = getMatchedQueues(payload, routingKey); if(queues == null || queues.isEmpty()) { - _logger.info("Message routing key: " + payload.getRoutingKey() + " No routes - " + _bindingKey2queues); + _logger.info("Message routing key: " + payload.getRoutingKey() + " No routes."); } payload.enqueue(queues); @@ -316,23 +534,29 @@ public class TopicExchange extends AbstractExchange public boolean isBound(AMQShortString routingKey, AMQQueue queue) { - List<AMQQueue> queues = _bindingKey2queues.get(normalize(routingKey)); + Binding binding = new Binding(routingKey, queue); - return (queues != null) && queues.contains(queue); + return _bindings.containsKey(binding); } public boolean isBound(AMQShortString routingKey) { - List<AMQQueue> queues = _bindingKey2queues.get(normalize(routingKey)); + for(Binding b : _bindings.keySet()) + { + if(b.getBindingKey().equals(routingKey)) + { + return true; + } + } - return (queues != null) && !queues.isEmpty(); + return false; } public boolean isBound(AMQQueue queue) { - for (List<AMQQueue> queues : _bindingKey2queues.values()) + for(Binding b : _bindings.keySet()) { - if (queues.contains(queue)) + if(b.getQueue().equals(queue)) { return true; } @@ -343,7 +567,7 @@ public class TopicExchange extends AbstractExchange public boolean hasBindings() { - return !_bindingKey2queues.isEmpty(); + return !_bindings.isEmpty(); } public synchronized void deregisterQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException @@ -351,52 +575,27 @@ public class TopicExchange extends AbstractExchange assert queue != null; assert rKey != null; - List<AMQQueue> queues = _bindingKey2queues.get(rKey); - if (queues == null) - { - throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() - + " with routing key " + rKey + ". No queue was registered with that _routing key"); + Binding binding = new Binding(rKey, queue); - } - boolean removedQ = queues.remove(queue); - if (!removedQ) + if (!_bindings.containsKey(binding)) { - throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() - + " with routing key " + rKey); + throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue.getName() + " was not registered with exchange " + this.getName() + + " with routing key " + rKey + "."); } - - if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE)) + FieldTable bindingArgs = _bindings.remove(binding); + AMQShortString bindingKey = normalize(rKey); + TopicExchangeResult result = _topicExchangeResults.get(bindingKey); + if(argumentsContainSelector(bindingArgs)) { - AMQShortString bindingKey = normalize(rKey); - List<AMQQueue> queues2 = _wildCardBindingKey2queues.get(bindingKey); - queues2.remove(queue); - if(queues2.isEmpty()) - { - _wildCardBindingKey2queues.remove(bindingKey); - _bindingKey2Tokenized.remove(bindingKey); - } - + result.removeFilteredQueue(queue, createSelectorFilter(bindingArgs)); } else { - List<AMQQueue> queues2 = _simpleBindingKey2queues.get(rKey); - queues2.remove(queue); - if(queues2.isEmpty()) - { - _simpleBindingKey2queues.remove(rKey); - } - + result.removeUnfilteredQueue(queue); } - - - - if (queues.isEmpty()) - { - _bindingKey2queues.remove(rKey); - } } protected ExchangeMBean createMBean() throws AMQException @@ -412,172 +611,25 @@ public class TopicExchange extends AbstractExchange } } - public Map<AMQShortString, List<AMQQueue>> getBindings() + private Collection<AMQQueue> getMatchedQueues(IncomingMessage message, AMQShortString routingKey) { - return _bindingKey2queues; - } - - private List<AMQQueue> getMatchedQueues(AMQShortString routingKey) - { - - List<AMQQueue> list = null; - if(!_wildCardBindingKey2queues.isEmpty()) + Collection<TopicMatcherResult> results = _parser.parse(routingKey); + if(results.isEmpty()) { - - - AMQShortStringTokenizer routingTokens = routingKey.tokenize(TOPIC_SEPARATOR); - - final int routingTokensCount = routingTokens.countTokens(); - - - AMQShortString[] routingkeyTokens = new AMQShortString[routingTokensCount]; - - if(routingTokensCount == 1) - { - routingkeyTokens[0] =routingKey; - } - else - { - - - int token = 0; - while (routingTokens.hasMoreTokens()) - { - - AMQShortString next = routingTokens.nextToken(); - /* if (next.equals(AMQP_HASH) && routingkeyTokens.get(routingkeyTokens.size() - 1).equals(AMQP_HASH)) - { - continue; - } - */ - - routingkeyTokens[token++] = next; - } - } - - _logger.info("Routing key tokens: " + Arrays.asList(routingkeyTokens)); - - for (AMQShortString bindingKey : _wildCardBindingKey2queues.keySet()) - { - - AMQShortString[] bindingKeyTokens = _bindingKey2Tokenized.get(bindingKey); - - - boolean matching = true; - boolean done = false; - - int depthPlusRoutingSkip = 0; - int depthPlusQueueSkip = 0; - - final int bindingKeyTokensCount = bindingKeyTokens.length; - - while (matching && !done) - { - - if ((bindingKeyTokensCount == depthPlusQueueSkip) || (routingTokensCount == depthPlusRoutingSkip)) - { - done = true; - - // if it was the routing key that ran out of digits - if (routingTokensCount == depthPlusRoutingSkip) - { - if (bindingKeyTokensCount > depthPlusQueueSkip) - { // a hash and it is the last entry - matching = - bindingKeyTokens[depthPlusQueueSkip].equals(AMQP_HASH_TOKEN) - && (bindingKeyTokensCount == (depthPlusQueueSkip + 1)); - } - } - else if (routingTokensCount > depthPlusRoutingSkip) - { - // There is still more routing key to check - matching = false; - } - - continue; - } - - // if the values on the two topics don't match - if (!bindingKeyTokens[depthPlusQueueSkip].equals(routingkeyTokens[depthPlusRoutingSkip])) - { - if (bindingKeyTokens[depthPlusQueueSkip].equals(AMQP_STAR_TOKEN)) - { - depthPlusQueueSkip++; - depthPlusRoutingSkip++; - - continue; - } - else if (bindingKeyTokens[depthPlusQueueSkip].equals(AMQP_HASH_TOKEN)) - { - // Is this a # at the end - if (bindingKeyTokensCount == (depthPlusQueueSkip + 1)) - { - done = true; - - continue; - } - - // otherwise # in the middle - while (routingTokensCount > depthPlusRoutingSkip) - { - if (routingkeyTokens[depthPlusRoutingSkip].equals(bindingKeyTokens[depthPlusQueueSkip + 1])) - { - depthPlusQueueSkip += 2; - depthPlusRoutingSkip++; - - break; - } - - depthPlusRoutingSkip++; - } - - continue; - } - - matching = false; - } - - depthPlusQueueSkip++; - depthPlusRoutingSkip++; - } - - if (matching) - { - if(list == null) - { - list = new ArrayList<AMQQueue>(_wildCardBindingKey2queues.get(bindingKey)); - } - else - { - list.addAll(_wildCardBindingKey2queues.get(bindingKey)); - } - } - } - + return Collections.EMPTY_SET; } - if(!_simpleBindingKey2queues.isEmpty()) + else { - List<AMQQueue> queues = _simpleBindingKey2queues.get(routingKey); - if(list == null) - { - if(queues == null) - { - list = Collections.EMPTY_LIST; - } - else - { - list = new ArrayList<AMQQueue>(queues); - } - } - else if(queues != null) + Set<AMQQueue> queues = new HashSet<AMQQueue>(); + for(TopicMatcherResult result : results) { - list.addAll(queues); - } + ((TopicExchangeResult)result).processMessage(message, queues); + } + return queues; } - return list; } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java index 9da1852f8e..36076cf75b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java @@ -4,6 +4,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.AMQShortStringTokenizer; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; /* * @@ -27,6 +28,10 @@ import java.util.*; */ public class TopicMatcherDFAState { + private static final AtomicInteger stateId = new AtomicInteger(); + + private final int _id = stateId.incrementAndGet(); + private final Collection<TopicMatcherResult> _results; private final Map<TopicWord, TopicMatcherDFAState> _nextStateMap; private static final byte TOPIC_DELIMITTER = (byte)'.'; @@ -233,4 +238,58 @@ public class TopicMatcherDFAState } + public String toString() + { + StringBuilder transitions = new StringBuilder(); + for(Map.Entry<TopicWord, TopicMatcherDFAState> entry : _nextStateMap.entrySet()) + { + transitions.append("[ "); + transitions.append(entry.getKey()); + transitions.append("\t ->\t "); + transitions.append(entry.getValue()._id); + transitions.append(" ]\n"); + } + + + return "[ State " + _id + " ]\n" + transitions + "\n"; + + } + + public String reachableStates() + { + StringBuilder result = new StringBuilder("Start state: " + _id + "\n"); + + SortedSet<TopicMatcherDFAState> reachableStates = + new TreeSet<TopicMatcherDFAState>(new Comparator<TopicMatcherDFAState>() + { + public int compare(final TopicMatcherDFAState o1, final TopicMatcherDFAState o2) + { + return o1._id - o2._id; + } + }); + reachableStates.add(this); + + int count; + + do + { + count = reachableStates.size(); + Collection<TopicMatcherDFAState> originalStates = new ArrayList<TopicMatcherDFAState>(reachableStates); + for(TopicMatcherDFAState state : originalStates) + { + reachableStates.addAll(state._nextStateMap.values()); + } + } + while(reachableStates.size() != count); + + + + for(TopicMatcherDFAState state : reachableStates) + { + result.append(state.toString()); + } + + return result.toString(); + } + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java index 7e3d1819f4..71d30adfac 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java @@ -20,6 +20,6 @@ package org.apache.qpid.server.exchange.topic; * under the License. * */ -public class TopicMatcherResult +public interface TopicMatcherResult { } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java index f564bbb565..3e9facf412 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java @@ -4,6 +4,8 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.AMQShortStringTokenizer; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.io.IOException; /* * @@ -30,6 +32,7 @@ public class TopicParser private static final byte TOPIC_DELIMITER = (byte)'.'; private final TopicWordDictionary _dictionary = new TopicWordDictionary(); + private final AtomicReference<TopicMatcherDFAState> _stateMachine = new AtomicReference<TopicMatcherDFAState>(); private static class Position { @@ -37,6 +40,7 @@ public class TopicParser private final boolean _selfTransition; private final int _position; private final boolean _endState; + private boolean _followedByAnyLoop; public Position(final int position, final TopicWord word, final boolean selfTransition, final boolean endState) @@ -59,6 +63,43 @@ public class TopicParser } + public void addBinding(AMQShortString bindingKey, TopicMatcherResult result) + { + + TopicMatcherDFAState startingStateMachine; + TopicMatcherDFAState newStateMachine; + + do + { + startingStateMachine = _stateMachine.get(); + if(startingStateMachine == null) + { + newStateMachine = createStateMachine(bindingKey, result); + } + else + { + newStateMachine = startingStateMachine.mergeStateMachines(createStateMachine(bindingKey, result)); + } + + } + while(!_stateMachine.compareAndSet(startingStateMachine,newStateMachine)); + + } + + public Collection<TopicMatcherResult> parse(AMQShortString routingKey) + { + TopicMatcherDFAState stateMachine = _stateMachine.get(); + if(stateMachine == null) + { + return Collections.EMPTY_SET; + } + else + { + return stateMachine.parse(_dictionary,routingKey); + } + } + + TopicMatcherDFAState createStateMachine(AMQShortString bindingKey, TopicMatcherResult result) { List<TopicWord> wordList = createTopicWordList(bindingKey); @@ -108,7 +149,6 @@ public class TopicParser } - int pos = 0; int wordPos = 0; @@ -131,6 +171,31 @@ public class TopicParser } + + for(int p = 0; p<positionCount; p++) + { + boolean followedByWildcards = true; + + int n = p; + while(followedByWildcards && n<(positionCount+1)) + { + + if(positions[n]._selfTransition) + { + break; + } + else if(positions[n]._word!=TopicWord.ANY_WORD) + { + followedByWildcards = false; + } + n++; + } + + + positions[p]._followedByAnyLoop = followedByWildcards && (n!= positionCount+1); + } + + // from each position you transition to a set of other positions. // we approach this by examining steps of increasing length - so we // look how far we can go from the start position in 1 word, 2 words, etc... @@ -258,6 +323,32 @@ public class TopicParser { dest.setValue(Collections.singleton(loopingTerminal)); } + else + { + Position anyLoop = null; + for(Position destPos : dest.getValue()) + { + if(destPos._followedByAnyLoop) + { + if(anyLoop == null || anyLoop._position<destPos._position) + { + anyLoop = destPos; + } + } + } + if(anyLoop != null) + { + Collection<Position> removals = new ArrayList<Position>(); + for(Position destPos : dest.getValue()) + { + if(destPos._position < anyLoop._position) + { + removals.add(destPos); + } + } + dest.getValue().removeAll(removals); + } + } SimpleState stateForEntry = stateMap.get(dest.getValue()); if(stateForEntry == null) @@ -332,8 +423,65 @@ public class TopicParser public static void main(String[] args) { + + printMatches("#.b.*.*.*.*.*.h.#.j.*.*.*.*.*.*.q.#.r.*.*.*.*.*.*.*.*","a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z"); + printMatches(new String[]{ + "#.a.#", + "#.b.#", + "#.c.#", + "#.d.#", + "#.e.#", + "#.f.#", + "#.g.#", + "#.h.#", + "#.i.#", + "#.j.#", + "#.k.#", + "#.l.#", + "#.m.#", + "#.n.#", + "#.o.#", + "#.p.#", + "#.q.#" + + }, "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z"); +/* + printMatches(new String[]{ + "#.a.#", + "#.b.#", + "#.c.#", + "#.d.#", + "#.e.#", + "#.f.#", + "#.g.#", + "#.h.#", + "#.i.#", + "#.j.#", + "#.k.#", + "#.l.#", + "#.m.#", + "#.n.#", + "#.o.#", + "#.p.#", + "#.q.#", + "#.r.#", + "#.s.#", + "#.t.#", + "#.u.#", + "#.v.#", + "#.w.#", + "#.x.#", + "#.y.#", + "#.z.#" + + + },"a.b"); + + printMatches("#.b.*.*.*.*.*.h.#.j.*.*.*.*.*.p.#.r.*.*.*.*.*","a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z"); + printMatches("#.b.*.*.*.*.*.h.#.j.*.*.*.*.*.p.#.r.*.*.*.*.*.*.*.*","a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z"); printMatches("a.#.b.#","a.b.b.b.b.b.b.b.c"); +*/ printMatches("",""); printMatches("a","a"); @@ -394,12 +542,19 @@ public class TopicParser TopicParser parser = new TopicParser(); + long start = System.currentTimeMillis(); for(int i = 0; i < bindingKeys.length; i++) { - TopicMatcherResult r = new TopicMatcherResult(); + System.out.println((System.currentTimeMillis() - start) + ":\t" + bindingKeys[i]); + TopicMatcherResult r = new TopicMatcherResult(){}; resultMap.put(r, bindingKeys[i]); AMQShortString bindingKeyShortString = new AMQShortString(bindingKeys[i]); + System.err.println("====================================================="); + System.err.println("Adding binding key: " + bindingKeyShortString); + System.err.println("-----------------------------------------------------"); + + if(i==0) { sm = parser.createStateMachine(bindingKeyShortString, r); @@ -408,6 +563,16 @@ public class TopicParser { sm = sm.mergeStateMachines(parser.createStateMachine(bindingKeyShortString, r)); } + System.err.println(sm.reachableStates()); + System.err.println("====================================================="); + try + { + System.in.read(); + } + catch (IOException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } } AMQShortString routingKeyShortString = new AMQShortString(routingKey); @@ -438,7 +603,7 @@ public class TopicParser AMQShortString routingKeyShortString = new AMQShortString(routingKey); TopicParser parser = new TopicParser(); - final TopicMatcherResult result = new TopicMatcherResult(); + final TopicMatcherResult result = new TopicMatcherResult(){}; TopicMatcherDFAState sm = parser.createStateMachine(bindingKeyShortString, result); return !sm.parse(parser._dictionary,routingKeyShortString).isEmpty(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java index e86a726b4f..f14d70f8a1 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java @@ -28,7 +28,27 @@ import java.util.concurrent.ConcurrentHashMap; */ public final class TopicWord { - public static final TopicWord ANY_WORD = new TopicWord(); - public static final TopicWord WILDCARD_WORD = new TopicWord(); + public static final TopicWord ANY_WORD = new TopicWord("*"); + public static final TopicWord WILDCARD_WORD = new TopicWord("#"); + private String _word; + public TopicWord() + { + + } + + public TopicWord(String s) + { + _word = s; + } + + public TopicWord(final AMQShortString name) + { + _word = name.toString(); + } + + public String toString() + { + return _word; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java index f69616cc85..65a0cd3107 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java @@ -42,7 +42,7 @@ public class TopicWordDictionary public TopicWord getOrCreateWord(AMQShortString name) { - TopicWord word = _dictionary.putIfAbsent(name, new TopicWord()); + TopicWord word = _dictionary.putIfAbsent(name, new TopicWord(name)); if(word == null) { word = _dictionary.get(name); diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java index fb5220f4da..a964bce306 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java @@ -21,12 +21,12 @@ package org.apache.qpid.server.filter; // import org.apache.qpid.AMQException; -import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; /** * An expression which performs an operation on two expression values */ -public abstract class ArithmeticExpression extends BinaryExpression +public abstract class ArithmeticExpression<E extends Exception> extends BinaryExpression<E> { protected static final int INTEGER = 1; @@ -248,7 +248,7 @@ public abstract class ArithmeticExpression extends BinaryExpression } } - public Object evaluate(AMQMessage message) throws AMQException + public Object evaluate(Filterable<E> message) throws E { Object lvalue = left.evaluate(message); if (lvalue == null) diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java index 024257bea9..7308de80d6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java @@ -23,23 +23,23 @@ package org.apache.qpid.server.filter; /** * An expression which performs an operation on two expression values. */ -public abstract class BinaryExpression implements Expression +public abstract class BinaryExpression<E extends Exception> implements Expression<E> { - protected Expression left; - protected Expression right; + protected Expression<E> left; + protected Expression<E> right; - public BinaryExpression(Expression left, Expression right) + public BinaryExpression(Expression<E> left, Expression<E> right) { this.left = left; this.right = right; } - public Expression getLeft() + public Expression<E> getLeft() { return left; } - public Expression getRight() + public Expression<E> getRight() { return right; } @@ -90,7 +90,7 @@ public abstract class BinaryExpression implements Expression /** * @param expression */ - public void setRight(Expression expression) + public void setRight(Expression<E> expression) { right = expression; } @@ -98,7 +98,7 @@ public abstract class BinaryExpression implements Expression /** * @param expression */ - public void setLeft(Expression expression) + public void setLeft(Expression<E> expression) { left = expression; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java index e28ff79820..9beb9798d0 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java @@ -22,19 +22,20 @@ package org.apache.qpid.server.filter; import org.apache.qpid.AMQException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; /** * A BooleanExpression is an expression that always * produces a Boolean result. */ -public interface BooleanExpression extends Expression +public interface BooleanExpression<E extends Exception> extends Expression<E> { /** * @param message * @return true if the expression evaluates to Boolean.TRUE. - * @throws AMQException + * @throws E */ - public boolean matches(AMQMessage message) throws AMQException; + public boolean matches(Filterable<E> message) throws E; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java index 72a9ef7969..921005c462 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java @@ -29,19 +29,20 @@ import java.util.regex.Pattern; import org.apache.qpid.AMQException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; /** * A filter performing a comparison of two objects */ -public abstract class ComparisonExpression extends BinaryExpression implements BooleanExpression +public abstract class ComparisonExpression<E extends Exception> extends BinaryExpression<E> implements BooleanExpression<E> { - public static BooleanExpression createBetween(Expression value, Expression left, Expression right) + public static<E extends Exception> BooleanExpression<E> createBetween(Expression<E> value, Expression left, Expression<E> right) { return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right)); } - public static BooleanExpression createNotBetween(Expression value, Expression left, Expression right) + public static<E extends Exception> BooleanExpression<E> createNotBetween(Expression<E> value, Expression<E> left, Expression<E> right) { return LogicExpression.createOR(createLessThan(value, left), createGreaterThan(value, right)); } @@ -72,7 +73,7 @@ public abstract class ComparisonExpression extends BinaryExpression implements B REGEXP_CONTROL_CHARS.add(new Character('!')); } - static class LikeExpression extends UnaryExpression implements BooleanExpression + static class LikeExpression<E extends Exception> extends UnaryExpression<E> implements BooleanExpression<E> { Pattern likePattern; @@ -80,7 +81,7 @@ public abstract class ComparisonExpression extends BinaryExpression implements B /** * @param right */ - public LikeExpression(Expression right, String like, int escape) + public LikeExpression(Expression<E> right, String like, int escape) { super(right); @@ -137,7 +138,7 @@ public abstract class ComparisonExpression extends BinaryExpression implements B /** * org.apache.activemq.filter.Expression#evaluate(MessageEvaluationContext) */ - public Object evaluate(AMQMessage message) throws AMQException + public Object evaluate(Filterable<E> message) throws E { Object rv = this.getRight().evaluate(message); @@ -157,7 +158,7 @@ public abstract class ComparisonExpression extends BinaryExpression implements B return likePattern.matcher((String) rv).matches() ? Boolean.TRUE : Boolean.FALSE; } - public boolean matches(AMQMessage message) throws AMQException + public boolean matches(Filterable<E> message) throws E { Object object = evaluate(message); @@ -235,45 +236,9 @@ public abstract class ComparisonExpression extends BinaryExpression implements B return doCreateEqual(left, right); } - private static BooleanExpression doCreateEqual(Expression left, Expression right) + private static<E extends Exception> BooleanExpression<E> doCreateEqual(Expression<E> left, Expression<E> right) { - return new ComparisonExpression(left, right) - { - - public Object evaluate(AMQMessage message) throws AMQException - { - Object lv = left.evaluate(message); - Object rv = right.evaluate(message); - - // Iff one of the values is null - if ((lv == null) ^ (rv == null)) - { - return Boolean.FALSE; - } - - if ((lv == rv) || lv.equals(rv)) - { - return Boolean.TRUE; - } - - if ((lv instanceof Comparable) && (rv instanceof Comparable)) - { - return compare((Comparable) lv, (Comparable) rv); - } - - return Boolean.FALSE; - } - - protected boolean asBoolean(int answer) - { - return answer == 0; - } - - public String getExpressionSymbol() - { - return "="; - } - }; + return new EqualExpression(left, right); } public static BooleanExpression createGreaterThan(final Expression left, final Expression right) @@ -423,7 +388,7 @@ public abstract class ComparisonExpression extends BinaryExpression implements B super(left, right); } - public Object evaluate(AMQMessage message) throws AMQException + public Object evaluate(Filterable<E> message) throws E { Comparable lv = (Comparable) left.evaluate(message); if (lv == null) @@ -585,11 +550,52 @@ public abstract class ComparisonExpression extends BinaryExpression implements B protected abstract boolean asBoolean(int answer); - public boolean matches(AMQMessage message) throws AMQException + public boolean matches(Filterable<E> message) throws E { Object object = evaluate(message); return (object != null) && (object == Boolean.TRUE); } + private static class EqualExpression<E extends Exception> extends ComparisonExpression<E> + { + public EqualExpression(final Expression<E> left, final Expression<E> right) + { + super(left, right); + } + + public Object evaluate(Filterable<E> message) throws E + { + Object lv = left.evaluate(message); + Object rv = right.evaluate(message); + + // Iff one of the values is null + if ((lv == null) ^ (rv == null)) + { + return Boolean.FALSE; + } + + if ((lv == rv) || lv.equals(rv)) + { + return Boolean.TRUE; + } + + if ((lv instanceof Comparable) && (rv instanceof Comparable)) + { + return compare((Comparable) lv, (Comparable) rv); + } + + return Boolean.FALSE; + } + + protected boolean asBoolean(int answer) + { + return answer == 0; + } + + public String getExpressionSymbol() + { + return "="; + } + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java index 0e729cc521..3ed2286f2e 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java @@ -27,21 +27,22 @@ import java.math.BigDecimal; import org.apache.qpid.AMQException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; /** * Represents a constant expression */ -public class ConstantExpression implements Expression +public class ConstantExpression<E extends Exception> implements Expression<E> { - static class BooleanConstantExpression extends ConstantExpression implements BooleanExpression + static class BooleanConstantExpression<E extends Exception> extends ConstantExpression<E> implements BooleanExpression<E> { public BooleanConstantExpression(Object value) { super(value); } - public boolean matches(AMQMessage message) throws AMQException + public boolean matches(Filterable<E> message) throws E { Object object = evaluate(message); @@ -120,7 +121,7 @@ public class ConstantExpression implements Expression this.value = value; } - public Object evaluate(AMQMessage message) throws AMQException + public Object evaluate(Filterable<E> message) throws E { return value; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java index 5f646c15db..f2ebe41d26 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java @@ -22,16 +22,17 @@ package org.apache.qpid.server.filter; import org.apache.qpid.AMQException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; /** * Represents an expression */ -public interface Expression +public interface Expression<E extends Exception> { /** * @return the value of this expression */ - public Object evaluate(AMQMessage message) throws AMQException; + public Object evaluate(Filterable<E> message) throws E; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java b/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java index c82de9fa15..dd3c126ee5 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java @@ -24,14 +24,16 @@ package org.apache.qpid.server.filter; // import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; +import org.apache.qpid.AMQException; -public interface FilterManager +public interface FilterManager<E extends Exception> { - void add(MessageFilter filter); + void add(MessageFilter<E> filter); - void remove(MessageFilter filter); + void remove(MessageFilter<E> filter); - boolean allAllow(AMQMessage msg); + boolean allAllow(Filterable<E> msg); boolean hasFilters(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java b/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java index 311f0680ec..a7f49d0566 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java @@ -39,7 +39,7 @@ public class FilterManagerFactory if (filters != null) { - manager = new SimpleFilterManager(); + if(filters.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue())) { @@ -47,23 +47,13 @@ public class FilterManagerFactory if (selector != null && !selector.equals("")) { + manager = new SimpleFilterManager(); manager.add(new JMSSelectorFilter(selector)); } } - if (filters.containsKey(AMQPFilterTypes.NO_CONSUME.getValue())) - { - manager.add(new NoConsumerFilter()); - } - - - //If we added no filters don't bear the overhead of having an filter manager - if (!manager.hasFilters()) - { - manager = null; - } } else { diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java b/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java index 32f58ed666..96c9353872 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java @@ -23,42 +23,30 @@ package org.apache.qpid.server.filter; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.server.filter.jms.selector.SelectorParser; -import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; - -public class JMSSelectorFilter implements MessageFilter +public class JMSSelectorFilter<E extends Exception> implements MessageFilter<E> { private final static Logger _logger = org.apache.log4j.Logger.getLogger(JMSSelectorFilter.class); private String _selector; - private BooleanExpression _matcher; + private BooleanExpression<E> _matcher; public JMSSelectorFilter(String selector) throws AMQException { _selector = selector; - _logger.info("Created JMSSelectorFilter with selector:" + _selector); - - _matcher = new SelectorParser().parse(selector); - - } - public boolean matches(AMQMessage message) + public boolean matches(Filterable<E> message) throws E { - try - { - boolean match = _matcher.matches(message); - _logger.info(message + " match(" + match + ") selector(" + System.identityHashCode(_selector) + "):" + _selector); - return match; - } - catch (AMQException e) + boolean match = _matcher.matches(message); + if(_logger.isDebugEnabled()) { - //fixme this needs to be sorted.. it shouldn't happen - e.printStackTrace(); + _logger.debug(message + " match(" + match + ") selector(" + System.identityHashCode(_selector) + "):" + _selector); } - return false; + return match; } public String getSelector() diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java index c8cbdb2125..094363ed9a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java @@ -22,71 +22,22 @@ package org.apache.qpid.server.filter; import org.apache.qpid.AMQException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; /** * A filter performing a comparison of two objects */ -public abstract class LogicExpression extends BinaryExpression implements BooleanExpression +public abstract class LogicExpression<E extends Exception> extends BinaryExpression<E> implements BooleanExpression<E> { - public static BooleanExpression createOR(BooleanExpression lvalue, BooleanExpression rvalue) + public static<E extends Exception> BooleanExpression createOR(BooleanExpression<E> lvalue, BooleanExpression<E> rvalue) { - return new LogicExpression(lvalue, rvalue) - { - - public Object evaluate(AMQMessage message) throws AMQException - { - - Boolean lv = (Boolean) left.evaluate(message); - // Can we do an OR shortcut?? - if ((lv != null) && lv.booleanValue()) - { - return Boolean.TRUE; - } - - Boolean rv = (Boolean) right.evaluate(message); - - return (rv == null) ? null : rv; - } - - public String getExpressionSymbol() - { - return "OR"; - } - }; + return new OrExpression(lvalue, rvalue); } - public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue) + public static<E extends Exception> BooleanExpression createAND(BooleanExpression<E> lvalue, BooleanExpression<E> rvalue) { - return new LogicExpression(lvalue, rvalue) - { - - public Object evaluate(AMQMessage message) throws AMQException - { - - Boolean lv = (Boolean) left.evaluate(message); - - // Can we do an AND shortcut?? - if (lv == null) - { - return null; - } - - if (!lv.booleanValue()) - { - return Boolean.FALSE; - } - - Boolean rv = (Boolean) right.evaluate(message); - - return (rv == null) ? null : rv; - } - - public String getExpressionSymbol() - { - return "AND"; - } - }; + return new AndExpression(lvalue, rvalue); } /** @@ -98,13 +49,74 @@ public abstract class LogicExpression extends BinaryExpression implements Boolea super(left, right); } - public abstract Object evaluate(AMQMessage message) throws AMQException; + public abstract Object evaluate(Filterable<E> message) throws E; - public boolean matches(AMQMessage message) throws AMQException + public boolean matches(Filterable<E> message) throws E { Object object = evaluate(message); return (object != null) && (object == Boolean.TRUE); } + private static class OrExpression<E extends Exception> extends LogicExpression<E> + { + public OrExpression(final BooleanExpression<E> lvalue, final BooleanExpression<E> rvalue) + { + super(lvalue, rvalue); + } + + public Object evaluate(Filterable<E> message) throws E + { + + Boolean lv = (Boolean) left.evaluate(message); + // Can we do an OR shortcut?? + if ((lv != null) && lv.booleanValue()) + { + return Boolean.TRUE; + } + + Boolean rv = (Boolean) right.evaluate(message); + + return (rv == null) ? null : rv; + } + + public String getExpressionSymbol() + { + return "OR"; + } + } + + private static class AndExpression<E extends Exception> extends LogicExpression<E> + { + public AndExpression(final BooleanExpression<E> lvalue, final BooleanExpression<E> rvalue) + { + super(lvalue, rvalue); + } + + public Object evaluate(Filterable<E> message) throws E + { + + Boolean lv = (Boolean) left.evaluate(message); + + // Can we do an AND shortcut?? + if (lv == null) + { + return null; + } + + if (!lv.booleanValue()) + { + return Boolean.FALSE; + } + + Boolean rv = (Boolean) right.evaluate(message); + + return (rv == null) ? null : rv; + } + + public String getExpressionSymbol() + { + return "AND"; + } + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java b/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java index e6bfe974d5..58fc55f8e6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java @@ -22,8 +22,9 @@ package org.apache.qpid.server.filter; import org.apache.qpid.AMQException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; -public interface MessageFilter +public interface MessageFilter<E extends Exception> { - boolean matches(AMQMessage message) throws AMQException; + boolean matches(Filterable<E> message) throws E; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java b/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java index 47ca930d12..f1b3b2511d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java @@ -22,7 +22,7 @@ package org.apache.qpid.server.filter; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; -import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; public class NoConsumerFilter implements MessageFilter { @@ -34,7 +34,7 @@ public class NoConsumerFilter implements MessageFilter _logger.info("Created NoConsumerFilter"); } - public boolean matches(AMQMessage message) + public boolean matches(Filterable message) { return true; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java index 5ab360ca19..b30c70dac3 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java @@ -30,12 +30,12 @@ import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.CommonContentHeaderProperties; -import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; /** * Represents a property expression */ -public class PropertyExpression implements Expression +public class PropertyExpression<E extends Exception> implements Expression<E> { // Constants - defined the same as JMS private static final int NON_PERSISTENT = 1; @@ -44,223 +44,60 @@ public class PropertyExpression implements Expression private static final Logger _logger = org.apache.log4j.Logger.getLogger(PropertyExpression.class); - private static final HashMap<String, Expression> JMS_PROPERTY_EXPRESSIONS = new HashMap<String, Expression>(); + private static final HashMap<String, Expression<? extends Exception>> JMS_PROPERTY_EXPRESSIONS = new HashMap<String, Expression<? extends Exception>>(); - static { - JMS_PROPERTY_EXPRESSIONS.put("JMSDestination", new Expression() + JMS_PROPERTY_EXPRESSIONS.put("JMSDestination", new Expression<E>() { - public Object evaluate(AMQMessage message) + public Object evaluate(Filterable<E> message) { //TODO return null; } }); - JMS_PROPERTY_EXPRESSIONS.put("JMSReplyTo", new Expression() - { - public Object evaluate(AMQMessage message) - { - try - { - CommonContentHeaderProperties _properties = - (CommonContentHeaderProperties) - message.getContentHeaderBody().properties; - AMQShortString replyTo = _properties.getReplyTo(); - - return (replyTo == null) ? null : replyTo.toString(); - } - catch (AMQException e) - { - _logger.warn(e); - - return null; - } - - } + JMS_PROPERTY_EXPRESSIONS.put("JMSReplyTo", new ReplyToExpression()); - }); - - JMS_PROPERTY_EXPRESSIONS.put("JMSType", new Expression() - { - public Object evaluate(AMQMessage message) - { - try - { - CommonContentHeaderProperties _properties = - (CommonContentHeaderProperties) - message.getContentHeaderBody().properties; - AMQShortString type = _properties.getType(); - - return (type == null) ? null : type.toString(); - } - catch (AMQException e) - { - _logger.warn(e); - - return null; - } - - } - }); - - JMS_PROPERTY_EXPRESSIONS.put("JMSDeliveryMode", new Expression() - { - public Object evaluate(AMQMessage message) - { - try - { - int mode = message.isPersistent() ? PERSISTENT : NON_PERSISTENT; - if (_logger.isDebugEnabled()) - { - _logger.debug("JMSDeliveryMode is :" + mode); - } - - return mode; - } - catch (AMQException e) - { - _logger.warn(e); - } - - return NON_PERSISTENT; - } - }); + JMS_PROPERTY_EXPRESSIONS.put("JMSType", new TypeExpression()); - JMS_PROPERTY_EXPRESSIONS.put("JMSPriority", new Expression() - { - public Object evaluate(AMQMessage message) - { - try - { - CommonContentHeaderProperties _properties = - (CommonContentHeaderProperties) - message.getContentHeaderBody().properties; - - return (int) _properties.getPriority(); - } - catch (AMQException e) - { - _logger.warn(e); - } - - return DEFAULT_PRIORITY; - } - }); + JMS_PROPERTY_EXPRESSIONS.put("JMSDeliveryMode", new DeliveryModeExpression()); - JMS_PROPERTY_EXPRESSIONS.put("AMQMessageID", new Expression() - { - public Object evaluate(AMQMessage message) - { + JMS_PROPERTY_EXPRESSIONS.put("JMSPriority", new PriorityExpression()); - try - { - CommonContentHeaderProperties _properties = - (CommonContentHeaderProperties) - message.getContentHeaderBody().properties; - AMQShortString messageId = _properties.getMessageId(); + JMS_PROPERTY_EXPRESSIONS.put("AMQMessageID", new MessageIDExpression()); - return (messageId == null) ? null : messageId; - } - catch (AMQException e) - { - _logger.warn(e); + JMS_PROPERTY_EXPRESSIONS.put("JMSTimestamp", new TimestampExpression()); - return null; - } + JMS_PROPERTY_EXPRESSIONS.put("JMSCorrelationID", new CorrelationIdExpression()); - } - }); + JMS_PROPERTY_EXPRESSIONS.put("JMSExpiration", new ExpirationExpression()); - JMS_PROPERTY_EXPRESSIONS.put("JMSTimestamp", new Expression() + JMS_PROPERTY_EXPRESSIONS.put("JMSRedelivered", new Expression<E>() { - public Object evaluate(AMQMessage message) - { - - try - { - CommonContentHeaderProperties _properties = - (CommonContentHeaderProperties) - message.getContentHeaderBody().properties; - - return _properties.getTimestamp(); - } - catch (AMQException e) - { - _logger.warn(e); - - return null; - } - - } - }); - - JMS_PROPERTY_EXPRESSIONS.put("JMSCorrelationID", new Expression() - { - public Object evaluate(AMQMessage message) - { - - try - { - CommonContentHeaderProperties _properties = - (CommonContentHeaderProperties) - message.getContentHeaderBody().properties; - AMQShortString correlationId = _properties.getCorrelationId(); - - return (correlationId == null) ? null : correlationId.toString(); - } - catch (AMQException e) - { - _logger.warn(e); - - return null; - } - - } - }); - - JMS_PROPERTY_EXPRESSIONS.put("JMSExpiration", new Expression() - { - public Object evaluate(AMQMessage message) - { - - try - { - CommonContentHeaderProperties _properties = - (CommonContentHeaderProperties) - message.getContentHeaderBody().properties; - - return _properties.getExpiration(); - } - catch (AMQException e) - { - _logger.warn(e); - - return null; - } - - } - }); - - JMS_PROPERTY_EXPRESSIONS.put("JMSRedelivered", new Expression() - { - public Object evaluate(AMQMessage message) + public Object evaluate(Filterable message) throws E { return message.isRedelivered(); } }); - } private final String name; - private final Expression jmsPropertyExpression; + private final Expression<E> jmsPropertyExpression; + + public boolean outerTest() + { + return false; + } public PropertyExpression(String name) { this.name = name; - jmsPropertyExpression = JMS_PROPERTY_EXPRESSIONS.get(name); + + + + jmsPropertyExpression = (Expression<E>) JMS_PROPERTY_EXPRESSIONS.get(name); } - public Object evaluate(AMQMessage message) throws AMQException + public Object evaluate(Filterable<E> message) throws E { if (jmsPropertyExpression != null) @@ -319,4 +156,113 @@ public class PropertyExpression implements Expression } + private static class ReplyToExpression<E extends Exception> implements Expression<E> + { + public Object evaluate(Filterable<E> message) throws E + { + + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString replyTo = _properties.getReplyTo(); + + return (replyTo == null) ? null : replyTo.toString(); + + } + + } + + private static class TypeExpression<E extends Exception> implements Expression<E> + { + public Object evaluate(Filterable<E> message) throws E + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString type = _properties.getType(); + + return (type == null) ? null : type.toString(); + + } + } + + private static class DeliveryModeExpression<E extends Exception> implements Expression<E> + { + public Object evaluate(Filterable<E> message) throws E + { + int mode = message.isPersistent() ? PERSISTENT : NON_PERSISTENT; + if (_logger.isDebugEnabled()) + { + _logger.debug("JMSDeliveryMode is :" + mode); + } + + return mode; + } + } + + private static class PriorityExpression<E extends Exception> implements Expression<E> + { + public Object evaluate(Filterable<E> message) throws E + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + + return (int) _properties.getPriority(); + } + } + + private static class MessageIDExpression<E extends Exception> implements Expression<E> + { + public Object evaluate(Filterable<E> message) throws E + { + + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString messageId = _properties.getMessageId(); + + return (messageId == null) ? null : messageId; + + } + } + + private static class TimestampExpression<E extends Exception> implements Expression<E> + { + public Object evaluate(Filterable<E> message) throws E + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + + return _properties.getTimestamp(); + } + } + + private static class CorrelationIdExpression<E extends Exception> implements Expression<E> + { + public Object evaluate(Filterable<E> message) throws E + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString correlationId = _properties.getCorrelationId(); + + return (correlationId == null) ? null : correlationId.toString(); + } + } + + private static class ExpirationExpression<E extends Exception> implements Expression<E> + { + public Object evaluate(Filterable<E> message) throws E + { + + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + + return _properties.getExpiration(); + + } + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java b/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java index 62a45f5420..cb738e1489 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java @@ -25,32 +25,33 @@ import java.util.concurrent.ConcurrentLinkedQueue; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; -public class SimpleFilterManager implements FilterManager +public class SimpleFilterManager implements FilterManager<AMQException> { private final Logger _logger = Logger.getLogger(SimpleFilterManager.class); - private final ConcurrentLinkedQueue<MessageFilter> _filters; + private final ConcurrentLinkedQueue<MessageFilter<AMQException>> _filters; public SimpleFilterManager() { _logger.debug("Creating SimpleFilterManager"); - _filters = new ConcurrentLinkedQueue<MessageFilter>(); + _filters = new ConcurrentLinkedQueue<MessageFilter<AMQException>>(); } - public void add(MessageFilter filter) + public void add(MessageFilter<AMQException> filter) { _filters.add(filter); } - public void remove(MessageFilter filter) + public void remove(MessageFilter<AMQException> filter) { _filters.remove(filter); } - public boolean allAllow(AMQMessage msg) + public boolean allAllow(Filterable<AMQException> msg) { - for (MessageFilter filter : _filters) + for (MessageFilter<AMQException> filter : _filters) { try { diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java index 83b4ed5358..799a38af5a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java @@ -30,45 +30,23 @@ import java.util.Iterator; import java.util.List; import org.apache.qpid.AMQException; -import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; /** * An expression which performs an operation on two expression values */ -public abstract class UnaryExpression implements Expression +public abstract class UnaryExpression<E extends Exception> implements Expression<E> { private static final BigDecimal BD_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE); - protected Expression right; + protected Expression<E> right; - public static Expression createNegate(Expression left) + public static<E extends Exception> Expression<E> createNegate(Expression<E> left) { - return new UnaryExpression(left) - { - public Object evaluate(AMQMessage message) throws AMQException - { - Object rvalue = right.evaluate(message); - if (rvalue == null) - { - return null; - } - - if (rvalue instanceof Number) - { - return negate((Number) rvalue); - } - - return null; - } - - public String getExpressionSymbol() - { - return "-"; - } - }; + return new NegativeExpression(left); } - public static BooleanExpression createInExpression(PropertyExpression right, List elements, final boolean not) + public static<E extends Exception> BooleanExpression createInExpression(PropertyExpression<E> right, List elements, final boolean not) { // Use a HashSet if there are many elements. @@ -88,81 +66,17 @@ public abstract class UnaryExpression implements Expression final Collection inList = t; - return new BooleanUnaryExpression(right) - { - public Object evaluate(AMQMessage message) throws AMQException - { - - Object rvalue = right.evaluate(message); - if (rvalue == null) - { - return null; - } - - if (rvalue.getClass() != String.class) - { - return null; - } - - if (((inList != null) && inList.contains(rvalue)) ^ not) - { - return Boolean.TRUE; - } - else - { - return Boolean.FALSE; - } - - } - - public String toString() - { - StringBuffer answer = new StringBuffer(); - answer.append(right); - answer.append(" "); - answer.append(getExpressionSymbol()); - answer.append(" ( "); - - int count = 0; - for (Iterator i = inList.iterator(); i.hasNext();) - { - Object o = (Object) i.next(); - if (count != 0) - { - answer.append(", "); - } - - answer.append(o); - count++; - } - - answer.append(" )"); - - return answer.toString(); - } - - public String getExpressionSymbol() - { - if (not) - { - return "NOT IN"; - } - else - { - return "IN"; - } - } - }; + return new InExpression(right, inList, not); } - abstract static class BooleanUnaryExpression extends UnaryExpression implements BooleanExpression + abstract static class BooleanUnaryExpression<E extends Exception> extends UnaryExpression<E> implements BooleanExpression<E> { - public BooleanUnaryExpression(Expression left) + public BooleanUnaryExpression(Expression<E> left) { super(left); } - public boolean matches(AMQMessage message) throws AMQException + public boolean matches(Filterable<E> message) throws E { Object object = evaluate(message); @@ -171,26 +85,9 @@ public abstract class UnaryExpression implements Expression } ; - public static BooleanExpression createNOT(BooleanExpression left) + public static<E extends Exception> BooleanExpression<E> createNOT(BooleanExpression<E> left) { - return new BooleanUnaryExpression(left) - { - public Object evaluate(AMQMessage message) throws AMQException - { - Boolean lvalue = (Boolean) right.evaluate(message); - if (lvalue == null) - { - return null; - } - - return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE; - } - - public String getExpressionSymbol() - { - return "NOT"; - } - }; + return new NotExpression(left); } public static BooleanExpression createXPath(final String xpath) @@ -203,36 +100,9 @@ public abstract class UnaryExpression implements Expression return new XQueryExpression(xpath); } - public static BooleanExpression createBooleanCast(Expression left) + public static<E extends Exception> BooleanExpression createBooleanCast(Expression<E> left) { - return new BooleanUnaryExpression(left) - { - public Object evaluate(AMQMessage message) throws AMQException - { - Object rvalue = right.evaluate(message); - if (rvalue == null) - { - return null; - } - - if (!rvalue.getClass().equals(Boolean.class)) - { - return Boolean.FALSE; - } - - return ((Boolean) rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE; - } - - public String toString() - { - return right.toString(); - } - - public String getExpressionSymbol() - { - return ""; - } - }; + return new BooleanCastExpression(left); } private static Number negate(Number left) @@ -281,7 +151,7 @@ public abstract class UnaryExpression implements Expression this.right = left; } - public Expression getRight() + public Expression<E> getRight() { return right; } @@ -334,4 +204,166 @@ public abstract class UnaryExpression implements Expression */ public abstract String getExpressionSymbol(); + private static class NegativeExpression<E extends Exception> extends UnaryExpression<E> + { + public NegativeExpression(final Expression<E> left) + { + super(left); + } + + public Object evaluate(Filterable<E> message) throws E + { + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + if (rvalue instanceof Number) + { + return negate((Number) rvalue); + } + + return null; + } + + public String getExpressionSymbol() + { + return "-"; + } + } + + private static class InExpression<E extends Exception> extends BooleanUnaryExpression<E> + { + private final Collection _inList; + private final boolean _not; + + public InExpression(final PropertyExpression<E> right, final Collection inList, final boolean not) + { + super(right); + _inList = inList; + _not = not; + } + + public Object evaluate(Filterable<E> message) throws E + { + + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + if (rvalue.getClass() != String.class) + { + return null; + } + + if (((_inList != null) && _inList.contains(rvalue)) ^ _not) + { + return Boolean.TRUE; + } + else + { + return Boolean.FALSE; + } + + } + + public String toString() + { + StringBuffer answer = new StringBuffer(); + answer.append(right); + answer.append(" "); + answer.append(getExpressionSymbol()); + answer.append(" ( "); + + int count = 0; + for (Iterator i = _inList.iterator(); i.hasNext();) + { + Object o = (Object) i.next(); + if (count != 0) + { + answer.append(", "); + } + + answer.append(o); + count++; + } + + answer.append(" )"); + + return answer.toString(); + } + + public String getExpressionSymbol() + { + if (_not) + { + return "NOT IN"; + } + else + { + return "IN"; + } + } + } + + private static class NotExpression<E extends Exception> extends BooleanUnaryExpression<E> + { + public NotExpression(final BooleanExpression<E> left) + { + super(left); + } + + public Object evaluate(Filterable<E> message) throws E + { + Boolean lvalue = (Boolean) right.evaluate(message); + if (lvalue == null) + { + return null; + } + + return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE; + } + + public String getExpressionSymbol() + { + return "NOT"; + } + } + + private static class BooleanCastExpression<E extends Exception> extends BooleanUnaryExpression<E> + { + public BooleanCastExpression(final Expression<E> left) + { + super(left); + } + + public Object evaluate(Filterable<E> message) throws E + { + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + if (!rvalue.getClass().equals(Boolean.class)) + { + return Boolean.FALSE; + } + + return ((Boolean) rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE; + } + + public String toString() + { + return right.toString(); + } + + public String getExpressionSymbol() + { + return ""; + } + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java index f5454afae5..1311178fb1 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.filter; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -70,7 +71,7 @@ public final class XPathExpression implements BooleanExpression { private final XPathEvaluator evaluator; static public interface XPathEvaluator { - public boolean evaluate(AMQMessage message) throws AMQException; + public boolean evaluate(Filterable message) throws AMQException; } XPathExpression(String xpath) { @@ -92,7 +93,7 @@ public final class XPathExpression implements BooleanExpression { } } - public Object evaluate(AMQMessage message) throws AMQException { + public Object evaluate(Filterable message) throws AMQException { // try { //FIXME this is flow to disk work // if( message.isDropped() ) @@ -117,7 +118,7 @@ public final class XPathExpression implements BooleanExpression { * @return true if the expression evaluates to Boolean.TRUE. * @throws AMQException */ - public boolean matches(AMQMessage message) throws AMQException + public boolean matches(Filterable message) throws AMQException { Object object = evaluate(message); return object!=null && object==Boolean.TRUE; diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java index f5debb607a..c13f81cd08 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java @@ -19,6 +19,7 @@ package org.apache.qpid.server.filter; import org.apache.qpid.AMQException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; // // Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html> @@ -35,7 +36,7 @@ public final class XQueryExpression implements BooleanExpression { this.xpath = xpath; } - public Object evaluate(AMQMessage message) throws AMQException { + public Object evaluate(Filterable message) throws AMQException { return Boolean.FALSE; } @@ -48,7 +49,7 @@ public final class XQueryExpression implements BooleanExpression { * @return true if the expression evaluates to Boolean.TRUE. * @throws AMQException */ - public boolean matches(AMQMessage message) throws AMQException + public boolean matches(Filterable message) throws AMQException { Object object = evaluate(message); return object!=null && object==Boolean.TRUE; diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java b/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java index 35d770fd5d..cc67776682 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java @@ -29,6 +29,7 @@ import javax.xml.parsers.DocumentBuilderFactory; import org.apache.qpid.AMQException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; import org.apache.xpath.CachedXPathAPI; import org.w3c.dom.Document; import org.w3c.dom.traversal.NodeIterator; @@ -42,7 +43,7 @@ public class XalanXPathEvaluator implements XPathExpression.XPathEvaluator { this.xpath = xpath; } - public boolean evaluate(AMQMessage m) throws AMQException + public boolean evaluate(Filterable m) throws AMQException { // TODO - we would have to check the content type and then evaluate the content // here... is this really a feature we wish to implement? - RobG diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java index af49e113f6..895db7b15b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java @@ -54,4 +54,9 @@ public abstract class AbstractFlowCreditManager implements FlowCreditManager notifyListeners(suspended); } } + + protected final void notifyIncreaseBytesCredit() + { + notifyListeners(false); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java index 6cff2d8488..c1b3a09006 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java @@ -28,7 +28,7 @@ public class MessageOnlyCreditManager extends AbstractFlowCreditManager implemen { private final AtomicLong _messageCredit; - MessageOnlyCreditManager(final long initialCredit) + public MessageOnlyCreditManager(final long initialCredit) { _messageCredit = new AtomicLong(initialCredit); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java index d9752b1098..be0300f2c1 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java @@ -84,8 +84,10 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F public synchronized void addCredit(final long messageCredit, final long bytesCredit) { final long messageCreditLimit = _messageCreditLimit; + boolean notifyIncrease = true; if(messageCreditLimit != 0L) { + notifyIncrease = (_messageCredit != 0); long newCredit = _messageCredit + messageCredit; _messageCredit = newCredit > messageCreditLimit ? messageCreditLimit : newCredit; } @@ -96,8 +98,14 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F { long newCredit = _bytesCredit + bytesCredit; _bytesCredit = newCredit > bytesCreditLimit ? bytesCreditLimit : newCredit; + if(notifyIncrease && bytesCredit>0) + { + notifyIncreaseBytesCredit(); + } } + + setSuspended(!hasCredit()); } @@ -138,7 +146,7 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F } else { - setSuspended(true); + //setSuspended(true); return false; } } @@ -166,7 +174,7 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F } else { - setSuspended(true); + //setSuspended(true); return false; } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java index b97831c3ce..be1135dd91 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java @@ -26,11 +26,21 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.BasicGetBody;
import org.apache.qpid.framing.BasicGetEmptyBody;
import org.apache.qpid.framing.MethodRegistry;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.flow.FlowCreditManager;
+import org.apache.qpid.server.flow.MessageOnlyCreditManager;
+import org.apache.qpid.server.subscription.SubscriptionImpl;
+import org.apache.qpid.server.subscription.ClientDeliveryMethod;
+import org.apache.qpid.server.subscription.RecordDeliveryMethod;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.subscription.SubscriptionFactoryImpl;
import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQQueueImpl;
import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.SimpleAMQQueue;
import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
@@ -86,7 +96,7 @@ public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetB //Perform ACLs
vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue);
- if (!queue.performGet(session, channel, !body.getNoAck()))
+ if (!performGet(queue,session, channel, !body.getNoAck()))
{
MethodRegistry methodRegistry = session.getMethodRegistry();
// TODO - set clusterId
@@ -98,4 +108,80 @@ public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetB }
}
}
+
+ public static boolean performGet(final AMQQueue queue,
+ final AMQProtocolSession session,
+ final AMQChannel channel,
+ final boolean acks)
+ throws AMQException
+ {
+
+ final FlowCreditManager singleMessageCredit = new MessageOnlyCreditManager(1L);
+
+ final ClientDeliveryMethod getDeliveryMethod = new ClientDeliveryMethod()
+ {
+
+ int _msg;
+
+ public void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag)
+ throws AMQException
+ {
+ singleMessageCredit.useCreditForMessage(entry.getMessage());
+ session.getProtocolOutputConverter().writeGetOk(entry.getMessage(), channel.getChannelId(),
+ deliveryTag, queue.getMessageCount());
+
+ }
+ };
+ final RecordDeliveryMethod getRecordMethod = new RecordDeliveryMethod()
+ {
+
+ public void recordMessageDelivery(final Subscription sub, final QueueEntry entry, final long deliveryTag)
+ {
+ channel.addUnacknowledgedMessage(entry, deliveryTag, null);
+ }
+ };
+
+ Subscription sub;
+ if(acks)
+ {
+ sub = SubscriptionFactoryImpl.INSTANCE.createSubscription(channel, session, null, acks, null, false, singleMessageCredit, getDeliveryMethod, getRecordMethod);
+ }
+ else
+ {
+ sub = new GetNoAckSubscription(channel,
+ session,
+ null,
+ null,
+ false,
+ singleMessageCredit,
+ getDeliveryMethod,
+ getRecordMethod);
+ }
+
+ queue.registerSubscription(sub,false);
+ queue.flushSubscription(sub);
+ queue.unregisterSubscription(sub);
+ return(!singleMessageCredit.hasCredit());
+
+
+ }
+
+ public static final class GetNoAckSubscription extends SubscriptionImpl.NoAckSubscription
+ {
+ public GetNoAckSubscription(AMQChannel channel, AMQProtocolSession protocolSession,
+ AMQShortString consumerTag, FieldTable filters,
+ boolean noLocal, FlowCreditManager creditManager,
+ ClientDeliveryMethod deliveryMethod,
+ RecordDeliveryMethod recordMethod)
+ throws AMQException
+ {
+ super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod);
+ }
+
+ public boolean wouldSuspend(QueueEntry msg)
+ {
+ return !getCreditManager().useCreditForMessage(msg.getMessage());
+ }
+
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java index 2241281be1..379ec7a7d6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java @@ -174,7 +174,9 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar { final QueueRegistry registry = virtualHost.getQueueRegistry(); AMQShortString owner = body.getExclusive() ? session.getContextKey() : null; - final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), virtualHost); + + final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), virtualHost, + body.getArguments()); if (body.getExclusive() && !body.getDurable()) diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java index cf3d07160f..e790493a82 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java @@ -42,7 +42,7 @@ import java.util.concurrent.atomic.AtomicInteger; /** * A deliverable message. */ -public class AMQMessage +public class AMQMessage implements Filterable<AMQException> { /** Used for debugging purposes. */ private static final Logger _log = Logger.getLogger(AMQMessage.class); diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java new file mode 100644 index 0000000000..8ef28fbcd2 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java @@ -0,0 +1,41 @@ +/* +* +* 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.qpid.server.queue; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.AMQException; + +public class AMQPriorityQueue extends SimpleAMQQueue +{ + protected AMQPriorityQueue(final AMQShortString name, + final boolean durable, + final AMQShortString owner, + final boolean autoDelete, + final VirtualHost virtualHost, + int priorities) + throws AMQException + { + super(name, durable, owner, autoDelete, virtualHost, new PriorityQueueList.Factory(priorities)); + } + + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index fcfe26c4bd..98583c03d4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -34,7 +34,7 @@ import org.apache.qpid.AMQException; import java.util.List; import java.util.Set; -public interface AMQQueue extends Managable, Comparable +public interface AMQQueue extends Managable, Comparable<AMQQueue> { AMQShortString getName(); @@ -147,11 +147,6 @@ public interface AMQQueue extends Managable, Comparable void deliverAsync(); - - boolean performGet(final AMQProtocolSession session, final AMQChannel channel, final boolean b) throws AMQException; - - - void addQueueDeleteTask(final Task task); boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException; @@ -160,6 +155,8 @@ public interface AMQQueue extends Managable, Comparable Set<NotificationCheck> getNotificationChecks(); + void flushSubscription(final Subscription sub) throws AMQException; + /** * ExistingExclusiveSubscription signals a failure to create a subscription, because an exclusive subscription diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java index 99da9c20fa..e751212272 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java @@ -21,20 +21,32 @@ package org.apache.qpid.server.queue; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.AMQException; public class AMQQueueFactory { + private static final AMQShortString X_QPID_PRIORITIES = new AMQShortString("x-qpid-priorities"); + public static AMQQueue createAMQQueueImpl(AMQShortString name, - boolean durable, - AMQShortString owner, - boolean autoDelete, - VirtualHost virtualHost) + boolean durable, + AMQShortString owner, + boolean autoDelete, + VirtualHost virtualHost, final FieldTable arguments) throws AMQException { - //return new AMQQueueImpl(name, durable, owner, autoDelete, virtualHost); - return new SimpleAMQQueue(name, durable, owner, autoDelete, virtualHost); + + final int priorities = arguments.containsKey(X_QPID_PRIORITIES) ? arguments.getInteger(X_QPID_PRIORITIES) : 1; + + if(priorities > 1) + { + return new AMQPriorityQueue(name, durable, owner, autoDelete, virtualHost, priorities); + } + else + { + return new SimpleAMQQueue(name, durable, owner, autoDelete, virtualHost); + } } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueImpl.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueImpl.java deleted file mode 100644 index d8d0e599ed..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueImpl.java +++ /dev/null @@ -1,1003 +0,0 @@ -/* - * - * 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.qpid.server.queue; - -import org.apache.log4j.Logger; -import org.apache.qpid.AMQException; -import org.apache.qpid.configuration.Configured; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.StoreContext; -import org.apache.qpid.server.virtualhost.VirtualHost; - -import javax.management.JMException; -import java.text.MessageFormat; -import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -/** - * This is an AMQ Queue, and should not be confused with a JMS queue or any other abstraction like that. It is described - * fully in RFC 006. - */ -public class AMQQueueImpl implements AMQQueue, Subscription.StateListener -{ - - private static final Logger _logger = Logger.getLogger(AMQQueueImpl.class); - - private final AMQShortString _name; - - /** null means shared */ - private final AMQShortString _owner; - - private final boolean _durable; - - /** If true, this queue is deleted when the last subscriber is removed */ - private final boolean _autoDelete; - - private final AtomicInteger _subscriberCount = new AtomicInteger(); - - private final AtomicBoolean _isExclusive = new AtomicBoolean(); - - private final AtomicBoolean _deleted = new AtomicBoolean(false); - - private List<Task> _deleteTaskList = new CopyOnWriteArrayList<Task>(); - - /** Manages message delivery. */ - private final ConcurrentSelectorDeliveryManager _deliveryMgr; - - /** Used to track bindings to exchanges so that on deletion they can easily be cancelled. */ - private final ExchangeBindings _bindings = new ExchangeBindings(this); - - /** Executor on which asynchronous delivery will be carriedout where required */ - private final Executor _asyncDelivery; - - private final AMQQueueMBean _managedObject; - - private final VirtualHost _virtualHost; - - /** max allowed size(KB) of a single message */ - @Configured(path = "maximumMessageSize", defaultValue = "0") - public long _maximumMessageSize; - - /** max allowed number of messages on a queue. */ - @Configured(path = "maximumMessageCount", defaultValue = "0") - public long _maximumMessageCount; - - /** max queue depth for the queue */ - @Configured(path = "maximumQueueDepth", defaultValue = "0") - public long _maximumQueueDepth; - - /** maximum message age before alerts occur */ - @Configured(path = "maximumMessageAge", defaultValue = "0") - public long _maximumMessageAge; - - /** the minimum interval between sending out consequetive alerts of the same type */ - @Configured(path = "minimumAlertRepeatGap", defaultValue = "0") - public long _minimumAlertRepeatGap; - - /** total messages received by the queue since startup. */ - public AtomicLong _totalMessagesReceived = new AtomicLong(); - - - private final Set<NotificationCheck> _notificationChecks = EnumSet.noneOf(NotificationCheck.class); - private final AtomicLong _queueEntryId = new AtomicLong(Long.MIN_VALUE); - - - protected AMQQueueImpl(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost) - throws AMQException - { - - if (name == null) - { - throw new IllegalArgumentException("Queue name must not be null"); - } - - if (virtualHost == null) - { - throw new IllegalArgumentException("Virtual Host must not be null"); - } - - _name = name; - _durable = durable; - _owner = owner; - _autoDelete = autoDelete; - _virtualHost = virtualHost; - _asyncDelivery = AsyncDeliveryConfig.getAsyncDeliveryExecutor(); - - _managedObject = createMBean(); - _managedObject.register(); - - _deliveryMgr = new ConcurrentSelectorDeliveryManager(this); - - // This ensure that the notification checks for the configured alerts are created. - setMaximumMessageAge(_maximumMessageAge); - setMaximumMessageCount(_maximumMessageCount); - setMaximumMessageSize(_maximumMessageSize); - setMaximumQueueDepth(_maximumQueueDepth); - - } - - private AMQQueueMBean createMBean() throws AMQException - { - try - { - return new AMQQueueMBean(this); - } - catch (JMException ex) - { - throw new AMQException("AMQQueue MBean creation has failed ", ex); - } - } - - public final AMQShortString getName() - { - return _name; - } - - public boolean isDurable() - { - return _durable; - } - - public AMQShortString getOwner() - { - return _owner; - } - - public boolean isAutoDelete() - { - return _autoDelete; - } - - public boolean isDeleted() - { - return _deleted.get(); - } - - /** @return no of messages(undelivered) on the queue. */ - public int getMessageCount() - { - return _deliveryMgr.getQueueMessageCount(); - } - - public int getUndeliveredMessageCount() - { - return getMessageCount(); - } - - /** @return List of messages(undelivered) on the queue. */ - public List<QueueEntry> getMessagesOnTheQueue() - { - return _deliveryMgr.getMessages(); - } - - /** - * Returns messages within the given range of message Ids. - * - * @param fromMessageId - * @param toMessageId - * - * @return List of messages - */ - public List<QueueEntry> getMessagesOnTheQueue(long fromMessageId, long toMessageId) - { - return _deliveryMgr.getMessages(fromMessageId, toMessageId); - } - - public long getQueueDepth() - { - return _deliveryMgr.getTotalMessageSize(); - } - - /** - * @param messageId - * - * @return QueueEntry with give id if exists. null if QueueEntry with given id doesn't exist. - */ - public QueueEntry getMessageOnTheQueue(long messageId) - { - List<QueueEntry> list = getMessagesOnTheQueue(messageId, messageId); - if ((list == null) || (list.size() == 0)) - { - return null; - } - - return list.get(0); - } - - /** - * Moves messages from this queue to another queue, and also commits the move on the message store. Delivery activity - * on the queues being moved between is suspended during the move. - * - * @param fromMessageId The first message id to move. - * @param toMessageId The last message id to move. - * @param queueName The queue to move the messages to. - * @param storeContext The context of the message store under which to perform the move. This is associated with - * the stores transactional context. - */ - public synchronized void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, - StoreContext storeContext) - { - AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); - - MessageStore fromStore = getVirtualHost().getMessageStore(); - MessageStore toStore = toQueue.getVirtualHost().getMessageStore(); - - if (toStore != fromStore) - { - throw new RuntimeException("Can only move messages between queues on the same message store."); - } - - try - { - // Obtain locks to prevent activity on the queues being moved between. - quiesce(); - toQueue.quiesce(); - - // Get the list of messages to move. - List<QueueEntry> foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId); - - try - { - fromStore.beginTran(storeContext); - - // Move the messages in on the message store. - for (QueueEntry entry : foundMessagesList) - { - AMQMessage message = entry.getMessage(); - fromStore.dequeueMessage(storeContext, _name, message.getMessageId()); - toStore.enqueueMessage(storeContext, toQueue.getName(), message.getMessageId()); - } - - // Commit and flush the move transcations. - try - { - fromStore.commitTran(storeContext); - } - catch (AMQException e) - { - throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e); - } - - // Move the messages on the in-memory queues. - toQueue.enqueueMovedMessages(storeContext, foundMessagesList); - _deliveryMgr.removeMovedMessages(foundMessagesList); - } - // Abort the move transactions on move failures. - catch (AMQException e) - { - try - { - fromStore.abortTran(storeContext); - } - catch (AMQException ae) - { - throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae); - } - } - } - // Release locks to allow activity on the queues being moved between to continue. - finally - { - toQueue.start(); - start(); - } - } - - /** - * Copies messages on this queue to another queue, and also commits the move on the message store. Delivery activity - * on the queues being moved between is suspended during the move. - * - * @param fromMessageId The first message id to move. - * @param toMessageId The last message id to move. - * @param queueName The queue to move the messages to. - * @param storeContext The context of the message store under which to perform the move. This is associated with - * the stores transactional context. - */ - public synchronized void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, - StoreContext storeContext) - { - AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); - - MessageStore fromStore = getVirtualHost().getMessageStore(); - MessageStore toStore = toQueue.getVirtualHost().getMessageStore(); - - if (toStore != fromStore) - { - throw new RuntimeException("Can only move messages between queues on the same message store."); - } - - try - { - // Obtain locks to prevent activity on the queues being moved between. - quiesce(); - toQueue.quiesce(); - - // Get the list of messages to move. - List<QueueEntry> foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId); - - try - { - fromStore.beginTran(storeContext); - - // Move the messages in on the message store. - for (QueueEntry entry : foundMessagesList) - { - AMQMessage message = entry.getMessage(); - toStore.enqueueMessage(storeContext, toQueue.getName(), message.getMessageId()); - message.takeReference(); - } - - // Commit and flush the move transcations. - try - { - fromStore.commitTran(storeContext); - } - catch (AMQException e) - { - throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e); - } - - // Move the messages on the in-memory queues. - toQueue.enqueueMovedMessages(storeContext, foundMessagesList); - } - // Abort the move transactions on move failures. - catch (AMQException e) - { - try - { - fromStore.abortTran(storeContext); - } - catch (AMQException ae) - { - throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae); - } - } - } - // Release locks to allow activity on the queues being moved between to continue. - finally - { - toQueue.start(); - start(); - } - } - - /** - * Removes messages from this queue, and also commits the remove on the message store. Delivery activity - * on the queues being moved between is suspended during the remove. - * - * @param fromMessageId The first message id to move. - * @param toMessageId The last message id to move. - * @param storeContext The context of the message store under which to perform the move. This is associated with - * the stores transactional context. - */ - public synchronized void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext) - { - MessageStore fromStore = getVirtualHost().getMessageStore(); - - try - { - // Obtain locks to prevent activity on the queues being moved between. - quiesce(); - - // Get the list of messages to move. - List<QueueEntry> foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId); - - try - { - fromStore.beginTran(storeContext); - - // remove the messages in on the message store. - for (QueueEntry entry : foundMessagesList) - { - AMQMessage message = entry.getMessage(); - fromStore.dequeueMessage(storeContext, _name, message.getMessageId()); - } - - // Commit and flush the move transcations. - try - { - fromStore.commitTran(storeContext); - } - catch (AMQException e) - { - throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e); - } - - // remove the messages on the in-memory queues. - _deliveryMgr.removeMovedMessages(foundMessagesList); - } - // Abort the move transactions on move failures. - catch (AMQException e) - { - try - { - fromStore.abortTran(storeContext); - } - catch (AMQException ae) - { - throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae); - } - } - } - // Release locks to allow activity on the queues being moved between to continue. - finally - { - start(); - } - } - - public void quiesce() - { - _deliveryMgr.startMovingMessages(); - } - - public void enqueueMovedMessages(StoreContext storeContext, List<QueueEntry> messageList) - { - _deliveryMgr.enqueueMovedMessages(storeContext, messageList); - _totalMessagesReceived.addAndGet(messageList.size()); - } - - public void start() - { - _deliveryMgr.stopMovingMessages(); - _deliveryMgr.processAsync(_asyncDelivery); - } - - /** @return MBean object associated with this Queue */ - public ManagedObject getManagedObject() - { - return _managedObject; - } - - public long getMaximumMessageSize() - { - return _maximumMessageSize; - } - - public void setMaximumMessageSize(final long maximumMessageSize) - { - _maximumMessageSize = maximumMessageSize; - if(maximumMessageSize == 0L) - { - _notificationChecks.remove(NotificationCheck.MESSAGE_SIZE_ALERT); - } - else - { - _notificationChecks.add(NotificationCheck.MESSAGE_SIZE_ALERT); - } - } - - public int getConsumerCount() - { - return getSubscribers().getConsumerCount(); - } - - public int getActiveConsumerCount() - { - return getSubscribers().getActiveConsumerCount(); - } - - public long getReceivedMessageCount() - { - return _totalMessagesReceived.get(); - } - - public long getMaximumMessageCount() - { - return _maximumMessageCount; - } - - public void setMaximumMessageCount(final long maximumMessageCount) - { - _maximumMessageCount = maximumMessageCount; - if(maximumMessageCount == 0L) - { - _notificationChecks.remove(NotificationCheck.MESSAGE_COUNT_ALERT); - } - else - { - _notificationChecks.add(NotificationCheck.MESSAGE_COUNT_ALERT); - } - - - - } - - public long getMaximumQueueDepth() - { - return _maximumQueueDepth; - } - - // Sets the queue depth, the max queue size - public void setMaximumQueueDepth(final long maximumQueueDepth) - { - _maximumQueueDepth = maximumQueueDepth; - if(maximumQueueDepth == 0L) - { - _notificationChecks.remove(NotificationCheck.QUEUE_DEPTH_ALERT); - } - else - { - _notificationChecks.add(NotificationCheck.QUEUE_DEPTH_ALERT); - } - - } - - public long getOldestMessageArrivalTime() - { - return _deliveryMgr.getOldestMessageArrival(); - - } - - /** Removes the QueueEntry from the top of the queue. */ - public synchronized void deleteMessageFromTop(StoreContext storeContext) throws AMQException - { - _deliveryMgr.removeAMessageFromTop(storeContext, this); - } - - /** removes all the messages from the queue. */ - public synchronized long clearQueue(StoreContext storeContext) throws AMQException - { - return _deliveryMgr.clearAllMessages(storeContext); - } - - public void bind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException - { - exchange.registerQueue(routingKey, this, arguments); - if (isDurable() && exchange.isDurable()) - { - _virtualHost.getMessageStore().bindQueue(exchange, routingKey, this, arguments); - } - - _bindings.addBinding(routingKey, arguments, exchange); - } - - public void unBind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException - { - exchange.deregisterQueue(routingKey, this, arguments); - if (isDurable() && exchange.isDurable()) - { - _virtualHost.getMessageStore().unbindQueue(exchange, routingKey, this, arguments); - } - - boolean removed = _bindings.remove(routingKey, arguments, exchange); - if(!removed) - { - _logger.error("Mismatch between queue bindings and exchange record of bindings"); - } - } - - public void registerSubscription(final Subscription subscription, final boolean exclusive) throws AMQException - { - - if (incrementSubscriberCount() > 1) - { - if (isExclusive()) - { - decrementSubscriberCount(); - throw new ExistingExclusiveSubscription(); - } - else if (exclusive) - { - decrementSubscriberCount(); - throw new ExistingSubscriptionPreventsExclusive(); - } - - } - else if (exclusive) - { - setExclusive(true); - } - - if (_logger.isDebugEnabled()) - { - _logger.debug(MessageFormat.format("Registering protocol subscription {0} with queue {1}." - , subscription, this)); - } - - - subscription.setQueue(this); - - if (subscription.filtersMessages()) - { - if (_deliveryMgr.hasQueuedMessages()) - { - _deliveryMgr.populatePreDeliveryQueue(subscription); - } - } - - subscription.setStateListener(this); - getSubscribers().addSubscriber(subscription); - if(exclusive) - { - getSubscribers().setExclusive(true); - } - _deliveryMgr.start(subscription); - deliverAsync(); - } - - private boolean isExclusive() - { - return _isExclusive.get(); - } - - private void setExclusive(boolean exclusive) - { - _isExclusive.set(exclusive); - } - - private int incrementSubscriberCount() - { - return _subscriberCount.incrementAndGet(); - } - - private int decrementSubscriberCount() - { - return _subscriberCount.decrementAndGet(); - } - - public void unregisterSubscription(final Subscription subscription) throws AMQException - { - if (_logger.isDebugEnabled()) - { - _logger.debug(MessageFormat.format( - "Unregistering subscription {0} from {1}", - subscription, this)); - } - - - getSubscribers().setExclusive(false); - _deliveryMgr.closeSubscription(subscription); - - if ((getSubscribers().removeSubscriber(subscription)) == null) - { - throw new AMQException("Subscription " + subscription + " not registered with queue " + this); - } - - - setExclusive(false); - decrementSubscriberCount(); - - // if we are eligible for auto deletion, unregister from the queue registry - if (_autoDelete && getSubscribers().isEmpty()) - { - if (_logger.isInfoEnabled()) - { - _logger.info("Auto-deleteing queue:" + this); - } - - autodelete(); - // we need to manually fire the event to the removed subscription (which was the last one left for this - // queue. This is because the delete method uses the subscription set which has just been cleared - subscription.queueDeleted(this); - } - } - - public boolean isUnused() - { - return getSubscribers().isEmpty(); - } - - public boolean isEmpty() - { - return !_deliveryMgr.hasQueuedMessages(); - } - - public int delete() throws AMQException - { - if (!_deleted.getAndSet(true)) - { - getSubscribers().queueDeleted(this); - _bindings.deregister(); - _virtualHost.getQueueRegistry().unregisterQueue(_name); - _managedObject.unregister(); - for (Task task : _deleteTaskList) - { - task.doTask(this); - } - - _deleteTaskList.clear(); - } - return _deliveryMgr.getQueueMessageCount(); - } - - protected void autodelete() throws AMQException - { - if (_logger.isDebugEnabled()) - { - _logger.debug(MessageFormat.format("autodeleting {0}", this)); - } - - delete(); - } - - /*public void processGet(StoreContext storeContext, AMQMessage msg, boolean deliverFirst) throws AMQException - { - // fixme not sure what this is doing. should we be passing deliverFirst through here? - // This code is not used so when it is perhaps it should - _deliveryMgr.deliver(storeContext, getName(), msg, deliverFirst); - try - { - msg.immediateAndNotDelivered(); - updateReceivedMessageCount(msg); - } - catch (NoConsumersException e) - { - // as this message will be returned, it should be removed - // from the queue: - dequeue(storeContext, msg); - } - }*/ - - public void requeue(StoreContext storeContext, QueueEntry entry) throws AMQException - { - process(storeContext,entry,true); - } - - - public QueueEntry enqueue(StoreContext storeContext, AMQMessage message) throws AMQException - { - QueueEntryImpl entry = createEntry(message); - process(storeContext, entry, false); - return entry; - } - - // public DeliveryManager getDeliveryManager() - // { - // return _deliveryMgr; - // } - - public void process(StoreContext storeContext, QueueEntry entry, boolean deliverFirst) throws AMQException - { - AMQMessage msg = entry.getMessage(); - _deliveryMgr.deliver(storeContext, _name, entry, deliverFirst); - if(msg.immediateAndNotDelivered()) - { - dequeue(storeContext, entry); - } - else - { - updateReceivedMessageCount(entry); - } - } - - public void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException - { - try - { - AMQMessage msg = entry.getMessage(); - if(isDurable() && msg.isPersistent()) - { - _virtualHost.getMessageStore().dequeueMessage(storeContext, getName(), msg.getMessageId()); - } - - } - catch (MessageCleanupException e) - { - // Message was dequeued, but could not then be deleted - // though it is no longer referenced. This should be very - // rare and can be detected and cleaned up on recovery or - // done through some form of manual intervention. - _logger.error(e, e); - } - catch (AMQException e) - { - throw new FailedDequeueException(_name.toString(), e); - } - } - - public void deliverAsync() - { - _deliveryMgr.processAsync(_asyncDelivery); - } - - public SubscriptionManager getSubscribers() - { - return _deliveryMgr.getSubscribers(); - } - - protected void updateReceivedMessageCount(QueueEntry entry) throws AMQException - { - AMQMessage msg = entry.getMessage(); - - if (!msg.isRedelivered()) - { - _totalMessagesReceived.incrementAndGet(); - } - - try - { - _managedObject.checkForNotification(msg); - } - catch (JMException e) - { - throw new AMQException("Unable to get notification from manage queue: " + e, e); - } - } - - public boolean equals(Object o) - { - if (this == o) - { - return true; - } - - if ((o == null) || (getClass() != o.getClass())) - { - return false; - } - - final AMQQueue amqQueue = (AMQQueue) o; - - return (_name.equals(amqQueue.getName())); - } - - public int hashCode() - { - return _name.hashCode(); - } - - public String toString() - { - return _name.toString(); - } - - public boolean performGet(AMQProtocolSession session, AMQChannel channel, boolean acks) throws AMQException - { - return _deliveryMgr.performGet(session, channel, acks); - } - - public QueueRegistry getQueueRegistry() - { - return _virtualHost.getQueueRegistry(); - } - - public VirtualHost getVirtualHost() - { - return _virtualHost; - } - - public void addQueueDeleteTask(Task task) - { - _deleteTaskList.add(task); - } - - public boolean resend(final QueueEntry entry, final Subscription sub) - { - - - // Get the lock so we can tell if the sub scription has closed. - // will stop delivery to this subscription until the lock is released. - // note: this approach would allow the use of a single queue if the - // PreDeliveryQueue would allow head additions. - // In the Java Qpid client we are suspended whilst doing this so it is all rather Mute.. - // needs guidance from AMQP WG Model SIG - synchronized (sub.getSendLock()) - { - if (sub.isClosed()) - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Subscription(" + System.identityHashCode(sub) - + ") closed during resend so requeuing message"); - } - // move this message to requeue - return false; - - } - else - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Requeuing " + entry.debugIdentity() + " for resend via sub:" - + System.identityHashCode(sub)); - } - - entry.release(); - _deliveryMgr.resend(entry, sub); - return true; - } - } // sync(sub.getSendLock) - - - - - } - - public long getMinimumAlertRepeatGap() - { - return _minimumAlertRepeatGap; - } - - public void setMinimumAlertRepeatGap(long minimumAlertRepeatGap) - { - _minimumAlertRepeatGap = minimumAlertRepeatGap; - } - - public long getMaximumMessageAge() - { - return _maximumMessageAge; - } - - public void setMaximumMessageAge(long maximumMessageAge) - { - _maximumMessageAge = maximumMessageAge; - if(maximumMessageAge == 0L) - { - _notificationChecks.remove(NotificationCheck.MESSAGE_AGE_ALERT); - } - else - { - _notificationChecks.add(NotificationCheck.MESSAGE_AGE_ALERT); - } - } - - public void subscriberHasPendingResend(boolean hasContent, Subscription subscription, QueueEntry entry) - { - _deliveryMgr.subscriberHasPendingResend(hasContent, subscription, entry); - } - - public QueueEntryImpl createEntry(AMQMessage amqMessage) - { - return new QueueEntryImpl(this, amqMessage, _queueEntryId.getAndIncrement()); - } - - public int compareTo(Object o) - { - return _name.compareTo(((AMQQueue) o).getName()); - } - - - public void removeExpiredIfNoSubscribers() throws AMQException - { - synchronized(getSubscribers().getChangeLock()) - { - if(getSubscribers().isEmpty()) - { - _deliveryMgr.removeExpired(); - } - } - } - - public final Set<NotificationCheck> getNotificationChecks() - { - return _notificationChecks; - } - - public void stateChange(Subscription sub, Subscription.State oldState, Subscription.State newState) - { - if(newState == Subscription.State.ACTIVE) - { - deliverAsync(); - } - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java deleted file mode 100644 index becd57752e..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java +++ /dev/null @@ -1,1098 +0,0 @@ -/* - * - * 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.qpid.server.queue; - -import org.apache.log4j.Logger; -import org.apache.qpid.AMQException; -import org.apache.qpid.configuration.Configured; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.configuration.Configurator; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.store.StoreContext; -import org.apache.qpid.util.ConcurrentLinkedMessageQueueAtomicSize; -import org.apache.qpid.util.MessageQueue; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.ReentrantLock; - - -/** Manages delivery of messages on behalf of a queue */ -public class ConcurrentSelectorDeliveryManager -{ - private static final Logger _log = Logger.getLogger(ConcurrentSelectorDeliveryManager.class); - - @Configured(path = "advanced.compressBufferOnQueue", - defaultValue = "false") - public boolean compressBufferOnQueue; - /** Holds any queued messages */ - private final MessageQueue<QueueEntry> _messages = new ConcurrentLinkedMessageQueueAtomicSize<QueueEntry>(); - - /** Ensures that only one asynchronous task is running for this manager at any time. */ - private final AtomicBoolean _processing = new AtomicBoolean(); - /** The subscriptions on the queue to whom messages are delivered */ - private final SubscriptionSet _subscriptions = new SubscriptionSet(); - - /** - * A reference to the queue we are delivering messages for. We need this to be able to pass the code that handles - * acknowledgements a handle on the queue. - */ - private final AMQQueueImpl _queue; - - /** - * Flag used while moving messages from this queue to another. For moving messages the async delivery should also - * stop. This flat should be set to true to stop async delivery and set to false to enable async delivery again. - */ - private AtomicBoolean _movingMessages = new AtomicBoolean(); - - /** - * Lock used to ensure that an channel that becomes unsuspended during the start of the queueing process is forced - * to wait till the first message is added to the queue. This will ensure that the _queue has messages to be - * delivered via the async thread. <p/> Lock is used to control access to hasQueuedMessages() and over the addition - * of messages to the queue. - */ - private ReentrantLock _lock = new ReentrantLock(); - private AtomicLong _totalMessageSize = new AtomicLong(); - private AtomicInteger _extraMessages = new AtomicInteger(); - private Set<DeliveryAgent> _hasContent = Collections.synchronizedSet(new HashSet<DeliveryAgent>()); - private final Object _queueHeadLock = new Object(); - private String _processingThreadName = ""; - - - /** Used by any reaping thread to purge messages */ - private StoreContext _reapingStoreContext = new StoreContext(); - - ConcurrentSelectorDeliveryManager(AMQQueueImpl queue) - { - - //Set values from configuration - Configurator.configure(this); - - if (compressBufferOnQueue) - { - _log.warn("Compressing Buffers on queue."); - } - - _queue = queue; - } - - - public SubscriptionSet getSubscribers() - { - return _subscriptions; - } - - - private boolean addMessageToQueue(QueueEntry entry, boolean deliverFirst) - { - AMQMessage msg = entry.getMessage(); - // Shrink the ContentBodies to their actual size to save memory. - if (compressBufferOnQueue) - { - Iterator<ContentChunk> it = msg.getContentBodyIterator(); - while (it.hasNext()) - { - ContentChunk cb = it.next(); - cb.reduceToFit(); - } - } - - if (deliverFirst) - { - synchronized (_queueHeadLock) - { - _messages.pushHead(entry); - } - } - else - { - _messages.offer(entry); - } - - _totalMessageSize.addAndGet(msg.getSize()); - - return true; - } - - - public boolean hasQueuedMessages() - { - _lock.lock(); - try - { - return !(_messages.isEmpty() && _hasContent.isEmpty()); - } - finally - { - _lock.unlock(); - } - } - - public int getQueueMessageCount() - { - return getMessageCount(); - } - - /** - * This is an EXPENSIVE opperation to perform with a ConcurrentLinkedQueue as it must run the queue to determine - * size. The ConcurrentLinkedQueueAtomicSize uses an AtomicInteger to record the number of elements on the queue. - * - * @return int the number of messages in the delivery queue. - */ - private int getMessageCount() - { - return _messages.size() + _extraMessages.get(); - } - - - public long getTotalMessageSize() - { - return _totalMessageSize.get(); - } - - public long getOldestMessageArrival() - { - QueueEntry entry = _messages.peek(); - return entry == null ? Long.MAX_VALUE : entry.getMessage().getArrivalTime(); - } - - public void subscriberHasPendingResend(boolean hasContent, Subscription subscription, QueueEntry entry) - { - subscriberHasPendingResend(hasContent,_subscriptions.getDeliveryAgent(subscription),entry); - } - private void subscriberHasPendingResend(boolean hasContent, DeliveryAgent deliveryAgent, QueueEntry entry) - { - _lock.lock(); - try - { - if (hasContent) - { - _log.debug("Queue has adding subscriber content"); - _hasContent.add(deliveryAgent); - _totalMessageSize.addAndGet(entry.getSize()); - _extraMessages.addAndGet(1); - } - else - { - _log.debug("Queue has removing subscriber content"); - if (entry == null) - { - _hasContent.remove(deliveryAgent); - } - else - { - _totalMessageSize.addAndGet(-entry.getSize()); - _extraMessages.addAndGet(-1); - } - } - } - finally - { - _lock.unlock(); - } - } - - /** - * NOTE : This method should only be called when there are no active subscribers - */ - public void removeExpired() throws AMQException - { - _lock.lock(); - - - for(Iterator<QueueEntry> iter = _messages.iterator(); iter.hasNext();) - { - QueueEntry entry = iter.next(); - if(entry.expired()) - { - // fixme: Currently we have to update the total byte size here for the data in the queue - _totalMessageSize.addAndGet(-entry.getSize()); - _queue.dequeue(_reapingStoreContext,entry); - iter.remove(); - } - } - - - _lock.unlock(); - } - - /** @return the state of the async processor. */ - public boolean isProcessingAsync() - { - return _processing.get(); - } - - /** - * Returns all the messages in the Queue - * - * @return List of messages - */ - public List<QueueEntry> getMessages() - { - _lock.lock(); - List<QueueEntry> list = new ArrayList<QueueEntry>(); - - for (QueueEntry entry : _messages) - { - list.add(entry); - } - _lock.unlock(); - - return list; - } - - /** - * Returns messages within the range of given messageIds - * - * @param fromMessageId - * @param toMessageId - * - * @return - */ - public List<QueueEntry> getMessages(long fromMessageId, long toMessageId) - { - if (fromMessageId <= 0 || toMessageId <= 0) - { - return null; - } - - long maxMessageCount = toMessageId - fromMessageId + 1; - - _lock.lock(); - - List<QueueEntry> foundMessagesList = new ArrayList<QueueEntry>(); - - for (QueueEntry entry : _messages) - { - long msgId = entry.getMessage().getMessageId(); - if (msgId >= fromMessageId && msgId <= toMessageId) - { - foundMessagesList.add(entry); - } - // break if the no of messages are found - if (foundMessagesList.size() == maxMessageCount) - { - break; - } - } - _lock.unlock(); - - return foundMessagesList; - } - - public void populatePreDeliveryQueue(Subscription subscription) - { - populatePreDeliveryQueue(_subscriptions.getDeliveryAgent(subscription)); - } - - private void populatePreDeliveryQueue(DeliveryAgent deliveryAgent) - { - - Iterator<QueueEntry> currentQueue = _messages.iterator(); - - while (currentQueue.hasNext()) - { - QueueEntry entry = currentQueue.next(); - - if (deliveryAgent != null && deliveryAgent.hasInterest(entry)) - { - deliveryAgent.enqueueForPreDelivery(entry, false); - } - - } - } - - public boolean performGet(AMQProtocolSession protocolSession, AMQChannel channel, boolean acks) throws AMQException - { - QueueEntry entry = getNextMessage(); - if (entry == null) - { - return false; - } - else - { - - try - { - // if we do not need to wait for client acknowledgements - // we can decrement the reference count immediately. - - // By doing this _before_ the send we ensure that it - // doesn't get sent if it can't be dequeued, preventing - // duplicate delivery on recovery. - - // The send may of course still fail, in which case, as - // the message is unacked, it will be lost. - if (!acks) - { - if (_log.isDebugEnabled()) - { - _log.debug("No ack mode so dequeuing message immediately: " + entry.getMessage().getMessageId()); - } - entry.dequeue(channel.getStoreContext()); - } - synchronized (channel) - { - long deliveryTag = channel.getNextDeliveryTag(); - - if (acks) - { - channel.addUnacknowledgedMessage(entry, deliveryTag, null); - } - - protocolSession.getProtocolOutputConverter().writeGetOk(entry.getMessage(), channel.getChannelId(), - deliveryTag, _queue.getMessageCount()); - - } - _totalMessageSize.addAndGet(-entry.getSize()); - - if (!acks) - { - entry.dispose(channel.getStoreContext()); - } - } - finally - { - entry.setDeliveredToSubscription(); - } - return true; - - } - } - - /** - * For feature of moving messages, this method is used. It sets the lock and sets the movingMessages flag, so that - * the asyn delivery is also stopped. - */ - public void startMovingMessages() - { - _movingMessages.set(true); - } - - /** - * Once moving messages to another queue is done or aborted, remove lock and unset the movingMessages flag, so that - * the async delivery can start again. - */ - public void stopMovingMessages() - { - _movingMessages.set(false); - if (_lock.isHeldByCurrentThread()) - { - _lock.unlock(); - } - } - - /** - * Messages will be removed from this queue and all preDeliveryQueues - * - * @param messageList - */ - public void removeMovedMessages(List<QueueEntry> messageList) - { - // Remove from the - boolean hasSubscribers = _subscriptions.hasActiveSubscribers(); - if (hasSubscribers) - { - for (Subscription sub : _subscriptions.getSubscriptions()) - { - DeliveryAgent deliveryAgent = _subscriptions.getDeliveryAgent(sub); - if (deliveryAgent != null && !sub.isSuspended() && sub.filtersMessages()) - { - Queue<QueueEntry> preDeliveryQueue = deliveryAgent.getPreDeliveryQueue(); - for (QueueEntry entry : messageList) - { - preDeliveryQueue.remove(entry); - } - } - } - } - - for (QueueEntry entry : messageList) - { - if (_messages.remove(entry)) - { - _totalMessageSize.getAndAdd(-entry.getSize()); - } - } - } - - /** - * Now with implementation of predelivery queues, this method will mark the message on the top as taken. - * - * @param storeContext - * - * @throws AMQException - */ - public void removeAMessageFromTop(StoreContext storeContext, AMQQueue queue) throws AMQException - { - _lock.lock(); - - QueueEntry entry = _messages.poll(); - - if (entry != null) - { - entry.dequeue(storeContext); - - _totalMessageSize.addAndGet(-entry.getSize()); - - //If this causes ref count to hit zero then data will be purged so message.getSize() will NPE. - entry.dispose(storeContext); - - } - - _lock.unlock(); - } - - public long clearAllMessages(StoreContext storeContext) throws AMQException - { - long count = 0; - _lock.lock(); - - synchronized (_queueHeadLock) - { - QueueEntry entry = getNextMessage(); - while (entry != null) - { - //and remove it - _messages.poll(); - - entry.dequeue(storeContext); - - entry.dispose(storeContext); - - entry = getNextMessage(); - count++; - } - _totalMessageSize.set(0L); - } - _lock.unlock(); - return count; - } - - /** - * This can only be used to clear the _messages queue. Any subscriber resend queue will not be purged. - * - * @return the next message or null - * - * @throws org.apache.qpid.AMQException - */ - private QueueEntry getNextMessage() throws AMQException - { - return getNextMessage(_messages, null, false); - } - - private QueueEntry getNextMessage(Queue<QueueEntry> messages, Subscription sub, boolean purgeOnly) throws AMQException - { - QueueEntry entry = messages.peek(); - - //while (we have a message) && ((The subscriber is not a browser or message is taken ) or we are clearing) && (Check message is taken.) - while (purgeMessage(entry, sub, purgeOnly)) - { - - //remove the already taken message or expired - QueueEntry removed = messages.poll(); - - assert removed == entry; - - // if the message expired then the _totalMessageSize needs adjusting - if (entry.expired() && !entry.acquire(sub)) - { - _totalMessageSize.addAndGet(-entry.getSize()); - - // Use the reapingStoreContext as any sub(if we have one) may be in a tx. - entry.dequeue(_reapingStoreContext); - entry.dispose(_reapingStoreContext); - - if (_log.isInfoEnabled()) - { - _log.info(debugIdentity() + " Doing clean up of the main _message queue."); - } - } - - //else the clean up is not required as the message has already been taken for this queue therefore - // it was the responsibility of the code that took the message to ensure the _totalMessageSize was updated. - - if (_log.isDebugEnabled()) - { - _log.debug("Removed taken message:" + entry.debugIdentity()); - } - - // try the next message - entry = messages.peek(); - } - - return entry; - } - - /** - * This method will return true if the message is to be purged from the queue. - * \ - * SIDE-EFFECT: The msg will be taken by the Subscription(sub) for the current Queue(_queue) when purgeOnly is false - * - * @param message - * @param sub - * @param purgeOnly When set to false the message will be taken by the given Subscription. - * - * @return if the msg should be purged - * - * @throws AMQException - */ - private boolean purgeMessage(QueueEntry message, Subscription sub, boolean purgeOnly) throws AMQException - { - //Original.. complicated while loop control -// (message != null -// && ( -// ((sub != null && !sub.isBrowser()) || message.isTaken(_queue)) -// || sub == null) -// && message.taken(_queue, sub)); - - boolean purge = false; - - // if the message is null then don't purge as we have no messagse. - if (message != null) - { - // Check that the message hasn't expired. - if (message.expired()) - { - return true; - } - - // if we have a subscriber perform message checks - if (sub != null) - { - // if we have a queue browser(we don't purge) so check mark the message as taken - purge = ((!sub.isBrowser() || message.isAcquired())); - } - else - { - // if there is no subscription we are doing - // a get or purging so mark message as taken. - message.isAcquired(); - // and then ensure that it gets purged - purge = true; - } - } - - if (purgeOnly) - { - // If we are simply purging the queue don't take the message - // just purge up to the next non-taken msg. - return purge && message.isAcquired(); - } - else - { - // if we are purging then ensure we mark this message taken for the current subscriber - // the current subscriber may be null in the case of a get or a purge but this is ok. - return purge && message.acquire(sub); - } - } - - public void sendNextMessage(DeliveryAgent sub) - { - - Queue<QueueEntry> messageQueue = sub.getNextQueue(_messages); - - if (_log.isDebugEnabled()) - { - _log.debug(debugIdentity() + "Async sendNextMessage for sub (" + System.identityHashCode(sub) + - ") from queue (" + System.identityHashCode(messageQueue) + - ") AMQQueue (" + System.identityHashCode(_queue) + ")"); - } - - if (messageQueue == null) - { - // There is no queue with messages currently. This is ok... just means the queue has no msgs matching selector - if (_log.isInfoEnabled()) - { - _log.info(debugIdentity() + sub + ": asked to send messages but has none on given queue:" + _queue); - } - return; - } - - QueueEntry entry = null; - QueueEntry removed; - try - { - synchronized (_queueHeadLock) - { - entry = getNextMessage(messageQueue, sub.getSubscription(), false); - - // message will be null if we have no messages in the messageQueue. - if (entry == null) - { - if (_log.isDebugEnabled()) - { - _log.debug(debugIdentity() + "No messages for Subscriber(" + System.identityHashCode(sub) + ") from queue; (" + System.identityHashCode(messageQueue) + ")"); - } - return; - } - if (_log.isDebugEnabled()) - { - _log.debug(debugIdentity() + "Async Delivery Message :" + entry + "(" + System.identityHashCode(entry) + - ") by :" + System.identityHashCode(this) + - ") to :" + System.identityHashCode(sub)); - } - - - if (messageQueue == _messages) - { - _totalMessageSize.addAndGet(-entry.getSize()); - } - - sub.send(entry); - - //remove sent message from our queue. - removed = messageQueue.poll(); - //If we don't remove the message from _messages - // Otherwise the Async send will never end - } - - if (removed != entry) - { - _log.error("Just send message:" + entry.getMessage().debugIdentity() + " BUT removed this from queue:" + removed); - } - - if (_log.isDebugEnabled()) - { - _log.debug(debugIdentity() + "Async Delivered Message r:" + removed.getMessage().debugIdentity() + "d:" + entry + - ") by :" + System.identityHashCode(this) + - ") to :" + System.identityHashCode(sub)); - } - - - if (messageQueue == sub.getResendQueue()) - { - if (_log.isDebugEnabled()) - { - _log.debug(debugIdentity() + "All messages sent from resendQueue for " + sub); - } - if (messageQueue.isEmpty()) - { - subscriberHasPendingResend(false, sub, null); - //better to use the above method as this keeps all the tracking in one location. - // _hasContent.remove(sub); - } - - _extraMessages.decrementAndGet(); - } - else if (messageQueue == sub.getPreDeliveryQueue() && !sub.isBrowser()) - { - if (_log.isInfoEnabled()) - { - //fixme - we should do the clean up as the message remains on the _message queue - // this is resulting in the next consumer receiving the message and then attempting to purge it - // - cleanMainQueue(sub); - } - } - - } - catch (AMQException e) - { - if (entry != null) - { - entry.release(); - } - else - { - _log.error(debugIdentity() + "Unable to release message as it is null. " + e, e); - } - _log.error(debugIdentity() + "Unable to deliver message as dequeue failed: " + e, e); - } - } - - private void cleanMainQueue(DeliveryAgent sub) - { - try - { - getNextMessage(_messages, sub.getSubscription(), true); - } - catch (AMQException e) - { - _log.warn("Problem during main queue purge:" + e.getMessage()); - } - } - - /** - * enqueues the messages in the list on the queue and all required predelivery queues - * - * @param storeContext - * @param movedMessageList - */ - public void enqueueMovedMessages(StoreContext storeContext, List<QueueEntry> movedMessageList) - { - _lock.lock(); - for (QueueEntry entry : movedMessageList) - { - addMessageToQueue(entry, false); - } - - // enqueue on the pre delivery queues - for (Subscription sub : _subscriptions.getSubscriptions()) - { - for (QueueEntry entry : movedMessageList) - { - // Only give the message to those that want them. - if (sub.hasInterest(entry)) - { - DeliveryAgent deliveryAgent = _subscriptions.getDeliveryAgent(sub); - if(deliveryAgent != null) - { - deliveryAgent.enqueueForPreDelivery(entry, true); - } - } - } - } - _lock.unlock(); - } - - /** - * Only one thread should ever execute this method concurrently, but it can do so while other threads invoke - * deliver(). - */ - private void processQueue() - { - //record thread name - if (_log.isDebugEnabled()) - { - _processingThreadName = Thread.currentThread().getName(); - } - - if (_log.isDebugEnabled()) - { - _log.debug(debugIdentity() + "Running process Queue." + currentStatus()); - } - - // Continue to process delivery while we haveSubscribers and messages - boolean hasSubscribers = _subscriptions.hasActiveSubscribers(); - - while (hasSubscribers && hasQueuedMessages() && !_movingMessages.get()) - { - hasSubscribers = false; - - for (Subscription sub : _subscriptions.getSubscriptions()) - { - DeliveryAgent da = _subscriptions.getDeliveryAgent(sub); - if(da != null) - { - synchronized (da.getSendLock()) - { - if (!sub.isSuspended()) - { - sendNextMessage(da); - - hasSubscribers = true; - } - } - } - } - } - - if (_log.isDebugEnabled()) - { - _log.debug(debugIdentity() + "Done process Queue." + currentStatus()); - } - - } - - public void deliver(StoreContext context, AMQShortString name, QueueEntry entry, boolean deliverFirst) throws AMQException - { - - final boolean debugEnabled = _log.isDebugEnabled(); - if (debugEnabled) - { - _log.debug(debugIdentity() + "deliver :first(" + deliverFirst + ") :" + entry); - } - - //Check if we have someone to deliver the message to. - _lock.lock(); - try - { - Subscription s = _subscriptions.nextSubscriber(entry); - DeliveryAgent da = (s==null) ? null : _subscriptions.getDeliveryAgent(s); - - - if (s == null || (!s.filtersMessages() && hasQueuedMessages())) //no-one can take the message right now or we're queueing - { - if (debugEnabled) - { - _log.debug(debugIdentity() + "Testing Message(" + entry + ") for Queued Delivery:" + currentStatus()); - } - if (!entry.getMessage().getMessagePublishInfo().isImmediate()) - { - addMessageToQueue(entry, deliverFirst); - - //release lock now message is on queue. - _lock.unlock(); - - //Pre Deliver to all subscriptions - if (debugEnabled) - { - _log.debug(debugIdentity() + "We have " + _subscriptions.getSubscriptions().size() + - " subscribers to give the message to:" + currentStatus()); - } - for (Subscription sub : _subscriptions.getSubscriptions()) - { - DeliveryAgent deliveryAgent = (s==null) ? null : _subscriptions.getDeliveryAgent(sub); - // Only give the message to those that want them. - if (deliveryAgent != null && sub.hasInterest(entry)) - { - if (debugEnabled) - { - _log.debug(debugIdentity() + "Queuing message(" + System.identityHashCode(entry) + - ") for PreDelivery for subscriber(" + System.identityHashCode(sub) + ")"); - } - deliveryAgent.enqueueForPreDelivery(entry, deliverFirst); - } - } - - //if we have a non-filtering subscriber but queued messages && we're not Async && we have other Active subs then something is wrong! - if ((s != null && hasQueuedMessages()) && !isProcessingAsync() && _subscriptions.hasActiveSubscribers()) - { - _queue.deliverAsync(); - } - - } - } - else - { - - if (s.filtersMessages()) - { - if (da.getPreDeliveryQueue().size() > 0) - { - _log.error("Direct delivery from PDQ with queued msgs:" + da.getPreDeliveryQueue().size()); - } - } - else if (_messages.size() > 0) - { - _log.error("Direct delivery from MainQueue queued msgs:" + _messages.size()); - } - - //release lock now - _lock.unlock(); - Object sendLock = (da == null) ? new Object() : da.getSendLock(); - synchronized (sendLock) - { - if (!s.isSuspended()) - { - if (debugEnabled) - { - _log.debug(debugIdentity() + "Delivering Message:" + entry.getMessage().debugIdentity() + " to(" + - System.identityHashCode(s) + ") :" + s); - } - - if (entry.acquire(s)) - { - //Message has been delivered so don't redeliver. - // This can currently occur because of the recursive call below - // During unit tests the send can occur - // client then rejects - // this reject then releases the message by the time the - // if(!msg.isTaken()) call is made below - // the message has been released so that thread loops to send the message again - // of course by the time it gets back to here. the thread that released the - // message is now ready to send it. Here is a sample trace for reference -//1192627162613:Thread[pool-917-thread-4,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false] -//1192627162613:Thread[pool-917-thread-4,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null} -//1192627162613:Thread[pool-917-thread-4,5,main]:28398657 Sent :dt:214 msg:(HC:5529738 ID:145 Ref:1) -//1192627162613:Thread[pool-917-thread-2,5,main]:Reject message by:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false] -//1192627162613:Thread[pool-917-thread-2,5,main]:Releasing Message:(HC:5529738 ID:145 Ref:1) -//1192627162613:Thread[pool-917-thread-2,5,main]:Msg:Release:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:This:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]} -//1192627162613:Thread[pool-917-thread-2,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false] -//1192627162629:Thread[pool-917-thread-4,5,main]:CSDM:suspended: Message((HC:5529738 ID:145 Ref:1)) has not been taken so recursing!: Subscriber:28398657 -//1192627162629:Thread[pool-917-thread-4,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false] -//1192627162629:Thread[pool-917-thread-2,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null} -//1192627162629:Thread[pool-917-thread-2,5,main]:25386607 Sent :dt:172 msg:(HC:5529738 ID:145 Ref:1) -//1192627162629:Thread[pool-917-thread-4,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=true} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]} - // Note: In the last request to take the message from thread 4,5 the message has been - // taken by the previous call done by thread 2,5 - - - return; - } - //Deliver the message - da.send(entry); - } - else - { - if (debugEnabled) - { - _log.debug(debugIdentity() + " Subscription(" + System.identityHashCode(s) + ") became " + - "suspended between nextSubscriber and send for message:" + entry.getMessage().debugIdentity()); - } - } - } - - // - // Why do we do this? What was the reasoning? We should have a better approach - // than recursion and rejecting if someone else sends it before we do. - // - if (!entry.isAcquired()) - { - if (debugEnabled) - { - _log.debug(debugIdentity() + " Message(" + entry.getMessage().debugIdentity() + ") has not been taken so recursing!:" + - " Subscriber:" + System.identityHashCode(s)); - } - - deliver(context, name, entry, deliverFirst); - } - else - { - if (debugEnabled) - { - _log.debug(debugIdentity() + " Message(" + entry.toString() + - ") has been taken so disregarding deliver request to Subscriber:" + - System.identityHashCode(s)); - } - } - } - - } - finally - { - //ensure lock is released - if (_lock.isHeldByCurrentThread()) - { - _lock.unlock(); - } - } - } - - private final String id = "(" + String.valueOf(System.identityHashCode(this)) + ")"; - - private String debugIdentity() - { - return id; - } - - final Runner _asyncDelivery = new Runner(); - - public void debug() - { - System.err.println(_extraMessages); - System.err.println(_messages); - - } - - public void start(final Subscription subscription) - { - DeliveryAgent da = _subscriptions.getDeliveryAgent(subscription); - if(da != null) - { - da.start(); - } - } - - - private class Runner implements Runnable - { - public void run() - { - String startName = Thread.currentThread().getName(); - Thread.currentThread().setName("CSDM-AsyncDelivery:" + startName); - boolean running = true; - while (running && !_movingMessages.get()) - { - processQueue(); - - //Check that messages have not been added since we did our last peek(); - // Synchronize with the thread that adds to the queue. - // If the queue is still empty then we can exit - synchronized (_asyncDelivery) - { - if (!(hasQueuedMessages() && _subscriptions.hasActiveSubscribers())) - { - running = false; - _processing.set(false); - } - } - } - Thread.currentThread().setName(startName); - } - } - - public void processAsync(Executor executor) - { - if (_log.isDebugEnabled()) - { - _log.debug(debugIdentity() + "Processing Async." + currentStatus()); - } - - synchronized (_asyncDelivery) - { - if (hasQueuedMessages() && _subscriptions.hasActiveSubscribers()) - { - //are we already running? if so, don't re-run - if (_processing.compareAndSet(false, true)) - { - if (_log.isDebugEnabled()) - { - _log.debug(debugIdentity() + "Executing Async process."); - } - executor.execute(_asyncDelivery); - } - } - } - } - - public void closeSubscription(final Subscription subscription) - { - _subscriptions.getDeliveryAgent(subscription).close(); - subscription.close(); - } - - - public void resend(final QueueEntry entry, final Subscription subscription) - { - final DeliveryAgent deliveryAgent = _subscriptions.getDeliveryAgent(subscription); - deliveryAgent.addToResendQueue(entry); - - } - - - private String currentStatus() - { - return " Queued:" + (_messages.isEmpty() ? "Empty " : "Contains(H:M)") + - "(" + ((ConcurrentLinkedMessageQueueAtomicSize) _messages).headSize() + - ":" + (_messages.size() - ((ConcurrentLinkedMessageQueueAtomicSize) _messages).headSize()) + ") " + - " Extra: " + (_hasContent.isEmpty() ? "Empty " : "Contains") + - "(" + _hasContent.size() + ":" + _extraMessages.get() + ") " + - " Active:" + _subscriptions.hasActiveSubscribers() + - " Processing:" + (_processing.get() ? " true : Processing Thread: " + _processingThreadName : " false"); - } - - - -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryAgent.java b/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryAgent.java deleted file mode 100644 index a43e552cef..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryAgent.java +++ /dev/null @@ -1,344 +0,0 @@ -package org.apache.qpid.server.queue; - -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.output.ProtocolOutputConverter; -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.util.MessageQueue; -import org.apache.qpid.util.ConcurrentLinkedMessageQueueAtomicSize; -import org.apache.qpid.util.ConcurrentLinkedQueueAtomicSize; -import org.apache.log4j.Logger; - -import java.util.Queue; - -/* -* -* 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. -* -*/ -public class DeliveryAgent -{ - - private static final Logger _logger = Logger.getLogger(DeliveryAgent.class); - - private final Subscription _subscription; - - - - private MessageQueue<QueueEntry> _messages; - - private Queue<QueueEntry> _resendQueue; - - private boolean _sentClose = false; - - public DeliveryAgent(final Subscription subscription) - { - _subscription = subscription; - - if (filtersMessages()) - { - _messages = new ConcurrentLinkedMessageQueueAtomicSize<QueueEntry>(); - } - else - { - // Reference the DeliveryManager - _messages = null; - } - } - - - public Subscription getSubscription() - { - return _subscription; - } - - - public AMQQueue getQueue() - { - return _subscription.getQueue(); - } - - public void setQueue(final AMQQueue queue) - { - _subscription.setQueue(queue); - } - - public AMQChannel getChannel() - { - return _subscription.getChannel(); - } - - public boolean isSuspended() - { - return _subscription.isSuspended(); - } - - public boolean hasInterest(final QueueEntry msg) - { - return _subscription.hasInterest(msg); - } - - public boolean isAutoClose() - { - return _subscription.isAutoClose(); - } - - public boolean isClosed() - { - return _subscription.isClosed(); - } - - public boolean isBrowser() - { - return _subscription.isBrowser(); - } - - public void close() - { - _subscription.close(); - - if (_resendQueue != null && !_resendQueue.isEmpty()) - { - if (_logger.isInfoEnabled()) - { - _logger.info("Requeuing closing subscription " + this); - } - requeue(); - } - - //remove references in PDQ - if (_messages != null) - { - if (_logger.isInfoEnabled()) - { - _logger.info("Clearing PDQ " + this); - } - - _messages.clear(); - } - } - - public boolean filtersMessages() - { - return _subscription.filtersMessages(); - } - - public void send(final QueueEntry msg) - throws AMQException - { - _subscription.send(msg); - } - - public void queueDeleted(final AMQQueue queue) - { - _subscription.queueDeleted(queue); - } - - public Queue<QueueEntry> getPreDeliveryQueue() - { - return _messages; - } - - public Queue<QueueEntry> getResendQueue() - { - if (_resendQueue == null) - { - _resendQueue = new ConcurrentLinkedQueueAtomicSize<QueueEntry>(); - } - return _resendQueue; - } - - public Queue<QueueEntry> getNextQueue(final Queue<QueueEntry> messages) - { - if (_resendQueue != null && !_resendQueue.isEmpty()) - { - return _resendQueue; - } - - if (filtersMessages()) - { - if (isAutoClose()) - { - if (_messages.isEmpty()) - { - autoclose(); - return null; - } - } - return _messages; - } - else // we want the DM queue - { - return messages; - } - } - - - private void autoclose() - { - close(); - - if (isAutoClose() && !_sentClose) - { - _logger.info("Closing autoclose subscription" + this); - - ProtocolOutputConverter converter = getChannel().getProtocolSession().getProtocolOutputConverter(); - converter.confirmConsumerAutoClose(getChannel().getChannelId(), getConsumerTag()); - _sentClose = true; - - //fixme JIRA do this better - try - { - getChannel().unsubscribeConsumer(getConsumerTag()); - } - catch (AMQException e) - { - // Occurs if we cannot find the subscriber in the channel with protocolSession and consumerTag. - } - } - } - - - public void enqueueForPreDelivery(final QueueEntry msg, final boolean deliverFirst) - { - if (_messages != null) - { - if (deliverFirst) - { - _messages.pushHead(msg); - } - else - { - _messages.offer(msg); - } - } - - } - - public boolean wouldSuspend(final QueueEntry msg) - { - return _subscription.wouldSuspend(msg); - } - - public void addToResendQueue(final QueueEntry msg) - { - // add to our resend queue - getResendQueue().add(msg); - - // Mark Queue has having content. - if (getQueue() == null) - { - _logger.error("Queue is null won't be able to resend messages"); - } - else - { - ((AMQQueueImpl) getQueue()).subscriberHasPendingResend(true, getSubscription(), msg); - } - } - - public Object getSendLock() - { - return _subscription.getSendLock(); - } - - private void requeue() - { - if (getQueue() != null) - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Requeuing :" + _resendQueue.size() + " messages"); - } - - while (!_resendQueue.isEmpty()) - { - QueueEntry resent = _resendQueue.poll(); - - if (_logger.isTraceEnabled()) - { - _logger.trace("Removed for resending:" + resent.debugIdentity()); - } - - resent.release(); - ((AMQQueueImpl) getQueue()).subscriberHasPendingResend(false, getSubscription(), resent); - - try - { - getChannel().getTransactionalContext().requeue(resent); - } - catch (AMQException e) - { - _logger.error("MESSAGE LOSS : Unable to re-deliver messages", e); - } - } - - if (!_resendQueue.isEmpty()) - { - _logger.error("[MESSAGES LOST]Unable to re-deliver messages as queue is null."); - } - - ((AMQQueueImpl) getQueue()).subscriberHasPendingResend(false, getSubscription(), null); - } - else - { - if (!_resendQueue.isEmpty()) - { - _logger.error("Unable to re-deliver messages as queue is null."); - } - } - - // Clear the messages - _resendQueue = null; - } - - - public String toString() - { - return "[Delivery Agent for: "+getSubscription()+"]"; - } - - public AMQShortString getConsumerTag() - { - return getSubscription().getConumerTag(); - } - - boolean ableToDeliver() - { - // if the queue is not empty then this client is ready to receive a message. - //FIXME the queue could be full of sent messages. - // Either need to clean all PDQs after sending a message - // OR have a clean up thread that runs the PDQs expunging the messages. - return (!_subscription.filtersMessages() || getPreDeliveryQueue().isEmpty()); - } - - public void start() - { - // Check to see if we need to autoclose - if (filtersMessages()) - { - if (isAutoClose()) - { - if (_messages.isEmpty()) - { - autoclose(); - } - } - } - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/Filterable.java b/java/broker/src/main/java/org/apache/qpid/server/queue/Filterable.java new file mode 100644 index 0000000000..d38932bb61 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/Filterable.java @@ -0,0 +1,33 @@ +/* +* +* 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.qpid.server.queue; + +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.AMQException; + +public interface Filterable<E extends Exception> +{ + ContentHeaderBody getContentHeaderBody() throws E; + + boolean isPersistent() throws E; + + boolean isRedelivered(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java index 2dd9f14574..6a3e7e49ef 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java @@ -38,8 +38,9 @@ import org.apache.log4j.Logger; import java.util.List; import java.util.ArrayList; +import java.util.Collection; -public class IncomingMessage +public class IncomingMessage implements Filterable<RuntimeException> { /** Used for debugging purposes. */ @@ -66,7 +67,7 @@ public class IncomingMessage * delivered. It is <b>cleared after delivery has been attempted</b>. Any persistent record of destinations is done * by the message handle. */ - private List<AMQQueue> _destinationQueues; + private Collection<AMQQueue> _destinationQueues; private AMQProtocolSession _publisher; private MessageStore _messageStore; @@ -160,7 +161,7 @@ public class IncomingMessage // we get a reference to the destination queues now so that we can clear the // transient message data as quickly as possible - List<AMQQueue> destinationQueues = _destinationQueues; + Collection<AMQQueue> destinationQueues = _destinationQueues; if (_logger.isDebugEnabled()) { _logger.debug("Delivering message " + _messageId + " to " + destinationQueues); @@ -175,9 +176,6 @@ public class IncomingMessage _messageHandle.setPublishAndContentHeaderBody(_txnContext.getStoreContext(), _messagePublishInfo, getContentHeaderBody()); - // we then allow the transactional context to do something with the message content - // now that it has all been received, before we attempt delivery - _txnContext.messageFullyReceived(isPersistent()); message = new AMQMessage(_messageHandle,_txnContext.getStoreContext(), _messagePublishInfo); message.setPublisherIdentifier(_publisher.getClientIdentifier()); @@ -214,6 +212,10 @@ public class IncomingMessage } } + // we then allow the transactional context to do something with the message content + // now that it has all been received, before we attempt delivery + _txnContext.messageFullyReceived(isPersistent()); + return message; } finally @@ -283,6 +285,11 @@ public class IncomingMessage ((BasicContentHeaderProperties) getContentHeaderBody().properties).getDeliveryMode() == 2; } + public boolean isRedelivered() + { + return false; + } + public void setMessageStore(final MessageStore messageStore) { _messageStore = messageStore; @@ -303,7 +310,7 @@ public class IncomingMessage _exchange.route(this); } - public void enqueue(final List<AMQQueue> queues) + public void enqueue(final Collection<AMQQueue> queues) { _destinationQueues = queues; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java index 061ab56024..2bc94995e9 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java @@ -126,7 +126,7 @@ public interface ManagedQueue * @param age maximum age of message. * @throws IOException */ - @MBeanAttribute(name="MaximumMessageAge", description="Threshold high value for message age on thr broker") + @MBeanAttribute(name="MaximumMessageAge", description="Threshold high value for message age on the broker") void setMaximumMessageAge(Long age) throws IOException; /** diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java b/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java new file mode 100644 index 0000000000..e6628832cb --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java @@ -0,0 +1,164 @@ +/* +* +* 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.qpid.server.queue; + +import org.apache.qpid.framing.CommonContentHeaderProperties; +import org.apache.qpid.AMQException; + +public class PriorityQueueList implements QueueEntryList +{ + private final AMQQueue _queue; + private final QueueEntryList[] _priorityLists; + private final int _priorities; + private final int _priorityOffset; + + public PriorityQueueList(AMQQueue queue, int priorities) + { + _queue = queue; + _priorityLists = new QueueEntryList[priorities]; + _priorities = priorities; + _priorityOffset = 5-((priorities + 1)/2); + for(int i = 0; i < priorities; i++) + { + _priorityLists[i] = new SimpleQueueEntryList(queue); + } + } + + public AMQQueue getQueue() + { + return _queue; + } + + public QueueEntry add(AMQMessage message) + { + try + { + int index = ((CommonContentHeaderProperties)((message.getContentHeaderBody().properties))).getPriority() - _priorityOffset; + if(index >= _priorities) + { + index = _priorities-1; + } + else if(index < 0) + { + index = 0; + } + return _priorityLists[index].add(message); + } + catch (AMQException e) + { + // TODO - fix AMQ Exception + throw new RuntimeException(e); + } + + } + + public QueueEntry next(QueueEntry node) + { + QueueEntryImpl nodeImpl = (QueueEntryImpl)node; + QueueEntry next = nodeImpl.getNext(); + + if(next == null) + { + QueueEntryList nodeEntryList = nodeImpl.getQueueEntryList(); + int index; + for(index = _priorityLists.length-1; _priorityLists[index] != nodeEntryList; index--); + + while(next == null && index != 0) + { + index--; + next = ((QueueEntryImpl)_priorityLists[index].getHead()).getNext(); + } + + } + return next; + } + + private final class PriorityQueueEntryListIterator implements QueueEntryIterator + { + private final QueueEntryIterator[] _iterators = new QueueEntryIterator[ _priorityLists.length ]; + private QueueEntry _lastNode; + + PriorityQueueEntryListIterator() + { + for(int i = 0; i < _priorityLists.length; i++) + { + _iterators[i] = _priorityLists[i].iterator(); + } + _lastNode = _iterators[_iterators.length - 1].getNode(); + } + + + public boolean atTail() + { + for(int i = 0; i < _iterators.length; i++) + { + if(!_iterators[i].atTail()) + { + return false; + } + } + return true; + } + + public QueueEntry getNode() + { + return _lastNode; + } + + public boolean advance() + { + for(int i = _iterators.length-1; i >= 0; i--) + { + if(_iterators[i].advance()) + { + _lastNode = _iterators[i].getNode(); + return true; + } + } + return false; + } + } + + public QueueEntryIterator iterator() + { + return new PriorityQueueEntryListIterator(); + } + + public QueueEntry getHead() + { + return _priorityLists[_priorities-1].getHead(); + } + + static class Factory implements QueueEntryListFactory + { + private final int _priorities; + + Factory(int priorities) + { + _priorities = priorities; + } + + public QueueEntryList createQueueEntryList(AMQQueue queue) + { + return new PriorityQueueList(queue, _priorities); + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java index 08dcd0e329..39c28a7355 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java @@ -26,6 +26,99 @@ import org.apache.qpid.server.subscription.Subscription; */ public interface QueueEntry extends Comparable<QueueEntry> { + + + + public static enum State + { + AVAILABLE, + ACQUIRED, + EXPIRED, + DEQUEUED + } + + public static interface StateChangeListener + { + public void stateChanged(QueueEntry entry, State oldSate, State newState); + } + + public abstract class EntryState + { + private EntryState() + { + } + + public abstract State getState(); + } + + + public final class AvailableState extends EntryState + { + + public State getState() + { + return State.AVAILABLE; + } + } + + + public final class DeletedState extends EntryState + { + + public State getState() + { + return State.DEQUEUED; + } + } + + public final class ExpiredState extends EntryState + { + + public State getState() + { + return State.EXPIRED; + } + } + + + public final class NonSubscriptionAcquiredState extends EntryState + { + public State getState() + { + return State.ACQUIRED; + } + } + + public final class SubscriptionAcquiredState extends EntryState + { + private final Subscription _subscription; + + public SubscriptionAcquiredState(Subscription subscription) + { + _subscription = subscription; + } + + + public State getState() + { + return State.ACQUIRED; + } + + public Subscription getSubscription() + { + return _subscription; + } + } + + + final static EntryState AVAILABLE_STATE = new AvailableState(); + final static EntryState DELETED_STATE = new DeletedState(); + final static EntryState EXPIRED_STATE = new ExpiredState(); + final static EntryState NON_SUBSCRIPTION_ACQUIRED_STATE = new NonSubscriptionAcquiredState(); + + + + AMQQueue getQueue(); AMQMessage getMessage(); @@ -38,8 +131,12 @@ public interface QueueEntry extends Comparable<QueueEntry> boolean isAcquired(); + boolean acquire(); boolean acquire(Subscription sub); + boolean delete(); + boolean isDeleted(); + boolean acquiredBySubscription(); void setDeliveredToSubscription(); @@ -71,4 +168,7 @@ public interface QueueEntry extends Comparable<QueueEntry> void discard(StoreContext storeContext) throws AMQException; boolean isQueueDeleted(); + + void addStateChangeListener(StateChangeListener listener); + boolean removeStateChangeListener(StateChangeListener listener); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java index 54404e23e7..6225501c72 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -27,8 +27,9 @@ import org.apache.log4j.Logger; import java.util.Set; import java.util.HashSet; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import java.util.concurrent.CopyOnWriteArraySet; public class QueueEntryImpl implements QueueEntry @@ -39,42 +40,77 @@ public class QueueEntryImpl implements QueueEntry */ private static final Logger _log = Logger.getLogger(QueueEntryImpl.class); - private final AMQQueue _queue; + private final SimpleQueueEntryList _queueEntryList; + private final AMQMessage _message; private Set<Subscription> _rejectedBy = null; - private final AtomicReference<Object> _owner = new AtomicReference<Object>(); - private final AtomicLong _entryId = new AtomicLong(); + private volatile EntryState _state = AVAILABLE_STATE; + + private static final + AtomicReferenceFieldUpdater<QueueEntryImpl, EntryState> + _stateUpdater = + AtomicReferenceFieldUpdater.newUpdater + (QueueEntryImpl.class, EntryState.class, "_state"); + + + private volatile Set<StateChangeListener> _stateChangeListeners; + + private static final + AtomicReferenceFieldUpdater<QueueEntryImpl, Set> + _listenersUpdater = + AtomicReferenceFieldUpdater.newUpdater + (QueueEntryImpl.class, Set.class, "_stateChangeListeners"); + + private static final + AtomicLongFieldUpdater<QueueEntryImpl> + _entryIdUpdater = + AtomicLongFieldUpdater.newUpdater + (QueueEntryImpl.class, "_entryId"); - public QueueEntryImpl(AMQQueue queue, AMQMessage message, final long entryId) + + private volatile long _entryId; + + volatile QueueEntryImpl _next; + + + QueueEntryImpl(SimpleQueueEntryList queueEntryList) { - _queue = queue; + this(queueEntryList,null,Long.MIN_VALUE); + _state = DELETED_STATE; + } + + + public QueueEntryImpl(SimpleQueueEntryList queueEntryList, AMQMessage message, final long entryId) + { + _queueEntryList = queueEntryList; _message = message; - _entryId.set(entryId); + + _entryIdUpdater.set(this, entryId); } - public QueueEntryImpl(AMQQueue queue, AMQMessage message) + public QueueEntryImpl(SimpleQueueEntryList queueEntryList, AMQMessage message) { - _queue = queue; + _queueEntryList = queueEntryList; _message = message; } protected void setEntryId(long entryId) { - _entryId.set(entryId); + _entryIdUpdater.set(this, entryId); } protected long getEntryId() { - return _entryId.get(); + return _entryId; } public AMQQueue getQueue() { - return _queue; + return _queueEntryList.getQueue(); } public AMQMessage getMessage() @@ -94,23 +130,39 @@ public class QueueEntryImpl implements QueueEntry public boolean expired() throws AMQException { - return getMessage().expired(_queue); + return getMessage().expired(getQueue()); } public boolean isAcquired() { - return _owner.get() != null; + return _state.getState() == State.ACQUIRED; + } + + public boolean acquire() + { + return acquire(NON_SUBSCRIPTION_ACQUIRED_STATE); + } + + private boolean acquire(final EntryState state) + { + boolean acquired = _stateUpdater.compareAndSet(this,AVAILABLE_STATE, state); + if(acquired && _stateChangeListeners != null) + { + notifyStateChange(State.AVAILABLE, State.ACQUIRED); + } + + return acquired; } public boolean acquire(Subscription sub) { - return !(_owner.compareAndSet(null, sub == null ? this : sub)); + return acquire(sub.getOwningState()); } public boolean acquiredBySubscription() { - Object owner = _owner.get(); - return (owner != null) && (owner != this); + + return (_state instanceof SubscriptionAcquiredState); } public void setDeliveredToSubscription() @@ -120,7 +172,7 @@ public class QueueEntryImpl implements QueueEntry public void release() { - _owner.set(null); + _stateUpdater.set(this,AVAILABLE_STATE); } public String debugIdentity() @@ -141,18 +193,16 @@ public class QueueEntryImpl implements QueueEntry public Subscription getDeliveredSubscription() { - synchronized (this) - { - Object owner = _owner.get(); - if (owner instanceof Subscription) + EntryState state = _state; + if (state instanceof SubscriptionAcquiredState) { - return (Subscription) owner; + return ((SubscriptionAcquiredState) state).getSubscription(); } else { return null; } - } + } public void reject() @@ -193,25 +243,44 @@ public class QueueEntryImpl implements QueueEntry public void requeue(final StoreContext storeContext) throws AMQException { - _queue.requeue(storeContext, this); + getQueue().requeue(storeContext, this); + if(_stateChangeListeners != null) + { + notifyStateChange(QueueEntry.State.ACQUIRED, QueueEntry.State.AVAILABLE); + } } public void dequeue(final StoreContext storeContext) throws FailedDequeueException { + + getQueue().dequeue(storeContext, this); + if(_stateChangeListeners != null) + { + notifyStateChange(_state.getState() , QueueEntry.State.DEQUEUED); + } + } + + private void notifyStateChange(final State oldState, final State newState) + { + for(StateChangeListener l : _stateChangeListeners) + { + l.stateChanged(this, oldState, newState); + } } public void dispose(final StoreContext storeContext) throws MessageCleanupException { getMessage().decrementReference(storeContext); + delete(); } public void restoreCredit() { - Object owner = _owner.get(); - if(owner instanceof Subscription) + EntryState state = _state; + if(state instanceof SubscriptionAcquiredState) { - Subscription s = (Subscription) owner; + Subscription s = ((SubscriptionAcquiredState) _state).getSubscription(); s.restoreCredit(this); } } @@ -232,9 +301,85 @@ public class QueueEntryImpl implements QueueEntry return getQueue().isDeleted(); } + public void addStateChangeListener(StateChangeListener listener) + { + Set<StateChangeListener> listeners = _stateChangeListeners; + if(listeners == null) + { + _listenersUpdater.compareAndSet(this, null, new CopyOnWriteArraySet<StateChangeListener>()); + listeners = _stateChangeListeners; + } + + listeners.add(listener); + } + + public boolean removeStateChangeListener(StateChangeListener listener) + { + Set<StateChangeListener> listeners = _stateChangeListeners; + if(listeners != null) + { + return listeners.remove(listener); + } + + return false; + } + + public int compareTo(final QueueEntry o) { QueueEntryImpl other = (QueueEntryImpl)o; return getEntryId() > other.getEntryId() ? 1 : getEntryId() < other.getEntryId() ? -1 : 0; } + + public QueueEntryImpl getNext() + { + + QueueEntryImpl next = nextNode(); + while(next != null && next.isDeleted()) + { + + final QueueEntryImpl newNext = next.nextNode(); + if(newNext != null) + { + SimpleQueueEntryList._nextUpdater.compareAndSet(this,next, newNext); + next = nextNode(); + } + else + { + next = null; + } + + } + return next; + } + + QueueEntryImpl nextNode() + { + return _next; + } + + public boolean isDeleted() + { + return _state == DELETED_STATE; + } + + public boolean delete() + { + EntryState state = _state; + + if(state != DELETED_STATE && _stateUpdater.compareAndSet(this,state,DELETED_STATE)) + { + _queueEntryList.advanceHead(); + return true; + } + else + { + return false; + } + } + + public QueueEntryList getQueueEntryList() + { + return _queueEntryList; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java new file mode 100644 index 0000000000..c5c115a2d1 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java @@ -0,0 +1,30 @@ +/* +* +* 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.qpid.server.queue; + +public interface QueueEntryIterator +{ + boolean atTail(); + + QueueEntry getNode(); + + boolean advance(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java index fc32909079..313e076f61 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java @@ -1,10 +1,3 @@ -package org.apache.qpid.server.queue; - -import org.apache.qpid.server.store.StoreContext; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -25,196 +18,17 @@ import java.util.concurrent.atomic.AtomicReference; * under the License. * */ -public class QueueEntryList -{ - private final QueueEntryNode _head = new QueueEntryNode(); - - private AtomicReference<QueueEntryNode> _tail = new AtomicReference<QueueEntryNode>(_head); - private final AMQQueue _queue; - - - - public final class QueueEntryNode extends QueueEntryImpl - { - private final AtomicBoolean _deleted = new AtomicBoolean(); - private final AtomicReference<QueueEntryNode> _next = new AtomicReference<QueueEntryNode>(); - - - - public QueueEntryNode() - { - super(null,null,Long.MIN_VALUE); - _deleted.set(true); - } - - public QueueEntryNode(AMQQueue queue, AMQMessage message) - { - super(queue,message); - } - - public QueueEntryNode getNext() - { - - QueueEntryNode next = nextNode(); - while(next != null && next.isDeleted()) - { - - final QueueEntryList.QueueEntryNode newNext = next.nextNode(); - if(newNext != null) - { - _next.compareAndSet(next, newNext); - next = nextNode(); - } - else - { - next = null; - } - - } - return next; - } - - private QueueEntryList.QueueEntryNode nextNode() - { - return _next.get(); - } - - public boolean isDeleted() - { - return _deleted.get(); - } - - - public boolean delete() - { - if(_deleted.compareAndSet(false,true)) - { - advanceHead(); - return true; - } - else - { - return false; - } - } - - - public void dispose(final StoreContext storeContext) throws MessageCleanupException - { - super.dispose(storeContext); - delete(); - } - } - - - public QueueEntryList(AMQQueue queue) - { - _queue = queue; - } - - private void advanceHead() - { - QueueEntryNode head = _head.nextNode(); - while(head._next.get() != null && head.isDeleted()) - { - - final QueueEntryList.QueueEntryNode newhead = head.nextNode(); - if(newhead != null) - { - _head._next.compareAndSet(head, newhead); - } - head = _head.nextNode(); - } - } - - - public QueueEntry add(AMQMessage message) - { - QueueEntryNode node = new QueueEntryNode(_queue, message); - for (;;) - { - QueueEntryNode tail = _tail.get(); - QueueEntryNode next = tail.nextNode(); - if (tail == _tail.get()) - { - if (next == null) - { - node.setEntryId(tail.getEntryId()+1); - if (tail._next.compareAndSet(null, node)) - { - _tail.compareAndSet(tail, node); - - return node; - } - } - else - { - _tail.compareAndSet(tail, next); - } - } - } - } - - - public class QueueEntryIterator - { - - private QueueEntryNode _lastNode; - - QueueEntryIterator(QueueEntryNode startNode) - { - _lastNode = startNode; - } - - - public boolean atTail() - { - return _lastNode.nextNode() == null; - } - - public QueueEntryNode getNode() - { - - return _lastNode; - - } - - boolean advance() - { - - if(!atTail()) - { - QueueEntryNode nextNode = _lastNode.nextNode(); - while(nextNode.isDeleted() && nextNode.nextNode() != null) - { - nextNode = nextNode.nextNode(); - } - _lastNode = nextNode; - return true; - - } - else - { - return false; - } - - } - - } - - - public QueueEntryIterator iterator() - { - return new QueueEntryIterator(_head); - } +package org.apache.qpid.server.queue; +public interface QueueEntryList +{ + AMQQueue getQueue(); - public QueueEntryNode getHead() - { - return _head; - } + QueueEntry add(AMQMessage message); + QueueEntry next(QueueEntry node); - + QueueEntryIterator iterator(); + QueueEntry getHead(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryListFactory.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryListFactory.java new file mode 100644 index 0000000000..4dbce45f67 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryListFactory.java @@ -0,0 +1,26 @@ +/* +* +* 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.qpid.server.queue; + +interface QueueEntryListFactory +{ + public QueueEntryList createQueueEntryList(AMQQueue queue); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java index 6dae7edb72..7e2f0fc56a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -5,8 +5,6 @@ import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.store.StoreContext; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.subscription.SubscriptionList; import org.apache.qpid.server.output.ProtocolOutputConverter; @@ -72,9 +70,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private final List<Task> _deleteTaskList = new CopyOnWriteArrayList<Task>(); - private final AtomicInteger _messageCount = new AtomicInteger(0); + private final AtomicInteger _atomicQueueCount = new AtomicInteger(0); - private final AtomicLong _depth = new AtomicLong(0L); + private final AtomicLong _atomicQueueSize = new AtomicLong(0L); private final AtomicInteger _activeSubscriberCount = new AtomicInteger(); @@ -138,9 +136,22 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private AtomicInteger _deliveredMessages = new AtomicInteger(); - SimpleAMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost) + + + protected SimpleAMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost) throws AMQException { + this(name,durable,owner,autoDelete,virtualHost,new SimpleQueueEntryList.Factory()); + } + + protected SimpleAMQQueue(AMQShortString name, + boolean durable, + AMQShortString owner, + boolean autoDelete, + VirtualHost virtualHost, + QueueEntryListFactory entryListFactory) + throws AMQException + { if (name == null) { @@ -157,7 +168,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _owner = owner; _autoDelete = autoDelete; _virtualHost = virtualHost; - _entries = new QueueEntryList(this); + _entries = entryListFactory.createQueueEntryList(this); _asyncDelivery = AsyncDeliveryConfig.getAsyncDeliveryExecutor(); @@ -257,13 +268,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _activeSubscriberCount.incrementAndGet(); subscription.setStateListener(this); - subscription.setQueueContext(null,_entries.getHead()); + subscription.setLastSeenEntry(null,_entries.getHead()); - if(!_deleted.get()) + if(!isDeleted()) { subscription.setQueue(this); _subscriptionList.add(subscription); - if(_deleted.get()) + if(isDeleted()) { subscription.queueDeleted(this); } @@ -330,10 +341,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener // need to get the enqueue lock - _messageCount.incrementAndGet(); + incrementQueueCount(); + incrementQueueSize(message); _totalMessagesReceived.incrementAndGet(); - _depth.addAndGet(message.getSize()); + QueueEntry entry = _entries.add(message); @@ -366,7 +378,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } - + // always do one extra loop after we believe we've finished + // this catches the case where we *just* miss an update int loops = 2; while(!entry.isAcquired() && loops != 0) @@ -386,26 +399,23 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener && !sub.isSuspended() && sub.isActive()) { - - if( !sub.wouldSuspend(entry)) + if( !sub.wouldSuspend(entry)) + { + if(!sub.isBrowser() && !entry.acquire(sub)) + { + sub.restoreCredit(entry); + } + else { - if(!sub.isBrowser() && entry.acquire(sub)) + deliverMessage(sub, entry); + QueueEntry queueEntryNode = sub.getLastSeenEntry(); + if(_entries.next(queueEntryNode) == entry) { - sub.restoreCredit(entry); + sub.setLastSeenEntry(queueEntryNode,entry); } - else - { - deliverMessage(sub, entry); - QueueEntryList.QueueEntryNode queueEntryNode = (QueueEntryList.QueueEntryNode) sub.getQueueContext(); - if(queueEntryNode.getNext() == entry) - { - sub.setQueueContext(queueEntryNode,entry); - } - } } - - + } } } } @@ -421,7 +431,26 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } else if(!entry.isAcquired()) { - deliverAsync(); + // check that all subscriptions are not in advance of the entry + SubscriptionList.SubscriptionNodeIterator subIter = _subscriptionList.iterator(); + while(subIter.advance() && !entry.isAcquired()) + { + final Subscription subscription = subIter.getNode().getSubscription(); + QueueEntry subnode = subscription.getLastSeenEntry(); + while((entry.compareTo(subnode) < 0) && !entry.isAcquired()) + { + if(subscription.setLastSeenEntry(subnode,entry)) + { + break; + } + else + { + subnode = subscription.getLastSeenEntry(); + } + } + + } + deliverAsync(); } @@ -439,6 +468,16 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } + private void incrementQueueSize(final AMQMessage message) + { + getAtomicQueueSize().addAndGet(message.getSize()); + } + + private void incrementQueueCount() + { + getAtomicQueueCount().incrementAndGet(); + } + private void deliverMessage(final Subscription sub, final QueueEntry entry) throws AMQException { @@ -450,15 +489,15 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private boolean subscriptionReadyAndHasInterest(final Subscription sub, final QueueEntry entry) { - QueueEntryList.QueueEntryNode node = (QueueEntryList.QueueEntryNode) sub.getQueueContext(); + QueueEntry node = sub.getLastSeenEntry(); while(node.isAcquired() || node.isDeleted() || !sub.hasInterest(node) ) { - QueueEntryList.QueueEntryNode newNode = node.getNext(); + QueueEntry newNode = _entries.next(node); if(newNode != null) { - sub.setQueueContext(node, newNode); - node = (QueueEntryList.QueueEntryNode) sub.getQueueContext(); + sub.setLastSeenEntry(node, newNode); + node = sub.getLastSeenEntry(); } else { @@ -468,7 +507,29 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } - return node == entry; + if(node == entry) + { + return true; + } + else + { + node = sub.getLastSeenEntry(); + if(entry.compareTo(node) < 0 && sub.hasInterest(entry)) + { + do + { + if(sub.setLastSeenEntry(node,entry)) + { + return true; + } + else + { + node = sub.getLastSeenEntry(); + } + } while (entry.compareTo(node) < 0); + } + return false; + } } @@ -484,16 +545,16 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener // we don't make browsers send the same stuff twice if(!sub.isBrowser()) { - QueueEntry subEntry = (QueueEntry) sub.getQueueContext(); + QueueEntry subEntry = sub.getLastSeenEntry(); while(entry.compareTo(subEntry)<0) { - if(sub.setQueueContext(subEntry,entry)) + if(sub.setLastSeenEntry(subEntry,entry)) { break; } else { - subEntry = (QueueEntry) sub.getQueueContext(); + subEntry = sub.getLastSeenEntry(); } } } @@ -507,8 +568,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException { - _messageCount.decrementAndGet(); - _depth.addAndGet(-entry.getMessage().getSize()); + decrementQueueCount(); + decrementQueueSize(entry); if(entry.acquiredBySubscription()) { _deliveredMessages.decrementAndGet(); @@ -521,7 +582,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { _virtualHost.getMessageStore().dequeueMessage(storeContext, getName(), msg.getMessageId()); } - ((QueueEntryList.QueueEntryNode)entry).delete(); + entry.delete(); } catch (MessageCleanupException e) @@ -540,6 +601,16 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } + private void decrementQueueSize(final QueueEntry entry) + { + getAtomicQueueSize().addAndGet(-entry.getMessage().getSize()); + } + + void decrementQueueCount() + { + getAtomicQueueCount().decrementAndGet(); + } + public boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException { /* TODO : This is wrong as the subscription may be suspended, we should instead change the state of the message @@ -585,12 +656,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public int getMessageCount() { - return _messageCount.get(); + return getAtomicQueueCount().get(); } public long getQueueDepth() { - return _depth.get(); + return getAtomicQueueSize().get(); } public int getUndeliveredMessageCount() @@ -627,10 +698,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public List<QueueEntry> getMessagesOnTheQueue() { ArrayList<QueueEntry> entryList = new ArrayList<QueueEntry>(); - QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator(); + QueueEntryIterator queueListIterator = _entries.iterator(); while(queueListIterator.advance()) { - QueueEntryList.QueueEntryNode node = queueListIterator.getNode(); + QueueEntry node = queueListIterator.getNode(); if(node != null && !node.isDeleted()) { entryList.add(node); @@ -647,13 +718,32 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _activeSubscriberCount.decrementAndGet(); } - else if(newState == Subscription.State.ACTIVE && oldState != Subscription.State.ACTIVE) - { - _activeSubscriberCount.incrementAndGet(); + else if(newState == Subscription.State.ACTIVE) + { + if(oldState != Subscription.State.ACTIVE) + { + _activeSubscriberCount.incrementAndGet(); + + } deliverAsync(); } } + public int compareTo(final AMQQueue o) + { + return _name.compareTo(o.getName()); + } + + public AtomicInteger getAtomicQueueCount() + { + return _atomicQueueCount; + } + + public AtomicLong getAtomicQueueSize() + { + return _atomicQueueSize; + } + public static interface QueueEntryFilter { public boolean accept(QueueEntry entry); @@ -705,10 +795,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public List<QueueEntry> getMessagesOnTheQueue(QueueEntryFilter filter) { ArrayList<QueueEntry> entryList = new ArrayList<QueueEntry>(); - QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator(); + QueueEntryIterator queueListIterator = _entries.iterator(); while(queueListIterator.advance() && !filter.filterComplete()) { - QueueEntryList.QueueEntryNode node = queueListIterator.getNode(); + QueueEntry node = queueListIterator.getNode(); if(!node.isDeleted() && filter.accept(node)) { entryList.add(node); @@ -770,13 +860,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public void deleteMessageFromTop(StoreContext storeContext) throws AMQException { - QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator(); + QueueEntryIterator queueListIterator = _entries.iterator(); boolean noDeletes = true; while(noDeletes && queueListIterator.advance() ) { - QueueEntryList.QueueEntryNode node = queueListIterator.getNode(); - if(!node.isDeleted() && !node.acquire(null)) + QueueEntry node = queueListIterator.getNode(); + if(!node.isDeleted() && node.acquire()) { node.dequeue(storeContext); @@ -791,13 +881,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public long clearQueue(StoreContext storeContext) throws AMQException { - QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator(); + QueueEntryIterator queueListIterator = _entries.iterator(); long count = 0; while(queueListIterator.advance()) { - QueueEntryList.QueueEntryNode node = queueListIterator.getNode(); - if(!node.isDeleted() && !node.acquire(null)) + QueueEntry node = queueListIterator.getNode(); + if(!node.isDeleted() && node.acquire()) { node.dequeue(storeContext); @@ -864,8 +954,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { public void run() { - String startName = Thread.currentThread().getName(); - Thread.currentThread().setName("async delivery (Q: " + getName() + " VH: " + _virtualHost.getName() +")"); try { processQueue(this); @@ -874,8 +962,108 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { _logger.error(e); } - Thread.currentThread().setName(startName); + + } + } + + public void flushSubscription(Subscription sub) throws AMQException + { + boolean atTail = false; + while(sub.isActive() && !atTail) + { + + synchronized(sub.getSendLock()) + { + if(sub.isActive()) + { + + QueueEntry node = moveSubscriptionToNextNode(sub); + + if(!(node.isAcquired() || node.isDeleted())) + { + if(!sub.isSuspended()) + { + if(sub.hasInterest(node)) + { + if(!sub.wouldSuspend(node)) + { + if(!sub.isBrowser() && !node.acquire(sub)) + { + sub.restoreCredit(node); + + } + else + { + deliverMessage(sub, node); + + if(sub.isBrowser()) + { + QueueEntry newNode = _entries.next(node); + + if(newNode != null) + { + sub.setLastSeenEntry(node, newNode); + node = sub.getLastSeenEntry(); + } + + + } + } + + } + else + { + break; + } + } + else + { + // this subscription is not interested in this node so we can skip over it + QueueEntry newNode = _entries.next(node); + if(newNode != null) + { + sub.setLastSeenEntry(node, newNode); + } + } + } + } + atTail = (_entries.next(node) == null); + + } + } + + } + } + + private QueueEntry moveSubscriptionToNextNode(final Subscription sub) + throws AMQException + { + QueueEntry node = sub.getLastSeenEntry(); + while(node.isAcquired() || node.isDeleted() || node.expired()) + { + if(!node.isAcquired() && !node.isDeleted() && node.expired()) + { + if(node.acquire()) + { + final StoreContext reapingStoreContext = new StoreContext(); + node.dequeue(reapingStoreContext); + node.dispose(reapingStoreContext); + + } + } + QueueEntry newNode = _entries.next(node); + if(newNode != null) + { + sub.setLastSeenEntry(node, newNode); + node = sub.getLastSeenEntry(); + } + else + { + break; + } + } + return node; } @@ -916,31 +1104,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { boolean advanced = false; - QueueEntryList.QueueEntryNode node = (QueueEntryList.QueueEntryNode) sub.getQueueContext(); - while(node.isAcquired() || node.isDeleted() || node.expired()) - { - if(!node.isAcquired() && !node.isDeleted() && node.expired()) - { - if(node.acquire(null)) - { - final StoreContext reapingStoreContext = new StoreContext(); - node.dequeue(reapingStoreContext); - node.dispose(reapingStoreContext); - - } - } - QueueEntryList.QueueEntryNode newNode = node.getNext(); - if(newNode != null) - { - sub.setQueueContext(node, newNode); - node = (QueueEntryList.QueueEntryNode) sub.getQueueContext(); - } - else - { - break; - } - - } + QueueEntry node = moveSubscriptionToNextNode(sub); if(!(node.isAcquired() || node.isDeleted())) { if(!sub.isSuspended()) @@ -949,9 +1113,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { if(!sub.wouldSuspend(node)) { - if(!sub.isBrowser() && node.acquire(sub)) + if(!sub.isBrowser() && !node.acquire(sub)) { sub.restoreCredit(node); + } else { @@ -959,12 +1124,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener if(sub.isBrowser()) { - QueueEntryList.QueueEntryNode newNode = node.getNext(); + QueueEntry newNode = _entries.next(node); if(newNode != null) { - sub.setQueueContext(node, newNode); - node = (QueueEntryList.QueueEntryNode) sub.getQueueContext(); + sub.setLastSeenEntry(node, newNode); + node = sub.getLastSeenEntry(); advanced = true; } @@ -973,19 +1138,23 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } done = false; } + else + { + node.addStateChangeListener(new QueueEntryListener(sub,node)); + } } else { // this subscription is not interested in this node so we can skip over it - QueueEntryList.QueueEntryNode newNode = node.getNext(); + QueueEntry newNode = _entries.next(node); if(newNode != null) { - sub.setQueueContext(node, newNode); + sub.setLastSeenEntry(node, newNode); } } } } - final boolean atTail = (node.getNext() == null); + final boolean atTail = (_entries.next(node) == null); done = done && atTail; @@ -994,7 +1163,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener unregisterSubscription(sub); ProtocolOutputConverter converter = sub.getChannel().getProtocolSession().getProtocolOutputConverter(); - converter.confirmConsumerAutoClose(sub.getChannel().getChannelId(), sub.getConumerTag()); + converter.confirmConsumerAutoClose(sub.getChannel().getChannelId(), sub.getConsumerTag()); } @@ -1026,24 +1195,18 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } - public boolean performGet(final AMQProtocolSession session, final AMQChannel channel, final boolean b) - throws AMQException - { - return false; //To change body of implemented methods use File | Settings | File Templates. - } - public void removeExpiredIfNoSubscribers() throws AMQException { final StoreContext storeContext = new StoreContext(); - QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator(); + QueueEntryIterator queueListIterator = _entries.iterator(); while(queueListIterator.advance()) { - QueueEntryList.QueueEntryNode node = queueListIterator.getNode(); - if(!node.isDeleted() && node.expired() && node.acquire(null)) + QueueEntry node = queueListIterator.getNode(); + if(!node.isDeleted() && node.expired() && node.acquire()) { node.dequeue(storeContext); @@ -1159,8 +1322,34 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener return _managedObject; } - public int compareTo(final Object o) + public int N(final Object o) { return _name.compareTo(((AMQQueue) o).getName()); } + + private final class QueueEntryListener implements QueueEntry.StateChangeListener + { + private final QueueEntry _entry; + + public QueueEntryListener(final Subscription sub, final QueueEntry entry) + { + _entry = entry; + } + + public boolean equals(Object o) + { + return _entry == ((QueueEntryListener)o)._entry; + } + + public int hashCode() + { + return System.identityHashCode(_entry); + } + + public void stateChanged(QueueEntry entry, QueueEntry.State oldSate, QueueEntry.State newState) + { + entry.removeStateChangeListener(this); + deliverAsync(); + } + } }
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java new file mode 100644 index 0000000000..a46c5ae2e8 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java @@ -0,0 +1,178 @@ +package org.apache.qpid.server.queue; + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +/* +* +* 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. +* +*/ +public class SimpleQueueEntryList implements QueueEntryList +{ + private final QueueEntryImpl _head; + + private volatile QueueEntryImpl _tail; + + static final AtomicReferenceFieldUpdater<SimpleQueueEntryList, QueueEntryImpl> + _tailUpdater = + AtomicReferenceFieldUpdater.newUpdater + (SimpleQueueEntryList.class, QueueEntryImpl.class, "_tail"); + + + private final AMQQueue _queue; + + static final AtomicReferenceFieldUpdater<QueueEntryImpl, QueueEntryImpl> + _nextUpdater = + AtomicReferenceFieldUpdater.newUpdater + (QueueEntryImpl.class, QueueEntryImpl.class, "_next"); + + + + + + public SimpleQueueEntryList(AMQQueue queue) + { + _queue = queue; + _head = new QueueEntryImpl(this); + _tail = _head; + } + + void advanceHead() + { + QueueEntryImpl head = _head.nextNode(); + while(head._next != null && head.isDeleted()) + { + + final QueueEntryImpl newhead = head.nextNode(); + if(newhead != null) + { + _nextUpdater.compareAndSet(_head,head, newhead); + } + head = _head.nextNode(); + } + } + + + public AMQQueue getQueue() + { + return _queue; + } + + + public QueueEntry add(AMQMessage message) + { + QueueEntryImpl node = new QueueEntryImpl(this, message); + for (;;) + { + QueueEntryImpl tail = _tail; + QueueEntryImpl next = tail.nextNode(); + if (tail == _tail) + { + if (next == null) + { + node.setEntryId(tail.getEntryId()+1); + if (_nextUpdater.compareAndSet(tail, null, node)) + { + _tailUpdater.compareAndSet(this, tail, node); + + return node; + } + } + else + { + _tailUpdater.compareAndSet(this,tail, next); + } + } + } + } + + public QueueEntry next(QueueEntry node) + { + return ((QueueEntryImpl)node).getNext(); + } + + + public class QueueEntryIteratorImpl implements QueueEntryIterator + { + + private QueueEntryImpl _lastNode; + + QueueEntryIteratorImpl(QueueEntryImpl startNode) + { + _lastNode = startNode; + } + + + public boolean atTail() + { + return _lastNode.nextNode() == null; + } + + public QueueEntry getNode() + { + + return _lastNode; + + } + + public boolean advance() + { + + if(!atTail()) + { + QueueEntryImpl nextNode = _lastNode.nextNode(); + while(nextNode.isDeleted() && nextNode.nextNode() != null) + { + nextNode = nextNode.nextNode(); + } + _lastNode = nextNode; + return true; + + } + else + { + return false; + } + + } + + } + + + public QueueEntryIterator iterator() + { + return new QueueEntryIteratorImpl(_head); + } + + + public QueueEntry getHead() + { + return _head; + } + + static class Factory implements QueueEntryListFactory + { + + public QueueEntryList createQueueEntryList(AMQQueue queue) + { + return new SimpleQueueEntryList(queue); + } + } + + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java deleted file mode 100644 index 4c39eb3397..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * 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.qpid.server.queue; - -import org.apache.qpid.server.subscription.Subscription; - -import java.util.List; - -/** - * Abstraction of actor that will determine the subscriber to whom - * a message will be sent. - */ -public interface SubscriptionManager -{ - public List<Subscription> getSubscriptions(); - public boolean hasActiveSubscribers(); - public int getActiveConsumerCount(); - public int getConsumerCount(); - public Subscription nextSubscriber(QueueEntry entry); - public void addSubscriber(Subscription subscription); - public Subscription removeSubscriber(Subscription subscription); - public boolean isEmpty(); - public void queueDeleted(AMQQueue queue); - - - void setExclusive(final boolean b); - - Object getChangeLock(); -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java deleted file mode 100644 index 117799ad87..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * - * 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.qpid.server.queue; - -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.log4j.Logger; -import org.apache.qpid.server.subscription.Subscription; - -/** Holds a set of subscriptions for a queue and manages the round robin-ing of deliver etc. */ -class SubscriptionSet implements SubscriptionManager -{ - private static final Logger _log = Logger.getLogger(SubscriptionSet.class); - - /** List of registered subscribers */ - private final List<Subscription> _subscriptions = new CopyOnWriteArrayList<Subscription>(); - - private final Map<Subscription, DeliveryAgent> _deliveryAgents = - new ConcurrentHashMap<Subscription, DeliveryAgent>(); - - - /** Used to control the round robin delivery of content */ - private int _currentSubscriber; - private final Object _changeLock = new Object(); - private volatile boolean _exclusive; - - - /** Accessor for unit tests. */ - int getCurrentSubscriber() - { - return _currentSubscriber; - } - - public void addSubscriber(Subscription subscription) - { - synchronized (_changeLock) - { - _subscriptions.add(subscription); - _deliveryAgents.put(subscription, new DeliveryAgent(subscription)); - } - } - - /** - * Remove the subscription, returning it if it was found - * - * @param subscription - * - * @return null if no match was found - */ - public Subscription removeSubscriber(Subscription subscription) - { - // TODO: possibly need O(1) operation here. - - synchronized (_changeLock) - { - - if (_subscriptions.remove(subscription)) - { - _deliveryAgents.remove(subscription); - return subscription; - } - else - { - _log.error("Unable to remove subscription:" + subscription); - debugDumpSubscription(subscription); - return null; - } - } - } - - private void debugDumpSubscription(Subscription subscription) - { - if (_log.isDebugEnabled()) - { - _log.debug("Subscription " + subscription + " not found. Dumping subscriptions:"); - for (Subscription s : _subscriptions) - { - _log.debug("Subscription: " + s); - } - _log.debug("Subscription dump complete"); - } - } - - /** - * Return the next unsuspended subscription or null if not found. <p/> Performance note: This method can scan all - * items twice when looking for a subscription that is not suspended. The worst case occcurs when all subscriptions - * are suspended. However, it is does this without synchronisation and subscriptions may be added and removed - * concurrently. Also note that because of race conditions and when subscriptions are removed between calls to - * nextSubscriber, the IndexOutOfBoundsException also causes the scan to start at the beginning. - */ - public Subscription nextSubscriber(QueueEntry msg) - { - - - try - { - final Subscription result = nextSubscriberImpl(msg); - if (result == null) - { - _currentSubscriber = 0; - return nextSubscriberImpl(msg); - } - else - { - return result; - } - } - catch (IndexOutOfBoundsException e) - { - _currentSubscriber = 0; - return nextSubscriber(msg); - } - } - - private Subscription nextSubscriberImpl(QueueEntry msg) - { - if(_exclusive) - { - try - { - Subscription subscription = _subscriptions.get(0); - subscriberScanned(); - - if (!subscription.isSuspended() ) - { - if (subscription.hasInterest(msg)) - { - DeliveryAgent deliverAgent = _deliveryAgents.get(subscription); - - if (deliverAgent.ableToDeliver()) - { - if(!subscription.wouldSuspend(msg)) - { - return subscription; - } - } - } - } - } - catch(IndexOutOfBoundsException e) - { - } - return null; - } - else - { - if (_subscriptions.isEmpty()) - { - return null; - } - final ListIterator<Subscription> iterator = _subscriptions.listIterator(_currentSubscriber); - while (iterator.hasNext()) - { - - Subscription subscription = iterator.next(); - DeliveryAgent deliverAgent = _deliveryAgents.get(subscription); - ++_currentSubscriber; - subscriberScanned(); - - if (!(deliverAgent == null || subscription.isSuspended())) - { - if (subscription.hasInterest(msg) && deliverAgent.ableToDeliver()) - { - if (!subscription.wouldSuspend(msg)) - { - return subscription; - } - } - } - } - - return null; - } - } - - /** Overridden in test classes. */ - protected void subscriberScanned() - { - } - - public boolean isEmpty() - { - return _subscriptions.isEmpty(); - } - - public List<Subscription> getSubscriptions() - { - return _subscriptions; - } - - public boolean hasActiveSubscribers() - { - for (Subscription s : _subscriptions) - { - if (!s.isSuspended()) - { - return true; - } - } - return false; - } - - public int getActiveConsumerCount() - { - int count = 0; - for (Subscription s : _subscriptions) - { - if (s.isActive()) - { - count++; - } - } - return count; - } - - public int getConsumerCount() - { - return size(); - } - - /** - * Notification that a queue has been deleted. This is called so that the subscription can inform the channel, which - * in turn can update its list of unacknowledged messages. - * - * @param queue - */ - public void queueDeleted(AMQQueue queue) - { - for (Subscription s : _subscriptions) - { - s.queueDeleted(queue); - } - } - - int size() - { - return _subscriptions.size(); - } - - - public Object getChangeLock() - { - return _changeLock; - } - - public void setExclusive(final boolean exclusive) - { - _exclusive = exclusive; - } - - - public DeliveryAgent getDeliveryAgent(final Subscription sub) - { - return _deliveryAgents.get(sub); - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index 455983c6d8..d0dcd051f0 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -94,7 +94,11 @@ public abstract class ApplicationRegistry implements IApplicationRegistry { try { - _instanceMap.get(instanceID).close(); + IApplicationRegistry instance = _instanceMap.get(instanceID); + if(instance != null) + { + instance.close(); + } } catch (Exception e) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java index 180d47001b..59b4e62974 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java @@ -13,7 +13,6 @@ import org.apache.qpid.server.txn.NonTransactionalContext; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.abstraction.ContentChunk; import org.apache.qpid.framing.abstraction.MessagePublishInfo; @@ -31,12 +30,9 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Blob; import java.sql.Types; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicBoolean; import java.util.List; -import java.util.Collections; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; @@ -328,7 +324,8 @@ public class DerbyMessageStore implements MessageStore String queueName = rs.getString(1); String owner = rs.getString(2); AMQShortString queueNameShortString = new AMQShortString(queueName); - AMQQueue q = AMQQueueFactory.createAMQQueueImpl(queueNameShortString, true, owner == null ? null : new AMQShortString(owner), false, _virtualHost); + AMQQueue q = AMQQueueFactory.createAMQQueueImpl(queueNameShortString, true, owner == null ? null : new AMQShortString(owner), false, _virtualHost, + null); _virtualHost.getQueueRegistry().registerQueue(q); queueMap.put(queueNameShortString,q); @@ -1317,7 +1314,7 @@ public class DerbyMessageStore implements MessageStore AMQQueue queue = queues.get(queueName); if (queue == null) { - queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, null, false, _virtualHost); + queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, null, false, _virtualHost, null); _virtualHost.getQueueRegistry().registerQueue(queue); queues.put(queueName, queue); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java index 3316302719..f048059b9b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java @@ -26,7 +26,6 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.qpid.server.queue.AMQQueueImpl; import org.apache.qpid.server.queue.MessageMetaData; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.queue.AMQQueue; diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java b/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java index 3ee49d58cf..fdb56a1a55 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java @@ -37,7 +37,7 @@ public class StoreContext public StoreContext() { - _name = super.toString(); + _name = "StoreContext"; } public StoreContext(String name) @@ -52,7 +52,10 @@ public class StoreContext public void setPayload(Object payload) { - _logger.debug("public void setPayload(Object payload = " + payload + "): called"); + if(_logger.isDebugEnabled()) + { + _logger.debug("public void setPayload(Object payload = " + payload + "): called"); + } _payload = payload; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java new file mode 100644 index 0000000000..fbc8b3af7d --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java @@ -0,0 +1,29 @@ +/* +* +* 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.qpid.server.subscription; + +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.AMQException; + +public interface ClientDeliveryMethod +{ + void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag) throws AMQException; +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java new file mode 100644 index 0000000000..e2ed4104de --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java @@ -0,0 +1,28 @@ +/* +* +* 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.qpid.server.subscription; + +import org.apache.qpid.server.queue.QueueEntry; + +public interface RecordDeliveryMethod +{ + void recordMessageDelivery(final Subscription sub, final QueueEntry entry, final long deliveryTag); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java index 3f9bd65008..acbeae0f40 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java @@ -45,11 +45,13 @@ public interface Subscription AMQQueue getQueue(); + QueueEntry.SubscriptionAcquiredState getOwningState(); + void setQueue(AMQQueue queue); AMQChannel getChannel(); - AMQShortString getConumerTag(); + AMQShortString getConsumerTag(); boolean isSuspended(); @@ -63,8 +65,6 @@ public interface Subscription void close(); - - boolean filtersMessages(); void send(QueueEntry msg) throws AMQException; @@ -72,26 +72,8 @@ public interface Subscription void queueDeleted(AMQQueue queue); - - - -// Queue<QueueEntry> getPreDeliveryQueue(); -// -// Queue<QueueEntry> getResendQueue(); -// -// Queue<QueueEntry> getNextQueue(Queue<QueueEntry> messages); -// -// void enqueueForPreDelivery(QueueEntry msg, boolean deliverFirst); -// - - - - - boolean wouldSuspend(QueueEntry msg); -// void addToResendQueue(QueueEntry msg); -// Object getSendLock(); @@ -101,8 +83,8 @@ public interface Subscription void setStateListener(final StateListener listener); - Object getQueueContext(); + QueueEntry getLastSeenEntry(); - boolean setQueueContext(Object expected, Object newValue); + boolean setLastSeenEntry(QueueEntry expected, QueueEntry newValue); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java index 3e6db071e3..ce0362d73f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java @@ -26,6 +26,7 @@ import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.flow.FlowCreditManager; import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.AMQChannel; /** * Allows the customisation of the creation of a subscription. This is typically done within an AMQQueue. This factory @@ -44,4 +45,15 @@ public interface SubscriptionFactory boolean noLocal, FlowCreditManager creditManager) throws AMQException; + Subscription createSubscription(AMQChannel channel, + AMQProtocolSession protocolSession, + AMQShortString consumerTag, + boolean acks, + FieldTable filters, + boolean noLocal, + FlowCreditManager creditManager, + ClientDeliveryMethod clientMethod, + RecordDeliveryMethod recordMethod + ) + throws AMQException; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java index da419e4ff0..5badbad642 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java @@ -24,9 +24,11 @@ import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.flow.FlowCreditManager; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.subscription.SubscriptionFactory; +import org.apache.qpid.server.AMQChannel; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.common.AMQPFilterTypes; public class SubscriptionFactoryImpl implements SubscriptionFactory @@ -37,11 +39,39 @@ public class SubscriptionFactoryImpl implements SubscriptionFactory }*/ - public Subscription createSubscription(int channel, AMQProtocolSession protocolSession, + public Subscription createSubscription(int channelId, AMQProtocolSession protocolSession, AMQShortString consumerTag, boolean acks, FieldTable filters, boolean noLocal, FlowCreditManager creditManager) throws AMQException { + AMQChannel channel = protocolSession.getChannel(channelId); + if (channel == null) + { + throw new AMQException(AMQConstant.NOT_FOUND, "channel :" + channelId + " not found in protocol session"); + } + ClientDeliveryMethod clientMethod = channel.getClientDeliveryMethod(); + RecordDeliveryMethod recordMethod = channel.getRecordDeliveryMethod(); + + return createSubscription(channel, protocolSession, consumerTag, acks, filters, + noLocal, + creditManager, + clientMethod, + recordMethod + ); + } + + public Subscription createSubscription(final AMQChannel channel, + final AMQProtocolSession protocolSession, + final AMQShortString consumerTag, + final boolean acks, + final FieldTable filters, + final boolean noLocal, + final FlowCreditManager creditManager, + final ClientDeliveryMethod clientMethod, + final RecordDeliveryMethod recordMethod + ) + throws AMQException + { boolean isBrowser; if (filters != null) @@ -56,19 +86,18 @@ public class SubscriptionFactoryImpl implements SubscriptionFactory if(isBrowser) { - return new SubscriptionImpl.BrowserSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager); + return new SubscriptionImpl.BrowserSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod); } else if(acks) { - return new SubscriptionImpl.AckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager); + return new SubscriptionImpl.AckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod); } else { - return new SubscriptionImpl.NoAckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager); + return new SubscriptionImpl.NoAckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod); } } - public static final SubscriptionFactoryImpl INSTANCE = new SubscriptionFactoryImpl(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java index 6f28bbba64..653c7de514 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -25,7 +25,6 @@ import java.util.concurrent.atomic.AtomicReference; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; -import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.common.ClientProperties; import org.apache.qpid.framing.AMQShortString; @@ -46,7 +45,6 @@ import org.apache.qpid.server.store.StoreContext; */ public abstract class SubscriptionImpl implements Subscription, FlowCreditManager.FlowCreditManagerListener { - private final AtomicBoolean _suspended = new AtomicBoolean(false); private StateListener _stateListener = new StateListener() { @@ -59,18 +57,26 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage private final AtomicReference<State> _state = new AtomicReference<State>(State.ACTIVE); - private final AtomicReference<Object> _queueContext = new AtomicReference<Object>(null); + private final AtomicReference<QueueEntry> _queueContext = new AtomicReference<QueueEntry>(null); + private final ClientDeliveryMethod _deliveryMethod; + private final RecordDeliveryMethod _recordMethod; + + private QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this); + static final class BrowserSubscription extends SubscriptionImpl { - public BrowserSubscription(int channelId, AMQProtocolSession protocolSession, + public BrowserSubscription(AMQChannel channel, AMQProtocolSession protocolSession, AMQShortString consumerTag, FieldTable filters, - boolean noLocal, FlowCreditManager creditManager) + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) throws AMQException { - super(channelId, protocolSession, consumerTag, filters, noLocal, creditManager); + super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); } + public boolean isBrowser() { return true; @@ -91,21 +97,22 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage synchronized (getChannel()) { long deliveryTag = getChannel().getNextDeliveryTag(); - - getProtocolSession().getProtocolOutputConverter().writeDeliver(msg.getMessage(), getChannel().getChannelId(), deliveryTag, getConumerTag()); + sendToClient(msg, deliveryTag); } } } - static final class NoAckSubscription extends SubscriptionImpl + public static class NoAckSubscription extends SubscriptionImpl { - public NoAckSubscription(int channelId, AMQProtocolSession protocolSession, + public NoAckSubscription(AMQChannel channel, AMQProtocolSession protocolSession, AMQShortString consumerTag, FieldTable filters, - boolean noLocal, FlowCreditManager creditManager) + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) throws AMQException { - super(channelId, protocolSession, consumerTag, filters, noLocal, creditManager); + super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); } @@ -142,7 +149,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage { long deliveryTag = getChannel().getNextDeliveryTag(); - getProtocolSession().getProtocolOutputConverter().writeDeliver(entry.getMessage(), getChannel().getChannelId(), deliveryTag, getConumerTag()); + sendToClient(entry, deliveryTag); } entry.dispose(storeContext); @@ -166,12 +173,14 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage static final class AckSubscription extends SubscriptionImpl { - public AckSubscription(int channelId, AMQProtocolSession protocolSession, + public AckSubscription(AMQChannel channel, AMQProtocolSession protocolSession, AMQShortString consumerTag, FieldTable filters, - boolean noLocal, FlowCreditManager creditManager) + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) throws AMQException { - super(channelId, protocolSession, consumerTag, filters, noLocal, creditManager); + super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); } public boolean isBrowser() @@ -189,7 +198,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage */ public void send(QueueEntry entry) throws AMQException { - StoreContext storeContext = getChannel().getStoreContext(); + try { // if we do not need to wait for client acknowledgements // we can decrement the reference count immediately. @@ -206,12 +215,10 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage long deliveryTag = getChannel().getNextDeliveryTag(); + recordMessageDelivery(entry, deliveryTag); + sendToClient(entry, deliveryTag); - getChannel().addUnacknowledgedMessage(entry, deliveryTag, this); - - getProtocolSession().getProtocolOutputConverter().writeDeliver(entry.getMessage(), getChannel().getChannelId(), deliveryTag, getConumerTag()); - } } finally @@ -225,7 +232,6 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage } - } @@ -252,16 +258,13 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage - public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession, + public SubscriptionImpl(AMQChannel channel , AMQProtocolSession protocolSession, AMQShortString consumerTag, FieldTable arguments, - boolean noLocal, FlowCreditManager creditManager) + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) throws AMQException { - AMQChannel channel = protocolSession.getChannel(channelId); - if (channel == null) - { - throw new AMQException(AMQConstant.NOT_FOUND, "channel :" + channelId + " not found in protocol session"); - } _channel = channel; _consumerTag = consumerTag; @@ -274,11 +277,12 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage _filters = FilterManagerFactory.createManager(arguments); + _deliveryMethod = deliveryMethod; + _recordMethod = recordMethod; - - if (_filters != null) + if (arguments != null) { Object autoClose = arguments.get(AMQPFilterTypes.AUTO_CLOSE.getValue()); if (autoClose != null) @@ -421,6 +425,12 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage return _autoClose; } + public FlowCreditManager getCreditManager() + { + return _creditManager; + } + + public void close() { boolean closed = false; @@ -484,7 +494,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage return _channel; } - public AMQShortString getConumerTag() + public AMQShortString getConsumerTag() { return _consumerTag; } @@ -514,6 +524,11 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage { _stateListener.stateChange(this, State.SUSPENDED, State.ACTIVE); } + else + { + // this is a hack to get round the issue of increasing bytes credit + _stateListener.stateChange(this, State.ACTIVE, State.ACTIVE); + } } else { @@ -536,19 +551,38 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage } - public Object getQueueContext() + public QueueEntry getLastSeenEntry() { return _queueContext.get(); } - public boolean setQueueContext(Object expected, Object newvalue) + public boolean setLastSeenEntry(QueueEntry expected, QueueEntry newvalue) { return _queueContext.compareAndSet(expected,newvalue); } + protected void sendToClient(final QueueEntry entry, final long deliveryTag) + throws AMQException + { + _deliveryMethod.deliverToClient(this,entry,deliveryTag); + } + + + protected void recordMessageDelivery(final QueueEntry entry, final long deliveryTag) + { + _recordMethod.recordMessageDelivery(this,entry,deliveryTag); + } + + public boolean isActive() { return getState() == State.ACTIVE; } + + public QueueEntry.SubscriptionAcquiredState getOwningState() + { + return _owningState; + } + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java index 3ff9b8c356..8b764efa42 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java @@ -178,35 +178,35 @@ public class VirtualHost implements Accessable private void initialiseHouseKeeping(final Configuration hostConfig) { - long period = hostConfig.getLong("housekeeping.expiredMessageCheckPeriod", DEFAULT_HOUSEKEEPING_PERIOD); + long period = hostConfig.getLong("housekeeping.expiredMessageCheckPeriod", DEFAULT_HOUSEKEEPING_PERIOD); - /* add a timer task to iterate over queues, cleaning expired messages from queues with no consumers */ - if(period != 0L) - { - class RemoveExpiredMessagesTask extends TimerTask - { - public void run() - { - for(AMQQueue q : _queueRegistry.getQueues()) - { - - try - { - q.removeExpiredIfNoSubscribers(); - } - catch (AMQException e) - { - _logger.error("Exception in housekeeping for queue: " + q.getName().toString(),e); - throw new RuntimeException(e); - } - } - } - } - - _houseKeepingTimer.scheduleAtFixedRate(new RemoveExpiredMessagesTask(), - period/2, - period); - } + /* add a timer task to iterate over queues, cleaning expired messages from queues with no consumers */ + if(period != 0L) + { + class RemoveExpiredMessagesTask extends TimerTask + { + public void run() + { + for(AMQQueue q : _queueRegistry.getQueues()) + { + + try + { + q.removeExpiredIfNoSubscribers(); + } + catch (AMQException e) + { + _logger.error("Exception in housekeeping for queue: " + q.getName().toString(),e); + throw new RuntimeException(e); + } + } + } + } + + _houseKeepingTimer.scheduleAtFixedRate(new RemoveExpiredMessagesTask(), + period/2, + period); + } } private void initialiseMessageStore(Configuration config) throws Exception diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java index e6de3ab560..df8b59ec19 100644 --- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java +++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java @@ -22,7 +22,6 @@ package org.apache.qpid.tools.messagestore.commands; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.queue.AMQQueueImpl; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java index 22b58b3aa4..3e8b1d0998 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java @@ -63,7 +63,7 @@ public class DestWildExchangeTest extends TestCase public void testNoRoute() throws AMQException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*#b"), false, null, false, _vhost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*#b"), false, null, false, _vhost, null); _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null); @@ -78,7 +78,7 @@ public class DestWildExchangeTest extends TestCase public void testDirectMatch() throws AMQException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("ab"), false, null, false, _vhost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("ab"), false, null, false, _vhost, null); _exchange.registerQueue(new AMQShortString("a.b"), queue, null); @@ -118,7 +118,7 @@ public class DestWildExchangeTest extends TestCase public void testStarMatch() throws AMQException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*"), false, null, false, _vhost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*"), false, null, false, _vhost, null); _exchange.registerQueue(new AMQShortString("a.*"), queue, null); @@ -176,7 +176,7 @@ public class DestWildExchangeTest extends TestCase public void testHashMatch() throws AMQException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); _exchange.registerQueue(new AMQShortString("a.#"), queue, null); @@ -272,7 +272,7 @@ public class DestWildExchangeTest extends TestCase public void testMidHash() throws AMQException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null); @@ -316,7 +316,7 @@ public class DestWildExchangeTest extends TestCase public void testMatchafterHash() throws AMQException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); _exchange.registerQueue(new AMQShortString("a.*.#.b.c"), queue, null); @@ -389,7 +389,7 @@ public class DestWildExchangeTest extends TestCase public void testHashAfterHash() throws AMQException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); _exchange.registerQueue(new AMQShortString("a.*.#.b.c.#.d"), queue, null); @@ -429,7 +429,7 @@ public class DestWildExchangeTest extends TestCase public void testHashHash() throws AMQException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); _exchange.registerQueue(new AMQShortString("a.#.*.#.d"), queue, null); @@ -468,7 +468,7 @@ public class DestWildExchangeTest extends TestCase public void testSubMatchFails() throws AMQException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); _exchange.registerQueue(new AMQShortString("a.b.c.d"), queue, null); @@ -497,7 +497,7 @@ public class DestWildExchangeTest extends TestCase public void testMoreRouting() throws AMQException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); _exchange.registerQueue(new AMQShortString("a.b"), queue, null); @@ -518,7 +518,7 @@ public class DestWildExchangeTest extends TestCase public void testMoreQueue() throws AMQException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); _exchange.registerQueue(new AMQShortString("a.b"), queue, null); diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java index 5184bf92e0..2a2bc72950 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java @@ -132,7 +132,8 @@ public class ExchangeMBeanTest extends TestCase IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance(); _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); _queueRegistry = _virtualHost.getQueueRegistry(); - _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, _virtualHost); + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, _virtualHost, + null); _queueRegistry.registerQueue(_queue); } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java index fe8e3c8055..9924d1c770 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java @@ -72,7 +72,8 @@ public class AMQQueueAlertTest extends TestCase public void testMessageCountAlert() throws Exception { _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue1"), false, new AMQShortString("AMQueueAlertTest"), - false, _virtualHost); + false, _virtualHost, + null); _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); @@ -95,7 +96,8 @@ public class AMQQueueAlertTest extends TestCase public void testMessageSizeAlert() throws Exception { _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue2"), false, new AMQShortString("AMQueueAlertTest"), - false, _virtualHost); + false, _virtualHost, + null); _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); _queueMBean.setMaximumMessageSize(MAX_MESSAGE_SIZE); @@ -120,7 +122,8 @@ public class AMQQueueAlertTest extends TestCase public void testQueueDepthAlertNoSubscriber() throws Exception { _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue3"), false, new AMQShortString("AMQueueAlertTest"), - false, _virtualHost); + false, _virtualHost, + null); _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); _queueMBean.setMaximumQueueDepth(MAX_QUEUE_DEPTH); @@ -148,7 +151,8 @@ public class AMQQueueAlertTest extends TestCase public void testMessageAgeAlert() throws Exception { _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue4"), false, new AMQShortString("AMQueueAlertTest"), - false, _virtualHost); + false, _virtualHost, + null); _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); _queueMBean.setMaximumMessageAge(MAX_MESSAGE_AGE); @@ -335,6 +339,6 @@ public class AMQQueueAlertTest extends TestCase false, new AMQShortString("AMQueueAlertTest"), false, - _virtualHost); + _virtualHost, null); } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java index ef30fb9093..98f78e3d69 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java @@ -306,7 +306,8 @@ public class AMQQueueMBeanTest extends TestCase new LinkedList<RequiredDeliveryException>() ); - _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("AMQueueMBeanTest"), false, _virtualHost); + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("AMQueueMBeanTest"), false, _virtualHost, + null); _queueMBean = new AMQQueueMBean(_queue); _protocolSession = new TestMinaProtocolSession(); diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java index 4b8143cfb5..60f57aaf0e 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java @@ -730,6 +730,8 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } } + + private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) throws AMQException, FailoverException { diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java index bba39403a5..82970e69e1 100644 --- a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java +++ b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -1110,11 +1110,18 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi public void createQueue(final AMQShortString name, final boolean autoDelete, final boolean durable, final boolean exclusive) throws AMQException { + createQueue(name, autoDelete, durable, exclusive, null); + } + + public void createQueue(final AMQShortString name, final boolean autoDelete, final boolean durable, + final boolean exclusive, final FieldTable arguments) throws AMQException + { + new FailoverRetrySupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>() { public Object execute() throws AMQException, FailoverException { - QueueDeclareBody body = getMethodRegistry().createQueueDeclareBody(getTicket(),name,false,durable,exclusive,autoDelete,false,null); + QueueDeclareBody body = getMethodRegistry().createQueueDeclareBody(getTicket(),name,false,durable,exclusive,autoDelete,false,arguments); AMQFrame queueDeclare = body.generateFrame(_channelId); @@ -2357,6 +2364,28 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi + + public void setPrefecthLimits(final int messagePrefetch, final long sizePrefetch) throws AMQException + { + new FailoverRetrySupport<Object, AMQException>( + new FailoverProtectedOperation<Object, AMQException>() + { + public Object execute() throws AMQException, FailoverException + { + + BasicQosBody basicQosBody = getProtocolHandler().getMethodRegistry().createBasicQosBody(sizePrefetch, messagePrefetch, false); + + // todo send low water mark when protocol allows. + // todo Be aware of possible changes to parameter order as versions change. + getProtocolHandler().syncWrite(basicQosBody.generateFrame(getChannelId()), BasicQosOkBody.class); + + return null; + } + }, _connection).execute(); + } + + + /** * Declares the named exchange and type of exchange. * diff --git a/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java index 7ae2ddf66c..5907bd90af 100644 --- a/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java +++ b/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java @@ -174,7 +174,7 @@ public class TransportConnection return -1; } - private static ITransportConnection getVMTransport(BrokerDetails details, boolean AutoCreate) + private synchronized static ITransportConnection getVMTransport(BrokerDetails details, boolean AutoCreate) throws AMQVMBrokerCreationException { int port = details.getPort(); @@ -197,7 +197,7 @@ public class TransportConnection return new VmPipeTransportConnection(port); } - public static void createVMBroker(int port) throws AMQVMBrokerCreationException + public static synchronized void createVMBroker(int port) throws AMQVMBrokerCreationException { if (_acceptor == null) { @@ -275,7 +275,7 @@ public class TransportConnection } } - private static IoHandlerAdapter createBrokerInstance(int port) throws AMQVMBrokerCreationException + private static synchronized IoHandlerAdapter createBrokerInstance(int port) throws AMQVMBrokerCreationException { String protocolProviderClass = System.getProperty("amqj.protocolprovider.class", DEFAULT_QPID_SERVER); _logger.info("Creating Qpid protocol provider: " + protocolProviderClass); @@ -314,7 +314,7 @@ public class TransportConnection return provider; } - public static void killAllVMBrokers() + public synchronized static void killAllVMBrokers() { _logger.info("Killing all VM Brokers"); if (_acceptor != null) @@ -330,7 +330,7 @@ public class TransportConnection _currentVMPort = -1; } - public static void killVMBroker(int port) + public synchronized static void killVMBroker(int port) { synchronized (_inVmPipeAddress) { diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java index 39730ef3ac..241af2c861 100644 --- a/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java +++ b/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java @@ -24,6 +24,7 @@ import javax.jms.*; import javax.jms.MessageConsumer; import javax.jms.Session; import javax.jms.Message; +import javax.jms.MessageProducer; import junit.framework.TestCase; @@ -32,12 +33,16 @@ import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.AMQTopic; import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.BasicMessageProducer; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQUndefinedDestination; import org.apache.qpid.client.transport.TransportConnection; import org.apache.qpid.url.URLSyntaxException; import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.jms.*; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; /** @author Apache Software Foundation */ @@ -144,7 +149,7 @@ public class TopicSessionTest extends TestCase while(true) { publisher.publish(session1.createTextMessage("hello")); - Thread.sleep(THREADS); + Thread.sleep(20); } } catch(Exception e) diff --git a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java index 03838ca3f1..fcaae7740b 100644 --- a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java +++ b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java @@ -766,7 +766,7 @@ class MultiThreadSocketIoProcessor extends SocketIoProcessor session.increaseWrittenBytes(writtenBytes); } - if (buf.hasRemaining() || (totalFlushedBytes <= MAX_FLUSH_BYTES_PER_SESSION)) + if (buf.hasRemaining()) { // Kernel buffer is full synchronized (writeLock) diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java b/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java index ad2ab2ac0b..f1a7d4970a 100644 --- a/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java +++ b/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java @@ -469,6 +469,8 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt return false; } + + return (_offset == 0 && otherString._offset == 0 && _length == _data.length && otherString._length == otherString._data.length && Arrays.equals(_data,otherString._data)) || Arrays.equals(getBytes(),otherString.getBytes()); diff --git a/java/common/src/main/java/org/apache/qpid/pool/Job.java b/java/common/src/main/java/org/apache/qpid/pool/Job.java index b2a09ac592..d65fce0fd0 100644 --- a/java/common/src/main/java/org/apache/qpid/pool/Job.java +++ b/java/common/src/main/java/org/apache/qpid/pool/Job.java @@ -67,18 +67,22 @@ public class Job implements Runnable /** Holds the completion continuation, called upon completion of a run of the job. */ private final JobCompletionHandler _completionHandler; + private final boolean _readJob; + /** * Creates a new job that aggregates many continuations together. * * @param session The Mina session. * @param completionHandler The per job run, terminal continuation. * @param maxEvents The maximum number of aggregated continuations to process per run of the job. + * @param readJob */ - Job(IoSession session, JobCompletionHandler completionHandler, int maxEvents) + Job(IoSession session, JobCompletionHandler completionHandler, int maxEvents, final boolean readJob) { _session = session; _completionHandler = completionHandler; _maxEvents = maxEvents; + _readJob = readJob; } /** @@ -157,6 +161,11 @@ public class Job implements Runnable } } + public boolean isReadJob() + { + return _readJob; + } + /** * Another interface for a continuation. * diff --git a/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java b/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java index 8352b5af77..dd2d7038fd 100644 --- a/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java +++ b/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java @@ -59,24 +59,6 @@ import java.util.concurrent.ExecutorService; * <td> {@link Job}, {@link Job.JobCompletionHandler} * </table> * - * @todo This seems a bit bizarre. ReadWriteThreadModel creates seperate pooling filters for read and write events. - * The pooling filters themselves batch read and write events into jobs, but hand these jobs to a common thread - * pool for execution. So the same thread pool ends up handling read and write events, albeit with many threads - * so there is concurrency. But why go to the trouble of seperating out the read and write events in that case? - * Why not just batch them into jobs together? Perhaps its so that seperate thread pools could be used for these - * stages. - * - * @todo Why set an event limit of 10 on the Job? This also seems bizarre, as the job can have more than 10 events in - * it. Its just that it runs them 10 at a time, but the completion hander here checks if there are more to run - * and trips off another batch of 10 until they are all done. Why not just have a straight forward - * consumer/producer queue scenario without the batches of 10? So instead of having many jobs with batches of 10 - * in them, just have one queue of events and worker threads taking the next event. There will be coordination - * between worker threads and new events arriving on the job anyway, so the simpler scenario may have the same - * amount of contention. I can see that the batches of 10 is done, so that no job is allowed to hog the worker - * pool for too long. I'm not convinced this fairly complex scheme will actually add anything, and it might be - * better to encapsulate it under a Queue interface anyway, so that different queue implementations can easily - * be substituted in. - * * @todo The static helper methods are pointless. Could just call new. */ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCompletionHandler @@ -95,17 +77,20 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo private final int _maxEvents; + private final boolean _readFilter; + /** * Creates a named pooling filter, on the specified shared thread pool. * * @param refCountingPool The thread pool reference. * @param name The identifying name of the filter type. */ - public PoolingFilter(ReferenceCountingExecutorService refCountingPool, String name, int maxEvents) + public PoolingFilter(ReferenceCountingExecutorService refCountingPool, String name, int maxEvents, boolean readFilter) { _poolReference = refCountingPool; _name = name; _maxEvents = maxEvents; + _readFilter = readFilter; } /** @@ -166,7 +151,6 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo void fireAsynchEvent(Job job, Event event) { - // job.acquire(); //prevents this job being removed from _jobs job.add(event); final ExecutorService pool = _poolReference.getPool(); @@ -200,7 +184,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo */ public void createNewJobForSession(IoSession session) { - Job job = new Job(session, this, MAX_JOB_EVENTS); + Job job = new Job(session, this, MAX_JOB_EVENTS,_readFilter); session.setAttribute(_name, job); } @@ -216,18 +200,6 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo return (Job) session.getAttribute(_name); } - /*private Job createJobForSession(IoSession session) - { - return addJobForSession(session, new Job(session, this, _maxEvents)); - }*/ - - /*private Job addJobForSession(IoSession session, Job job) - { - // atomic so ensures all threads agree on the same job - Job existing = _jobs.putIfAbsent(session, job); - - return (existing == null) ? job : existing; - }*/ /** * Implements a terminal continuation for the {@link Job} for this filter. Whenever the Job completes its processing @@ -238,16 +210,6 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo */ public void completed(IoSession session, Job job) { - // if (job.isComplete()) - // { - // job.release(); - // if (!job.isReferenced()) - // { - // _jobs.remove(session); - // } - // } - // else - if (!job.isComplete()) { @@ -454,7 +416,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo */ public AsynchReadPoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) { - super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_read_events", MAX_JOB_EVENTS)); + super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_read_events", MAX_JOB_EVENTS),true); } /** @@ -497,7 +459,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo */ public AsynchWritePoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) { - super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_write_events", MAX_JOB_EVENTS)); + super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_write_events", MAX_JOB_EVENTS),false); } /** diff --git a/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java b/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java new file mode 100644 index 0000000000..05141aea7b --- /dev/null +++ b/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java @@ -0,0 +1,432 @@ +package org.apache.qpid.pool; + +import java.util.AbstractQueue; +import java.util.Iterator; +import java.util.Collection; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.atomic.AtomicInteger; + +/* +* +* 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. +* +*/ +public class ReadWriteJobQueue extends AbstractQueue<Runnable> implements BlockingQueue<Runnable> +{ + + private final AtomicInteger _count = new AtomicInteger(0); + + private final ReentrantLock _takeLock = new ReentrantLock(); + + private final Condition _notEmpty = _takeLock.newCondition(); + + private final ReentrantLock _putLock = new ReentrantLock(); + + private final ConcurrentLinkedQueue<Job> _readJobQueue = new ConcurrentLinkedQueue<Job>(); + + private final ConcurrentLinkedQueue<Job> _writeJobQueue = new ConcurrentLinkedQueue<Job>(); + + + private class ReadWriteJobIterator implements Iterator<Runnable> + { + + private boolean _onReads; + private Iterator<Job> _iter = _writeJobQueue.iterator(); + + public boolean hasNext() + { + if(!_iter.hasNext()) + { + if(_onReads) + { + _iter = _readJobQueue.iterator(); + _onReads = true; + return _iter.hasNext(); + } + else + { + return false; + } + } + else + { + return true; + } + } + + public Runnable next() + { + if(_iter.hasNext()) + { + return _iter.next(); + } + else + { + return null; + } + } + + public void remove() + { + _takeLock.lock(); + try + { + _iter.remove(); + _count.decrementAndGet(); + } + finally + { + _takeLock.unlock(); + } + } + } + + public Iterator<Runnable> iterator() + { + return new ReadWriteJobIterator(); + } + + public int size() + { + return _count.get(); + } + + public boolean offer(final Runnable runnable) + { + final Job job = (Job) runnable; + final ReentrantLock putLock = _putLock; + putLock.lock(); + try + { + if(job.isReadJob()) + { + _readJobQueue.offer(job); + } + else + { + _writeJobQueue.offer(job); + } + if(_count.getAndIncrement() == 0) + { + _takeLock.lock(); + try + { + _notEmpty.signal(); + } + finally + { + _takeLock.unlock(); + } + } + return true; + } + finally + { + putLock.unlock(); + } + } + + public void put(final Runnable runnable) throws InterruptedException + { + final Job job = (Job) runnable; + final ReentrantLock putLock = _putLock; + putLock.lock(); + + try + { + if(job.isReadJob()) + { + _readJobQueue.offer(job); + } + else + { + _writeJobQueue.offer(job); + } + if(_count.getAndIncrement() == 0) + { + _takeLock.lock(); + try + { + _notEmpty.signal(); + } + finally + { + _takeLock.unlock(); + } + } + + } + finally + { + putLock.unlock(); + } + } + + + + public boolean offer(final Runnable runnable, final long timeout, final TimeUnit unit) throws InterruptedException + { + final Job job = (Job) runnable; + final ReentrantLock putLock = _putLock; + putLock.lock(); + + try + { + if(job.isReadJob()) + { + _readJobQueue.offer(job); + } + else + { + _writeJobQueue.offer(job); + } + if(_count.getAndIncrement() == 0) + { + _takeLock.lock(); + try + { + _notEmpty.signal(); + } + finally + { + _takeLock.unlock(); + } + } + + return true; + } + finally + { + putLock.unlock(); + } + + } + + public Runnable take() throws InterruptedException + { + final ReentrantLock takeLock = _takeLock; + takeLock.lockInterruptibly(); + try + { + try + { + while (_count.get() == 0) + { + _notEmpty.await(); + } + } + catch (InterruptedException ie) + { + _notEmpty.signal(); + throw ie; + } + + Job job = _writeJobQueue.poll(); + if(job == null) + { + job = _readJobQueue.poll(); + } + int c = _count.getAndDecrement(); + if (c > 1) + { + _notEmpty.signal(); + } + return job; + } + finally + { + takeLock.unlock(); + } + + + } + + public Runnable poll(final long timeout, final TimeUnit unit) throws InterruptedException + { + final ReentrantLock takeLock = _takeLock; + final AtomicInteger count = _count; + long nanos = unit.toNanos(timeout); + takeLock.lockInterruptibly(); + Job job = null; + try + { + + for (;;) + { + if (count.get() > 0) + { + job = _writeJobQueue.poll(); + if(job == null) + { + job = _readJobQueue.poll(); + } + int c = count.getAndDecrement(); + if (c > 1) + { + _notEmpty.signal(); + } + break; + } + if (nanos <= 0) + { + return null; + } + try + { + nanos = _notEmpty.awaitNanos(nanos); + } + catch (InterruptedException ie) + { + _notEmpty.signal(); + throw ie; + } + } + } + finally + { + takeLock.unlock(); + } + + return job; + } + + public int remainingCapacity() + { + return Integer.MAX_VALUE; + } + + public int drainTo(final Collection<? super Runnable> c) + { + int total = 0; + + _putLock.lock(); + _takeLock.lock(); + try + { + Job job; + while((job = _writeJobQueue.peek())!= null) + { + c.add(job); + _writeJobQueue.poll(); + _count.decrementAndGet(); + total++; + } + + while((job = _readJobQueue.peek())!= null) + { + c.add(job); + _readJobQueue.poll(); + _count.decrementAndGet(); + total++; + } + + } + finally + { + _takeLock.unlock(); + _putLock.unlock(); + } + return total; + } + + public int drainTo(final Collection<? super Runnable> c, final int maxElements) + { + int total = 0; + + _putLock.lock(); + _takeLock.lock(); + try + { + Job job; + while(total<=maxElements && (job = _writeJobQueue.peek())!= null) + { + c.add(job); + _writeJobQueue.poll(); + _count.decrementAndGet(); + total++; + } + + while(total<=maxElements && (job = _readJobQueue.peek())!= null) + { + c.add(job); + _readJobQueue.poll(); + _count.decrementAndGet(); + total++; + } + + } + finally + { + _takeLock.unlock(); + _putLock.unlock(); + } + return total; + + } + + public Runnable poll() + { + final ReentrantLock takeLock = _takeLock; + takeLock.lock(); + try + { + if(_count.get() > 0) + { + Job job = _writeJobQueue.poll(); + if(job == null) + { + job = _readJobQueue.poll(); + } + _count.decrementAndGet(); + return job; + } + else + { + return null; + } + } + finally + { + takeLock.unlock(); + } + + } + + public Runnable peek() + { + final ReentrantLock takeLock = _takeLock; + takeLock.lock(); + try + { + Job job = _writeJobQueue.peek(); + if(job == null) + { + job = _readJobQueue.peek(); + } + return job; + } + finally + { + takeLock.unlock(); + } + } +} diff --git a/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java b/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java index 84c9e1f465..1359e56958 100644 --- a/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java +++ b/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java @@ -22,6 +22,9 @@ package org.apache.qpid.pool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.LinkedBlockingQueue; /** * ReferenceCountingExecutorService wraps an ExecutorService in order to provide shared reference to it. It counts @@ -111,7 +114,12 @@ public class ReferenceCountingExecutorService { if (_refCount++ == 0) { - _pool = Executors.newFixedThreadPool(_poolSize); +// _pool = Executors.newFixedThreadPool(_poolSize); + + // Use a job queue that biases to writes + _pool = new ThreadPoolExecutor(_poolSize, _poolSize, + 0L, TimeUnit.MILLISECONDS, + new ReadWriteJobQueue()); } return _pool; diff --git a/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java b/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java index 6aec1cb164..fb616e2b59 100644 --- a/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java +++ b/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java @@ -63,7 +63,8 @@ public class ManagementConsoleTest extends TestCase { // If this test fails due to changes in the broker code, // then the constants in the Constants.java shoule be updated accordingly - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueueForManagement"), false, null, false, _virtualHost); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueueForManagement"), false, null, false, _virtualHost, + null); AMQManagedObject mbean = new AMQQueueMBean(queue); MBeanInfo mbeanInfo = mbean.getMBeanInfo(); diff --git a/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java b/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java index 600ae3f7be..779d47be1c 100644 --- a/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java +++ b/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java @@ -34,7 +34,6 @@ import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.exchange.AbstractExchange; import org.apache.qpid.server.management.MBeanConstructor; import org.apache.qpid.server.management.MBeanDescription; -import org.apache.qpid.server.queue.AMQQueueImpl; import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.AMQQueue; @@ -207,12 +206,7 @@ public class DiagnosticExchange extends AbstractExchange } - @Override - public Map<AMQShortString, List<AMQQueue>> getBindings() { - // TODO Auto-generated method stub - return null; - } - + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) { // TODO Auto-generated method stub diff --git a/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java b/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java index 7589a7974b..067125de56 100644 --- a/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java +++ b/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java @@ -7,7 +7,6 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.queue.AMQQueueImpl; import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.virtualhost.VirtualHost; diff --git a/java/systests/pom.xml b/java/systests/pom.xml index e6c1fa092e..f25410c2d2 100644 --- a/java/systests/pom.xml +++ b/java/systests/pom.xml @@ -151,7 +151,6 @@ <MessageReturnTest>-n MessageReturnTest org.apache.qpid.server.queue.MessageReturnTest </MessageReturnTest> <QueueDepthWithSelectorTest>-n QueueDepthWithSelectorTest org.apache.qpid.server.queue.QueueDepthWithSelectorTest </QueueDepthWithSelectorTest> <!--<SubscriptionManagerTest>-n SubscriptionManagerTest org.apache.qpid.server.queue.SubscriptionManagerTest </SubscriptionManagerTest>--> - <SubscriptionSetTest>-n SubscriptionSetTest org.apache.qpid.server.queue.SubscriptionSetTest </SubscriptionSetTest> <TimeToLiveTest>-n TimeToLiveTest org.apache.qpid.server.queue.TimeToLiveTest </TimeToLiveTest> <TxnBufferTest>-n TxnBufferTest org.apache.qpid.server.txn.TxnBufferTest </TxnBufferTest> <!--<TxnTest>-n TxnTest org.apache.qpid.server.txn.TxnTest </TxnTest>--> diff --git a/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java b/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java index 144e4be6af..4e39367da1 100644 --- a/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java +++ b/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java @@ -26,13 +26,16 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.QueueEntryImpl; import org.apache.qpid.server.queue.MessageHandleFactory; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.queue.AMQMessageHandle; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.store.TestableMemoryMessageStore; import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.store.MemoryMessageStore; import org.apache.qpid.server.txn.NonTransactionalContext; import org.apache.qpid.server.txn.TransactionalContext; @@ -100,12 +103,16 @@ public class TxAckTest extends TestCase private final List<Long> _unacked; private StoreContext _storeContext = new StoreContext(); - Scenario(int messageCount, List<Long> acked, List<Long> unacked) throws AMQException + Scenario(int messageCount, List<Long> acked, List<Long> unacked) throws Exception { TransactionalContext txnContext = new NonTransactionalContext(new TestableMemoryMessageStore(), _storeContext, null, new LinkedList<RequiredDeliveryException>() ); + AMQQueue queue = + AMQQueueFactory.createAMQQueueImpl(new AMQShortString("test"), false, null, false, new VirtualHost("", new MemoryMessageStore()), + null); + for (int i = 0; i < messageCount; i++) { long deliveryTag = i + 1; @@ -140,7 +147,7 @@ public class TxAckTest extends TestCase }; TestMessage message = new TestMessage(deliveryTag, i, info, txnContext.getStoreContext()); - _map.add(deliveryTag, new QueueEntryImpl(null,message, Long.MIN_VALUE)); + _map.add(deliveryTag, queue.enqueue(new StoreContext(), message)); } _acked = acked; _unacked = unacked; diff --git a/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java b/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java index 80470d44b3..74d7b39b72 100644 --- a/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java +++ b/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java @@ -33,6 +33,7 @@ import org.apache.qpid.server.store.StoreContext; import org.apache.qpid.server.txn.NonTransactionalContext; import org.apache.qpid.server.txn.TransactionalContext; import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.log4j.Logger; @@ -236,7 +237,7 @@ public class AbstractHeadersExchangeTestBase extends TestCase return properties; } - static class TestQueue extends AMQQueueImpl + static class TestQueue extends SimpleAMQQueue { final List<HeadersExchangeTest.Message> messages = new ArrayList<HeadersExchangeTest.Message>(); @@ -250,12 +251,160 @@ public class AbstractHeadersExchangeTestBase extends TestCase * not invoked. It is unnecessary since for this test we only care to know whether the message was * sent to the queue; the queue processing logic is not being tested. * @param msg - * @param deliverFirst * @throws AMQException */ - public void process(StoreContext context, QueueEntry msg, boolean deliverFirst) throws AMQException + @Override + public QueueEntry enqueue(StoreContext context, AMQMessage msg) throws AMQException { - messages.add( new HeadersExchangeTest.Message(msg.getMessage())); + messages.add( new HeadersExchangeTest.Message(msg)); + return new QueueEntry() + { + + public AMQQueue getQueue() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public AMQMessage getMessage() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getSize() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean getDeliveredToConsumer() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean expired() throws AMQException + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isAcquired() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean acquire() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean acquire(Subscription sub) + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean delete() + { + return false; + } + + public boolean isDeleted() + { + return false; + } + + public boolean acquiredBySubscription() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setDeliveredToSubscription() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void release() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public String debugIdentity() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean immediateAndNotDelivered() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setRedelivered(boolean b) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public Subscription getDeliveredSubscription() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void reject() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void reject(Subscription subscription) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isRejectedBy(Subscription subscription) + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public void requeue(StoreContext storeContext) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void dequeue(final StoreContext storeContext) throws FailedDequeueException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void dispose(final StoreContext storeContext) throws MessageCleanupException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void restoreCredit() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void discard(StoreContext storeContext) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isQueueDeleted() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public void addStateChangeListener(StateChangeListener listener) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean removeStateChangeListener(StateChangeListener listener) + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public int compareTo(final QueueEntry o) + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + }; } boolean isInQueue(Message msg) diff --git a/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java b/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java index 6f8963131b..8e7038eec3 100644 --- a/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java +++ b/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java @@ -59,7 +59,7 @@ public class AMQProtocolSessionMBeanTest extends TestCase false, new AMQShortString("test"), true, - _protocolSession.getVirtualHost()); + _protocolSession.getVirtualHost(), null); AMQChannel channel = new AMQChannel(_protocolSession,2, _messageStore); channel.setDefaultQueue(queue); _protocolSession.addChannel(channel); diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java index 30240217a2..dae81a875d 100644 --- a/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java +++ b/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java @@ -78,7 +78,8 @@ public class AckTest extends TestCase _protocolSession.addChannel(_channel); - _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("myQ"), false, new AMQShortString("guest"), true, ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test")); + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("myQ"), false, new AMQShortString("guest"), true, ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"), + null); } diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java b/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java deleted file mode 100644 index 3ff691c792..0000000000 --- a/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * - * 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.qpid.server.queue; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.handler.OnCurrentThreadExecutor; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.subscription.Subscription; - -import java.util.*; -import java.util.concurrent.Executor; - -/** - * Tests delivery in the face of concurrent incoming _messages, subscription alterations - * and attempts to asynchronously process queued _messages. - */ -public class ConcurrencyTestDisabled extends MessageTestHelper -{ - private final Random random = new Random(); - - private final int numMessages = 1000; - - private final List<SubscriptionTestHelper> _subscribers = new ArrayList<SubscriptionTestHelper>(); - private final Set<Subscription> _active = new HashSet<Subscription>(); - private final List<QueueEntry> _messages = new ArrayList<QueueEntry>(); - private int next = 0;//index to next message to send - private final List<QueueEntry> _received = Collections.synchronizedList(new ArrayList<QueueEntry>()); - private final Executor _executor = new OnCurrentThreadExecutor(); - private final List<Thread> _threads = new ArrayList<Thread>(); - - private final SubscriptionSet _subscriptionMgr; - private final ConcurrentSelectorDeliveryManager _deliveryMgr; - - private boolean isComplete; - private boolean failed; - private VirtualHost _virtualHost; - - public ConcurrencyTestDisabled() throws Exception - { - - IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance(); - _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); - _deliveryMgr = new ConcurrentSelectorDeliveryManager( new AMQQueueImpl(new AMQShortString("myQ"), false, new AMQShortString("guest"), false, - _virtualHost)); - _subscriptionMgr = _deliveryMgr.getSubscribers(); - } - - public void testConcurrent1() throws InterruptedException, AMQException - { - initSubscriptions(10); - initMessages(numMessages); - initThreads(1, 4, 4, 4); - doRun(); - check(); - } - - public void testConcurrent2() throws InterruptedException, AMQException - { - initSubscriptions(10); - initMessages(numMessages); - initThreads(4, 2, 2, 2); - doRun(); - check(); - } - - void check() - { - assertFalse("Failed", failed); - - _deliveryMgr.processAsync(_executor); - - assertEquals("Did not recieve the correct number of messages", _messages.size(), _received.size()); - for(int i = 0; i < _messages.size(); i++) - { - assertEquals("Wrong message at " + i, _messages.get(i), _received.get(i)); - } - } - - void initSubscriptions(int subscriptions) - { - for(int i = 0; i < subscriptions; i++) - { - _subscribers.add(new SubscriptionTestHelper("Subscriber" + i, _received)); - } - } - - void initMessages(int messages) throws AMQException - { - for(int i = 0; i < messages; i++) - { - _messages.add(message()); - } - } - - void initThreads(int senders, int subscribers, int suspenders, int processors) - { - addThreads(senders, senders == 1 ? new Sender() : new OrderedSender()); - addThreads(subscribers, new Subscriber()); - addThreads(suspenders, new Suspender()); - addThreads(processors, new Processor()); - } - - void addThreads(int count, Runnable runner) - { - for(int i = 0; i < count; i++) - { - _threads.add(new Thread(runner, runner.toString())); - } - } - - void doRun() throws InterruptedException - { - for(Thread t : _threads) - { - t.start(); - } - - for(Thread t : _threads) - { - t.join(); - } - } - - private void toggle(Subscription s) - { - synchronized (_active) - { - if (_active.contains(s)) - { - _active.remove(s); - Subscription result = _subscriptionMgr.removeSubscriber(s); - assertTrue("Removed subscription " + result + " but trying to remove subscription " + s, - result != null && result.equals(s)); - } - else - { - _active.add(s); - _subscriptionMgr.addSubscriber(s); - } - } - } - - private QueueEntry nextMessage() - { - synchronized (_messages) - { - if (next < _messages.size()) - { - return _messages.get(next++); - } - else - { - if (!_deliveryMgr.hasQueuedMessages()) { - isComplete = true; - } - return null; - } - } - } - - private boolean randomBoolean() - { - return random.nextBoolean(); - } - - private SubscriptionTestHelper randomSubscriber() - { - return _subscribers.get(random.nextInt(_subscribers.size())); - } - - private class Sender extends Runner - { - void doRun() throws Throwable - { - QueueEntry msg = nextMessage(); - if (msg != null) - { - _deliveryMgr.deliver(null, new AMQShortString(toString()), msg, false); - } - } - } - - private class OrderedSender extends Sender - { - synchronized void doRun() throws Throwable - { - super.doRun(); - } - } - - private class Suspender extends Runner - { - void doRun() throws Throwable - { - randomSubscriber().setSuspended(randomBoolean()); - } - } - - private class Subscriber extends Runner - { - void doRun() throws Throwable - { - toggle(randomSubscriber()); - } - } - - private class Processor extends Runner - { - void doRun() throws Throwable - { - _deliveryMgr.processAsync(_executor); - } - } - - private abstract class Runner implements Runnable - { - public void run() - { - try - { - while (!stop()) - { - doRun(); - } - } - catch (Throwable t) - { - failed = true; - t.printStackTrace(); - } - } - - abstract void doRun() throws Throwable; - - boolean stop() - { - return isComplete || failed; - } - } - - public static junit.framework.Test suite() - { - return new junit.framework.TestSuite(ConcurrencyTestDisabled.class); - } - -} diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/PriorityTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/PriorityTest.java new file mode 100644 index 0000000000..3d53b87b45 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/server/queue/PriorityTest.java @@ -0,0 +1,171 @@ +/* +* +* 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.qpid.server.queue; + +import junit.framework.TestCase; +import junit.framework.Assert; +import org.apache.log4j.Logger; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.jndi.PropertiesFileInitialContextFactory; +import org.apache.qpid.url.URLSyntaxException; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; + +import javax.jms.*; +import javax.naming.NamingException; +import javax.naming.Context; +import javax.naming.spi.InitialContextFactory; +import java.util.Hashtable; + +/** Test Case provided by client Non-functional Test NF101: heap exhaustion behaviour */ +public class PriorityTest extends TestCase +{ + private static final Logger _logger = Logger.getLogger(PriorityTest.class); + + + protected final String BROKER = "vm://:1"; + protected final String VHOST = "/test"; + protected final String QUEUE = "PriorityQueue"; + + + private static final int MSG_COUNT = 50; + + protected void setUp() throws Exception + { + super.setUp(); + + if (usingInVMBroker()) + { + TransportConnection.createVMBroker(1); + } + + + } + + private boolean usingInVMBroker() + { + return BROKER.startsWith("vm://"); + } + + protected void tearDown() throws Exception + { + if (usingInVMBroker()) + { + TransportConnection.killAllVMBrokers(); + } + super.tearDown(); + } + + public void testPriority() throws JMSException, NamingException, AMQException + { + InitialContextFactory factory = new PropertiesFileInitialContextFactory(); + + Hashtable<String, String> env = new Hashtable<String, String>(); + + env.put("connectionfactory.connection", "amqp://guest:guest@TTL_TEST_ID" + VHOST + "?brokerlist='" + BROKER + "'"); + env.put("queue.queue", QUEUE); + + Context context = factory.getInitialContext(env); + + Connection producerConnection = ((ConnectionFactory) context.lookup("connection")).createConnection(); + + Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + final FieldTable arguments = new FieldTable(); + arguments.put(new AMQShortString("x-qpid-priorities"),10); + + ((AMQSession) producerSession).createQueue(new AMQShortString(QUEUE), true, false, false, arguments); + + Queue queue = (Queue) context.lookup("queue"); + + ((AMQSession) producerSession).declareAndBind((AMQDestination)queue); + + + + + + + producerConnection.start(); + + + MessageProducer producer = producerSession.createProducer(queue); + + + + + + for (int msg = 0; msg < MSG_COUNT; msg++) + { + producer.setPriority(msg % 10); + producer.send(nextMessage(msg, false, producerSession, producer)); + } + + producer.close(); + producerSession.close(); + producerConnection.close(); + + + Connection consumerConnection = ((ConnectionFactory) context.lookup("connection")).createConnection(); + Session consumerSession = consumerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer = consumerSession.createConsumer(queue); + + + + + consumerConnection.start(); + + Message received; + //Receive Message 0 + StringBuilder buf = new StringBuilder(); + int receivedCount = 0; + Message previous = null; + while((received = consumer.receive(1000))!=null) + { + if(previous != null) + { + assertTrue("Messages arrived in unexpected order", (previous.getJMSPriority() > received.getJMSPriority()) || ((previous.getJMSPriority() == received.getJMSPriority()) && previous.getIntProperty("msg") < received.getIntProperty("msg")) ); + } + + previous = received; + receivedCount++; + } + + assertEquals("Incorrect number of message received", 50, receivedCount); + + producerSession.close(); + producer.close(); + + } + + private Message nextMessage(int msg, boolean first, Session producerSession, MessageProducer producer) throws JMSException + { + Message send = producerSession.createTextMessage("Message: " + msg); + send.setIntProperty("msg", msg); + + return send; + } + + +} diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java deleted file mode 100644 index 1200d7fc2b..0000000000 --- a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * - * 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.qpid.server.queue; - -import junit.framework.TestCase; -import org.apache.qpid.server.subscription.Subscription; - -public class SubscriptionManagerTest extends TestCase -{ - private final SubscriptionSet mgr = new SubscriptionSet(); - - public void testBasicSubscriptionManagement() - { - assertTrue(mgr.isEmpty()); - assertFalse(mgr.hasActiveSubscribers()); - SubscriptionTestHelper s1 = new SubscriptionTestHelper("S1"); - mgr.addSubscriber(s1); - assertFalse(mgr.isEmpty()); - assertTrue(mgr.hasActiveSubscribers()); - - SubscriptionTestHelper s2 = new SubscriptionTestHelper("S2"); - mgr.addSubscriber(s2); - - s2.setSuspended(true); - assertFalse(mgr.isEmpty()); - assertTrue(mgr.hasActiveSubscribers()); - assertTrue(s2.isSuspended()); - assertFalse(s1.isSuspended()); - - s1.setSuspended(true); - assertFalse(mgr.hasActiveSubscribers()); - - mgr.removeSubscriber(s1); - assertFalse(mgr.isEmpty()); - mgr.removeSubscriber(s2); - assertTrue(mgr.isEmpty()); - } - - public void testRoundRobin() - { - SubscriptionTestHelper a = new SubscriptionTestHelper("A"); - SubscriptionTestHelper b = new SubscriptionTestHelper("B"); - SubscriptionTestHelper c = new SubscriptionTestHelper("C"); - SubscriptionTestHelper d = new SubscriptionTestHelper("D"); - mgr.addSubscriber(a); - mgr.addSubscriber(b); - mgr.addSubscriber(c); - mgr.addSubscriber(d); - - for (int i = 0; i < 3; i++) - { - assertEquals(a, mgr.nextSubscriber(null)); - assertEquals(b, mgr.nextSubscriber(null)); - assertEquals(c, mgr.nextSubscriber(null)); - assertEquals(d, mgr.nextSubscriber(null)); - } - - c.setSuspended(true); - - for (int i = 0; i < 3; i++) - { - assertEquals(a, mgr.nextSubscriber(null)); - assertEquals(b, mgr.nextSubscriber(null)); - assertEquals(d, mgr.nextSubscriber(null)); - } - - mgr.removeSubscriber(a); - d.setSuspended(true); - c.setSuspended(false); - Subscription e = new SubscriptionTestHelper("D"); - mgr.addSubscriber(e); - - for (int i = 0; i < 3; i++) - { - assertEquals(b, mgr.nextSubscriber(null)); - assertEquals(c, mgr.nextSubscriber(null)); - assertEquals(e, mgr.nextSubscriber(null)); - } - } - - public static junit.framework.Test suite() - { - return new junit.framework.TestSuite(SubscriptionManagerTest.class); - } -} diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java deleted file mode 100644 index bcf54693d3..0000000000 --- a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * - * 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.qpid.server.queue; - -import junit.framework.TestCase; - -public class SubscriptionSetTest extends TestCase -{ - /** - * A SubscriptionSet that counts the number of items scanned. - */ - static class TestSubscriptionSet extends SubscriptionSet - { - private int scanned = 0; - - void resetScanned() - { - scanned = 0; - } - - protected void subscriberScanned() - { - ++scanned; - } - - int getScanned() - { - return scanned; - } - } - - final SubscriptionTestHelper sub1 = new SubscriptionTestHelper("1"); - final SubscriptionTestHelper sub2 = new SubscriptionTestHelper("2"); - final SubscriptionTestHelper sub3 = new SubscriptionTestHelper("3"); - - final SubscriptionTestHelper suspendedSub1 = new SubscriptionTestHelper("sus1", true); - final SubscriptionTestHelper suspendedSub2 = new SubscriptionTestHelper("sus2", true); - final SubscriptionTestHelper suspendedSub3 = new SubscriptionTestHelper("sus3", true); - - public void testNextMessage() - { - SubscriptionSet ss = new SubscriptionSet(); - assertNull(ss.nextSubscriber(null)); - assertEquals(0, ss.getCurrentSubscriber()); - - ss.addSubscriber(sub1); - assertEquals(sub1, ss.nextSubscriber(null)); - assertEquals(1, ss.getCurrentSubscriber()); - assertEquals(sub1, ss.nextSubscriber(null)); - assertEquals(1, ss.getCurrentSubscriber()); - - ss.addSubscriber(sub2); - ss.addSubscriber(sub3); - - assertEquals(sub2, ss.nextSubscriber(null)); - assertEquals(2, ss.getCurrentSubscriber()); - - assertEquals(sub3, ss.nextSubscriber(null)); - assertEquals(3, ss.getCurrentSubscriber()); - } - - public void testNextMessageWhenAllSuspended() - { - SubscriptionSet ss = createAllSuspendedSubscriptionSet(); - assertNull(ss.nextSubscriber(null)); - assertEquals(3, ss.getCurrentSubscriber()); - - assertNull(ss.nextSubscriber(null)); - assertEquals(3, ss.getCurrentSubscriber()); - } - - private TestSubscriptionSet createAllSuspendedSubscriptionSet() - { - TestSubscriptionSet ss = new TestSubscriptionSet(); - ss.addSubscriber(suspendedSub1); - ss.addSubscriber(suspendedSub2); - ss.addSubscriber(suspendedSub3); - return ss; - } - - public void testNextMessageAfterRemove() - { - SubscriptionSet ss = new SubscriptionSet(); - ss.addSubscriber(suspendedSub1); - ss.addSubscriber(suspendedSub2); - ss.addSubscriber(sub3); - assertEquals(sub3, ss.nextSubscriber(null)); - assertEquals(3, ss.getCurrentSubscriber()); - - assertEquals(suspendedSub1, ss.removeSubscriber(suspendedSub1)); - - assertEquals(sub3, ss.nextSubscriber(null)); // Current implementation handles OutOfBoundsException here. - assertEquals(2, ss.getCurrentSubscriber()); - } - - public void testNextMessageOverScanning() - { - TestSubscriptionSet ss = new TestSubscriptionSet(); - SubscriptionTestHelper sub = new SubscriptionTestHelper("test"); - ss.addSubscriber(suspendedSub1); - ss.addSubscriber(sub); - ss.addSubscriber(suspendedSub3); - assertEquals(sub, ss.nextSubscriber(null)); - assertEquals(2, ss.getCurrentSubscriber()); - assertEquals(2, ss.getScanned()); - - ss.resetScanned(); - sub.setSuspended(true); - assertNull(ss.nextSubscriber(null)); - assertEquals(3, ss.getCurrentSubscriber()); - // Current implementation overscans by one item here. - assertEquals(ss.size() + 1, ss.getScanned()); - } - - public void testNextMessageOverscanWorstCase() { - TestSubscriptionSet ss = createAllSuspendedSubscriptionSet(); - ss.nextSubscriber(null); - // Scans the subscriptions twice. - assertEquals(ss.size() * 2, ss.getScanned()); - } - - public static junit.framework.Test suite() - { - return new junit.framework.TestSuite(SubscriptionSetTest.class); - } -} diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java b/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java index 624a368b1f..7fd46474ab 100644 --- a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java +++ b/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java @@ -106,12 +106,12 @@ public class SubscriptionTestHelper implements Subscription //To change body of implemented methods use File | Settings | File Templates. } - public Object getQueueContext() + public QueueEntry getLastSeenEntry() { return null; //To change body of implemented methods use File | Settings | File Templates. } - public boolean setQueueContext(Object expected, Object newValue) + public boolean setLastSeenEntry(QueueEntry expected, QueueEntry newValue) { return false; //To change body of implemented methods use File | Settings | File Templates. } @@ -126,7 +126,7 @@ public class SubscriptionTestHelper implements Subscription //no-op } - public AMQShortString getConumerTag() + public AMQShortString getConsumerTag() { return null; //To change body of implemented methods use File | Settings | File Templates. } @@ -141,6 +141,11 @@ public class SubscriptionTestHelper implements Subscription return null; } + public QueueEntry.SubscriptionAcquiredState getOwningState() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + public void queueDeleted(AMQQueue queue) { } diff --git a/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java b/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java index 57bc173812..dea52fea5a 100644 --- a/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java +++ b/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java @@ -25,7 +25,6 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.qpid.server.queue.AMQQueueImpl; import org.apache.qpid.server.queue.MessageMetaData; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.virtualhost.VirtualHost; diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java new file mode 100644 index 0000000000..347217f732 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java @@ -0,0 +1,209 @@ +/* +* +* 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.qpid.test.client; + +import org.apache.qpid.test.VMTestCase; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.AMQException; +import org.apache.log4j.Logger; + +import javax.jms.*; +import javax.naming.NamingException; +import java.util.Enumeration; + +public class FlowControlTest extends VMTestCase +{ + private static final Logger _logger = Logger.getLogger(FlowControlTest.class); + + private Connection _clientConnection; + private Session _clientSession; + private Queue _queue; + + public void setUp() throws Exception + { + + super.setUp(); + + + } + + /** + * Simply + */ + public void testBasicBytesFlowControl() throws JMSException, NamingException, AMQException + { + _queue = new AMQQueue("amq.direct","testqueue");//(Queue) _context.lookup("queue"); + + //Create Client + _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + _clientConnection.start(); + + _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + //Ensure _queue is created + _clientSession.createConsumer(_queue).close(); + + Connection producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + producerConnection.start(); + + Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(_queue); + + BytesMessage m1 = producerSession.createBytesMessage(); + m1.writeBytes(new byte[128]); + m1.setIntProperty("msg",1); + producer.send(m1); + BytesMessage m2 = producerSession.createBytesMessage(); + m2.writeBytes(new byte[128]); + m2.setIntProperty("msg",2); + producer.send(m2); + BytesMessage m3 = producerSession.createBytesMessage(); + m3.writeBytes(new byte[256]); + m3.setIntProperty("msg",3); + producer.send(m3); + + producer.close(); + producerSession.close(); + producerConnection.close(); + + + Connection consumerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + Session consumerSession = consumerConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + ((AMQSession)consumerSession).setPrefecthLimits(0,256); + MessageConsumer recv = consumerSession.createConsumer(_queue); + consumerConnection.start(); + + Message r1 = recv.receive(RECEIVE_TIMEOUT); + assertNotNull("First message not received", r1); + assertEquals("Messages in wrong order", 1, r1.getIntProperty("msg")); + + Message r2 = recv.receive(RECEIVE_TIMEOUT); + assertNotNull("Second message not received", r2); + assertEquals("Messages in wrong order", 2, r2.getIntProperty("msg")); + + Message r3 = recv.receiveNoWait(); + assertNull("Third message incorrectly delivered", r3); + + r1.acknowledge(); + + r3 = recv.receiveNoWait(); + assertNull("Third message incorrectly delivered", r3); + + r2.acknowledge(); + + + r3 = recv.receive(RECEIVE_TIMEOUT); + assertNotNull("Third message not received", r3); + assertEquals("Messages in wrong order", 3, r3.getIntProperty("msg")); + + r3.acknowledge(); + recv.close(); + consumerSession.close(); + consumerConnection.close(); + + } + + public void testTwoConsumersBytesFlowControl() throws JMSException, NamingException, AMQException + { + _queue = new AMQQueue("amq.direct","testqueue1");//(Queue) _context.lookup("queue"); + + //Create Client + _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + _clientConnection.start(); + + _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + //Ensure _queue is created + _clientSession.createConsumer(_queue).close(); + + Connection producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + producerConnection.start(); + + Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(_queue); + + BytesMessage m1 = producerSession.createBytesMessage(); + m1.writeBytes(new byte[128]); + m1.setIntProperty("msg",1); + producer.send(m1); + BytesMessage m2 = producerSession.createBytesMessage(); + m2.writeBytes(new byte[256]); + m2.setIntProperty("msg",2); + producer.send(m2); + BytesMessage m3 = producerSession.createBytesMessage(); + m3.writeBytes(new byte[128]); + m3.setIntProperty("msg",3); + producer.send(m3); + + producer.close(); + producerSession.close(); + producerConnection.close(); + + + Connection consumerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + Session consumerSession1 = consumerConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + ((AMQSession)consumerSession1).setPrefecthLimits(0,256); + MessageConsumer recv1 = consumerSession1.createConsumer(_queue); + + consumerConnection.start(); + + Message r1 = recv1.receive(RECEIVE_TIMEOUT); + assertNotNull("First message not received", r1); + assertEquals("Messages in wrong order", 1, r1.getIntProperty("msg")); + + + Message r2 = recv1.receiveNoWait(); + assertNull("Second message incorrectly delivered", r2); + + Session consumerSession2 = consumerConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + ((AMQSession)consumerSession2).setPrefecthLimits(0,256); + MessageConsumer recv2 = consumerSession2.createConsumer(_queue); + + + r2 = recv2.receive(100000L);//RECEIVE_TIMEOUT); + assertNotNull("Second message not received", r2); + assertEquals("Messages in wrong order", 2, r2.getIntProperty("msg")); + + Message r3 = recv2.receiveNoWait(); + assertNull("Third message incorrectly delivered", r3); + + r3 = recv1.receive(100000L);//RECEIVE_TIMEOUT); + assertNotNull("Third message not received", r3); + assertEquals("Messages in wrong order", 3, r3.getIntProperty("msg")); + + + + r2.acknowledge(); + r3.acknowledge(); + recv1.close(); + recv2.close(); + consumerSession1.close(); + consumerSession2.close(); + consumerConnection.close(); + + } + +} |