diff options
author | Robert Godfrey <rgodfrey@apache.org> | 2008-06-19 09:01:59 +0000 |
---|---|---|
committer | Robert Godfrey <rgodfrey@apache.org> | 2008-06-19 09:01:59 +0000 |
commit | 913d1a55b290f9a8295d5e8396c696d3cee73bc0 (patch) | |
tree | a4dde827f8b825e6535197cc12df347bd8d064db /java/broker/src/main | |
parent | f3fc904893b8c345b1aa358816d118fd0aad7d8b (diff) | |
download | qpid-python-913d1a55b290f9a8295d5e8396c696d3cee73bc0.tar.gz |
QPID-950 : Broker refactoring, copied / merged from branch
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@669431 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java/broker/src/main')
145 files changed, 11319 insertions, 8286 deletions
diff --git a/java/broker/src/main/grammar/SelectorParser.jj b/java/broker/src/main/grammar/SelectorParser.jj index eb4467f1cc..c9e01cd01f 100644 --- a/java/broker/src/main/grammar/SelectorParser.jj +++ b/java/broker/src/main/grammar/SelectorParser.jj @@ -94,7 +94,7 @@ public class SelectorParser { return this.JmsSelector();
}
catch (Throwable e) {
- throw (AMQInvalidArgumentException)new AMQInvalidArgumentException(sql, e);
+ throw (AMQInvalidArgumentException)new AMQInvalidArgumentException(sql,e);
}
}
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 9335723bc5..88d5360f3e 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 @@ -56,8 +56,9 @@ import org.apache.qpid.server.management.MBeanConstructor; import org.apache.qpid.server.management.MBeanDescription; import org.apache.qpid.server.management.ManagedBroker; import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -175,7 +176,8 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr ownerShortString = new AMQShortString(owner); } - queue = new AMQQueue(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); @@ -220,7 +222,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr try { queue.delete(); - _messageStore.removeQueue(new AMQShortString(queueName)); + _messageStore.removeQueue(queue); } catch (AMQException ex) 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 1314b2b715..847c8b8459 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 @@ -21,7 +21,6 @@ package org.apache.qpid.server; import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.configuration.Configured; import org.apache.qpid.framing.AMQShortString; @@ -30,14 +29,23 @@ import org.apache.qpid.framing.ContentBody; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.server.ack.UnacknowledgedMessage; import org.apache.qpid.server.ack.UnacknowledgedMessageMap; import org.apache.qpid.server.ack.UnacknowledgedMessageMapImpl; import org.apache.qpid.server.configuration.Configurator; -import org.apache.qpid.server.exchange.NoRouteException; import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.NoRouteException; +import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.flow.Pre0_10CreditManager; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.*; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.IncomingMessage; +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; @@ -45,13 +53,13 @@ import org.apache.qpid.server.txn.NonTransactionalContext; import org.apache.qpid.server.txn.TransactionalContext; import java.util.Collection; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; public class AMQChannel { @@ -61,13 +69,8 @@ public class AMQChannel private final int _channelId; - // private boolean _transactional; - - private long _prefetch_HighWaterMark; - private long _prefetch_LowWaterMark; - - private long _prefetchSize; + private final Pre0_10CreditManager _creditManager = new Pre0_10CreditManager(0l,0l); /** * The delivery tag is unique per channel. This is pre-incremented before putting into the deliver frame so that @@ -86,10 +89,11 @@ public class AMQChannel * been received by this channel. As the frames are received the message gets updated and once all frames have been * received the message can then be routed. */ - private AMQMessage _currentMessage; + private IncomingMessage _currentMessage; + + /** Maps from consumer tag to subscription instance. Allows us to unsubscribe from a queue. */ + private final Map<AMQShortString, Subscription> _tag2SubscriptionMap = new HashMap<AMQShortString, Subscription>(); - /** Maps from consumer tag to queue instance. Allows us to unsubscribe from a queue. */ - private final Map<AMQShortString, AMQQueue> _consumerTag2QueueMap = new ConcurrentHashMap<AMQShortString, AMQQueue>(); private final MessageStore _messageStore; @@ -97,7 +101,7 @@ public class AMQChannel private final AtomicBoolean _suspended = new AtomicBoolean(false); - private TransactionalContext _txnContext, _nonTransactedContext; + private TransactionalContext _txnContext; /** * A context used by the message store enabling it to track context for a given channel even across thread @@ -109,8 +113,6 @@ public class AMQChannel private MessageHandleFactory _messageHandleFactory = new MessageHandleFactory(); - private Set<Long> _browsedAcks = new HashSet<Long>(); - // Why do we need this reference ? - ritchiem private final AMQProtocolSession _session; private boolean _closing; @@ -118,7 +120,7 @@ public class AMQChannel @Configured(path = "advanced.enableJMSXUserID", defaultValue = "false") public boolean ENABLE_JMSXUserID; - + public AMQChannel(AMQProtocolSession session, int channelId, MessageStore messageStore) throws AMQException @@ -129,8 +131,8 @@ public class AMQChannel _session = session; _channelId = channelId; _storeContext = new StoreContext("Session: " + session.getClientIdentifier() + "; channel: " + channelId); - _prefetch_HighWaterMark = DEFAULT_PREFETCH; - _prefetch_LowWaterMark = _prefetch_HighWaterMark / 2; + + _messageStore = messageStore; // by default the session is non-transactional @@ -140,7 +142,7 @@ public class AMQChannel /** Sets this channel to be part of a local transaction */ public void setLocalTransactional() { - _txnContext = new LocalTransactionalContext(_messageStore, _storeContext, _returnMessages); + _txnContext = new LocalTransactionalContext(this); } public boolean isTransactional() @@ -156,55 +158,15 @@ public class AMQChannel return _channelId; } - public long getPrefetchCount() - { - return _prefetch_HighWaterMark; - } - - public void setPrefetchCount(long prefetchCount) - { - _prefetch_HighWaterMark = prefetchCount; - } - - public long getPrefetchSize() - { - return _prefetchSize; - } - - public void setPrefetchSize(long prefetchSize) - { - _prefetchSize = prefetchSize; - } - - public long getPrefetchLowMarkCount() - { - return _prefetch_LowWaterMark; - } - - public void setPrefetchLowMarkCount(long prefetchCount) - { - _prefetch_LowWaterMark = prefetchCount; - } - - public long getPrefetchHighMarkCount() - { - return _prefetch_HighWaterMark; - } - - public void setPrefetchHighMarkCount(long prefetchCount) - { - _prefetch_HighWaterMark = prefetchCount; - } - - public void setPublishFrame(MessagePublishInfo info, AMQProtocolSession publisher, final Exchange e) throws AMQException + public void setPublishFrame(MessagePublishInfo info, final Exchange e) throws AMQException { - _currentMessage = new AMQMessage(_messageStore.getNewMessageId(), info, _txnContext); - _currentMessage.setPublisher(publisher); + _currentMessage = new IncomingMessage(_messageStore.getNewMessageId(), info, _txnContext, _session); + _currentMessage.setMessageStore(_messageStore); _currentMessage.setExchange(e); } - public void publishContentHeader(ContentHeaderBody contentHeaderBody, AMQProtocolSession protocolSession) + public void publishContentHeader(ContentHeaderBody contentHeaderBody) throws AMQException { if (_currentMessage == null) @@ -215,7 +177,7 @@ public class AMQChannel { if (_log.isDebugEnabled()) { - _log.debug(debugIdentity() + "Content header received on channel " + _channelId); + _log.debug("Content header received on channel " + _channelId); } if (ENABLE_JMSXUserID) @@ -225,25 +187,48 @@ public class AMQChannel //fixme: fudge for QPID-677 properties.getHeaders().keySet(); - properties.setUserId(protocolSession.getAuthorizedID().getName()); + properties.setUserId(_session.getAuthorizedID().getName()); } _currentMessage.setContentHeaderBody(contentHeaderBody); + _currentMessage.setExpiration(); routeCurrentMessage(); - _currentMessage.routingComplete(_messageStore, _storeContext, _messageHandleFactory); - // check and deliver if header says body length is zero - if (contentHeaderBody.bodySize == 0) + _currentMessage.routingComplete(_messageStore, _messageHandleFactory); + + deliverCurrentMessageIfComplete(); + + } + } + + private void deliverCurrentMessageIfComplete() + throws AMQException + { + // check and deliver if header says body length is zero + if (_currentMessage.allContentReceived()) + { + try { - _txnContext.messageProcessed(protocolSession); + _currentMessage.deliverToQueues(); + } + catch (NoRouteException e) + { + _returnMessages.add(e); + } + finally + { + // callback to allow the context to do any post message processing + // primary use is to allow message return processing in the non-tx case + _txnContext.messageProcessed(_session); _currentMessage = null; } } + } - public void publishContentBody(ContentBody contentBody, AMQProtocolSession protocolSession) throws AMQException + public void publishContentBody(ContentBody contentBody) throws AMQException { if (_currentMessage == null) { @@ -260,15 +245,11 @@ public class AMQChannel // returns true iff the message was delivered (i.e. if all data was // received - if (_currentMessage.addContentBodyFrame(_storeContext, - protocolSession.getMethodRegistry().getProtocolVersionMethodConverter().convertToContentChunk( - contentBody))) - { - // callback to allow the context to do any post message processing - // primary use is to allow message return processing in the non-tx case - _txnContext.messageProcessed(protocolSession); - _currentMessage = null; - } + _currentMessage.addContentBodyFrame( + _session.getMethodRegistry().getProtocolVersionMethodConverter().convertToContentChunk( + contentBody)); + + deliverCurrentMessageIfComplete(); } catch (AMQException e) { @@ -287,6 +268,7 @@ public class AMQChannel } catch (NoRouteException e) { + //_currentMessage.incrementReference(); _returnMessages.add(e); } } @@ -307,18 +289,17 @@ public class AMQChannel * * @param tag the tag chosen by the client (if null, server will generate one) * @param queue the queue to subscribe to - * @param session the protocol session of the subscriber - * @param noLocal Flag stopping own messages being receivied. - * @param exclusive Flag requesting exclusive access to the queue * @param acks Are acks enabled for this subscriber * @param filters Filters to apply to this subscriber * + * @param noLocal Flag stopping own messages being receivied. + * @param exclusive Flag requesting exclusive access to the queue * @return the consumer tag. This is returned to the subscriber and used in subsequent unsubscribe requests * * @throws ConsumerTagNotUniqueException if the tag is not unique * @throws AMQException if something goes wrong */ - public AMQShortString subscribeToQueue(AMQShortString tag, AMQQueue queue, AMQProtocolSession session, boolean acks, + public AMQShortString subscribeToQueue(AMQShortString tag, AMQQueue queue, boolean acks, FieldTable filters, boolean noLocal, boolean exclusive) throws AMQException, ConsumerTagNotUniqueException { if (tag == null) @@ -326,77 +307,65 @@ public class AMQChannel tag = new AMQShortString("sgen_" + getNextConsumerTag()); } - if (_consumerTag2QueueMap.containsKey(tag)) + if (_tag2SubscriptionMap.containsKey(tag)) { throw new ConsumerTagNotUniqueException(); } + Subscription subscription = + SubscriptionFactoryImpl.INSTANCE.createSubscription(_channelId, _session, tag, acks, filters, noLocal, _creditManager); + + + // So to keep things straight we put before the call and catch all exceptions from the register and tidy up. // We add before we register as the Async Delivery process may AutoClose the subscriber // so calling _cT2QM.remove before we have done put which was after the register succeeded. // So to keep things straight we put before the call and catch all exceptions from the register and tidy up. - _consumerTag2QueueMap.put(tag, queue); + + _tag2SubscriptionMap.put(tag, subscription); try { - queue.registerProtocolSession(session, _channelId, tag, acks, filters, noLocal, exclusive); + queue.registerSubscription(subscription, exclusive); } catch (AMQException e) { - _consumerTag2QueueMap.remove(tag); + _tag2SubscriptionMap.remove(tag); throw e; } - return tag; } /** * Unsubscribe a consumer from a queue. - * @param session * @param consumerTag * @return true if the consumerTag had a mapped queue that could be unregistered. * @throws AMQException */ - public boolean unsubscribeConsumer(AMQProtocolSession session, AMQShortString consumerTag) throws AMQException + public boolean unsubscribeConsumer(AMQShortString consumerTag) throws AMQException { - if (_log.isDebugEnabled()) - { - _log.debug("Unacked Map Dump size:" + _unacknowledgedMessageMap.size()); - _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor() - { - - public boolean callback(UnacknowledgedMessage message) throws AMQException - { - _log.debug(message); - - return true; - } - public void visitComplete() - { - } - }); - } - - AMQQueue q = _consumerTag2QueueMap.remove(consumerTag); - if (q != null) + Subscription sub = _tag2SubscriptionMap.remove(consumerTag); + if (sub != null) { - q.unregisterProtocolSession(session, _channelId, consumerTag); + sub.getQueue().unregisterSubscription(sub); return true; } + else + { + _log.warn("Attempt to unsubscribe consumer with tag '"+consumerTag+"' which is not registered."); + } return false; } /** * Called from the protocol session to close this channel and clean up. T * - * @param session The session to close - * * @throws AMQException if there is an error during closure */ - public void close(AMQProtocolSession session) throws AMQException + public void close() throws AMQException { _txnContext.rollback(); - unsubscribeAllConsumers(session); + unsubscribeAllConsumers(); try { requeue(); @@ -414,11 +383,11 @@ public class AMQChannel _closing = closing; } - private void unsubscribeAllConsumers(AMQProtocolSession session) throws AMQException + private void unsubscribeAllConsumers() throws AMQException { if (_log.isInfoEnabled()) { - if (!_consumerTag2QueueMap.isEmpty()) + if (!_tag2SubscriptionMap.isEmpty()) { _log.info("Unsubscribing all consumers on channel " + toString()); } @@ -428,17 +397,19 @@ public class AMQChannel } } - for (Map.Entry<AMQShortString, AMQQueue> me : _consumerTag2QueueMap.entrySet()) + for (Map.Entry<AMQShortString, Subscription> me : _tag2SubscriptionMap.entrySet()) { if (_log.isInfoEnabled()) { _log.info("Unsubscribing consumer '" + me.getKey() + "' on channel " + toString()); } - me.getValue().unregisterProtocolSession(session, _channelId, me.getKey()); + Subscription sub = me.getValue(); + + sub.getQueue().unregisterSubscription(sub); } - _consumerTag2QueueMap.clear(); + _tag2SubscriptionMap.clear(); } /** @@ -447,9 +418,9 @@ public class AMQChannel * @param entry the record of the message on the queue that was delivered * @param deliveryTag the delivery tag used when delivering the message (see protocol spec for description of the * delivery tag) - * @param consumerTag The tag for the consumer that is to acknowledge this message. + * @param subscription The consumer that is to acknowledge this message. */ - public void addUnacknowledgedMessage(QueueEntry entry, long deliveryTag, AMQShortString consumerTag) + public void addUnacknowledgedMessage(QueueEntry entry, long deliveryTag, Subscription subscription) { if (_log.isDebugEnabled()) { @@ -462,16 +433,13 @@ public class AMQChannel if (_log.isDebugEnabled()) { _log.debug(debugIdentity() + " Adding unacked message(" + entry.getMessage().toString() + " DT:" + deliveryTag - + ") with a queue(" + entry.getQueue() + ") for " + consumerTag); + + ") with a queue(" + entry.getQueue() + ") for " + subscription); } } } - synchronized (_unacknowledgedMessageMap.getLock()) - { - _unacknowledgedMessageMap.add(deliveryTag, new UnacknowledgedMessage(entry, consumerTag, deliveryTag,_unacknowledgedMessageMap)); - checkSuspension(); - } + _unacknowledgedMessageMap.add(deliveryTag, entry); + } private final String id = "(" + System.identityHashCode(this) + ")"; @@ -490,7 +458,7 @@ public class AMQChannel public void requeue() throws AMQException { // we must create a new map since all the messages will get a new delivery tag when they are redelivered - Collection<UnacknowledgedMessage> messagesToBeDelivered = _unacknowledgedMessageMap.cancelAllMessages(); + Collection<QueueEntry> messagesToBeDelivered = _unacknowledgedMessageMap.cancelAllMessages(); // Deliver these messages out of the transaction as their delivery was never // part of the transaction only the receive. @@ -505,13 +473,9 @@ public class AMQChannel if (!(_txnContext instanceof NonTransactionalContext)) { - // if (_nonTransactedContext == null) - { - _nonTransactedContext = - new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages); - } - deliveryContext = _nonTransactedContext; + deliveryContext = + new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages); } else { @@ -519,22 +483,23 @@ public class AMQChannel } } - for (UnacknowledgedMessage unacked : messagesToBeDelivered) + for (QueueEntry unacked : messagesToBeDelivered) { if (!unacked.isQueueDeleted()) { - // Ensure message is released for redelivery - unacked.entry.release(); - // Mark message redelivered unacked.getMessage().setRedelivered(true); + // Ensure message is released for redelivery + unacked.release(); + // Deliver Message - deliveryContext.deliver(unacked.entry, false); + deliveryContext.requeue(unacked); - // Should we allow access To the DM to directy deliver the message? - // As we don't need to check for Consumers or worry about incrementing the message count? - // unacked.queue.getDeliveryManager().deliver(_storeContext, unacked.queue.getName(), unacked.message, false); + } + else + { + unacked.discard(_storeContext); } } @@ -549,32 +514,29 @@ public class AMQChannel */ public void requeue(long deliveryTag) throws AMQException { - UnacknowledgedMessage unacked = _unacknowledgedMessageMap.remove(deliveryTag); + QueueEntry unacked = _unacknowledgedMessageMap.remove(deliveryTag); if (unacked != null) { + // Mark message redelivered + unacked.getMessage().setRedelivered(true); // Ensure message is released for redelivery if (!unacked.isQueueDeleted()) { - unacked.entry.release(); + unacked.release(); } - // Mark message redelivered - unacked.getMessage().setRedelivered(true); // Deliver these messages out of the transaction as their delivery was never // part of the transaction only the receive. TransactionalContext deliveryContext; if (!(_txnContext instanceof NonTransactionalContext)) { - // if (_nonTransactedContext == null) - { - _nonTransactedContext = + + deliveryContext = new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages); - } - deliveryContext = _nonTransactedContext; } else { @@ -584,7 +546,7 @@ public class AMQChannel if (!unacked.isQueueDeleted()) { // Redeliver the messages to the front of the queue - deliveryContext.deliver(unacked.entry, true); + deliveryContext.requeue(unacked); // Deliver increments the message count but we have already deliverted this once so don't increment it again // this was because deliver did an increment changed this. } @@ -592,11 +554,8 @@ public class AMQChannel { _log.warn(System.identityHashCode(this) + " Requested requeue of message(" + unacked.getMessage().debugIdentity() + "):" + deliveryTag + " but no queue defined and no DeadLetter queue so DROPPING message."); - // _log.error("Requested requeue of message:" + deliveryTag + - // " but no queue defined using DeadLetter queue:" + getDeadLetterQueue()); - // - // deliveryContext.deliver(unacked.message, getDeadLetterQueue(), false); - // + + unacked.discard(_storeContext); } } else @@ -604,25 +563,6 @@ public class AMQChannel _log.warn("Requested requeue of message:" + deliveryTag + " but no such delivery tag exists." + _unacknowledgedMessageMap.size()); - if (_log.isDebugEnabled()) - { - _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor() - { - int count = 0; - - public boolean callback(UnacknowledgedMessage message) throws AMQException - { - _log.debug( - (count++) + ": (" + message.getMessage().debugIdentity() + ")" + "[" + message.deliveryTag + "]"); - - return false; // Continue - } - - public void visitComplete() - { - } - }); - } } } @@ -636,8 +576,10 @@ public class AMQChannel */ public void resend(final boolean requeue) throws AMQException { - final List<UnacknowledgedMessage> msgToRequeue = new LinkedList<UnacknowledgedMessage>(); - final List<UnacknowledgedMessage> msgToResend = new LinkedList<UnacknowledgedMessage>(); + + + final Map<Long, QueueEntry> msgToRequeue = new LinkedHashMap<Long, QueueEntry>(); + final Map<Long, QueueEntry> msgToResend = new LinkedHashMap<Long, QueueEntry>(); if (_log.isDebugEnabled()) { @@ -647,23 +589,25 @@ public class AMQChannel // Process the Unacked-Map. // Marking messages who still have a consumer for to be resent // and those that don't to be requeued. + _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor() { - public boolean callback(UnacknowledgedMessage message) throws AMQException + public boolean callback(final long deliveryTag, QueueEntry message) throws AMQException { - AMQShortString consumerTag = message.consumerTag; + AMQMessage msg = message.getMessage(); msg.setRedelivered(true); - if (consumerTag != null) + final Subscription subscription = message.getDeliveredSubscription(); + if (subscription != null) { // Consumer exists - if (_consumerTag2QueueMap.containsKey(consumerTag)) + if (!subscription.isClosed()) { - msgToResend.add(message); + msgToResend.put(deliveryTag, message); } else // consumer has gone { - msgToRequeue.add(message); + msgToRequeue.put(deliveryTag, message); } } else @@ -675,7 +619,7 @@ public class AMQChannel { if (requeue) { - msgToRequeue.add(message); + msgToRequeue.put(deliveryTag, message); } else { @@ -684,7 +628,8 @@ public class AMQChannel } else { - _log.info("Message.queue is null and no DeadLetter Queue so dropping message:" + message); + message.discard(_storeContext); + _log.warn("Message.queue is null and no DeadLetter Queue so dropping message:" + message); } } @@ -697,6 +642,8 @@ public class AMQChannel } }); + _unacknowledgedMessageMap.clear(); + // Process Messages to Resend if (_log.isDebugEnabled()) { @@ -710,9 +657,15 @@ public class AMQChannel } } - for (UnacknowledgedMessage message : msgToResend) + for (Map.Entry<Long, QueueEntry> entry : msgToResend.entrySet()) { + QueueEntry message = entry.getValue(); + long deliveryTag = entry.getKey(); + + + AMQMessage msg = message.getMessage(); + AMQQueue queue = message.getQueue(); // Our Java Client will always suspend the channel when resending! // If the client has requested the messages be resent then it is @@ -727,46 +680,20 @@ public class AMQChannel // else // { // release to allow it to be delivered - message.entry.release(); // Without any details from the client about what has been processed we have to mark // all messages in the unacked map as redelivered. msg.setRedelivered(true); - Subscription sub = message.entry.getDeliveredSubscription(); + Subscription sub = message.getDeliveredSubscription(); if (sub != null) { - // 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(!queue.resend(message, sub)) { - if (sub.isClosed()) - { - if (_log.isDebugEnabled()) - { - _log.debug("Subscription(" + System.identityHashCode(sub) - + ") closed during resend so requeuing message"); - } - // move this message to requeue - msgToRequeue.add(message); - } - else - { - if (_log.isDebugEnabled()) - { - _log.debug("Requeuing " + msg.debugIdentity() + " for resend via sub:" - + System.identityHashCode(sub)); - } - - sub.addToResendQueue(message.entry); - _unacknowledgedMessageMap.remove(message.deliveryTag); - } - } // sync(sub.getSendLock) + msgToRequeue.put(deliveryTag, message); + } } else { @@ -777,7 +704,7 @@ public class AMQChannel + ")to prevent loss"); } // move this message to requeue - msgToRequeue.add(message); + msgToRequeue.put(deliveryTag, message); } } // for all messages // } else !isSuspend @@ -795,13 +722,9 @@ public class AMQChannel TransactionalContext deliveryContext; if (!(_txnContext instanceof NonTransactionalContext)) { - if (_nonTransactedContext == null) - { - _nonTransactedContext = - new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages); - } - deliveryContext = _nonTransactedContext; + deliveryContext = + new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages); } else { @@ -809,14 +732,17 @@ public class AMQChannel } // Process Messages to Requeue at the front of the queue - for (UnacknowledgedMessage message : msgToRequeue) + for (Map.Entry<Long, QueueEntry> entry : msgToRequeue.entrySet()) { - message.entry.release(); - message.entry.setRedelivered(true); + QueueEntry message = entry.getValue(); + long deliveryTag = entry.getKey(); + + message.release(); + message.setRedelivered(true); - deliveryContext.deliver(message.entry, true); + deliveryContext.requeue(message); - _unacknowledgedMessageMap.remove(message.deliveryTag); + _unacknowledgedMessageMap.remove(deliveryTag); } } @@ -827,38 +753,47 @@ public class AMQChannel * * @param queue the queue that has been deleted * - * @throws org.apache.qpid.AMQException if there is an error processing the unacked messages */ - public void queueDeleted(final AMQQueue queue) throws AMQException + /* public void queueDeleted(final AMQQueue queue) { - _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor() + try { - public boolean callback(UnacknowledgedMessage message) throws AMQException + _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor() { - if (message.getQueue() == queue) + public boolean callback(UnacknowledgedMessage message) { - try + if (message.getQueue() == queue) { - message.discard(_storeContext); - message.setQueueDeleted(true); + try + { + message.discard(_storeContext); + message.setQueueDeleted(true); + } + catch (AMQException e) + { + _log.error( + "Error decrementing ref count on message " + message.getMessage().getMessageId() + ": " + e, e); + throw new RuntimeException(e); + } } - catch (AMQException e) - { - _log.error( - "Error decrementing ref count on message " + message.getMessage().getMessageId() + ": " + e, e); - } + + return false; } - return false; - } + public void visitComplete() + { + } + }); + } + catch (AMQException e) + { + _log.error("Unexpected Error while handling deletion of queue", e); + throw new RuntimeException(e); + } - public void visitComplete() - { - } - }); } - +*/ /** * Acknowledge one or more messages. * @@ -870,23 +805,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()); - } - - } - - checkSuspension(); + _unacknowledgedMessageMap.acknowledgeMessage(deliveryTag, multiple, _txnContext); } /** @@ -899,43 +818,22 @@ public class AMQChannel return _unacknowledgedMessageMap; } - private void checkSuspension() - { - boolean suspend; - - suspend = - ((_prefetch_HighWaterMark != 0) && (_unacknowledgedMessageMap.size() >= _prefetch_HighWaterMark)) - || ((_prefetchSize != 0) && (_prefetchSize < _unacknowledgedMessageMap.getUnacknowledgeBytes())); - - setSuspended(suspend); - } public void setSuspended(boolean suspended) { - boolean isSuspended = _suspended.get(); - if (isSuspended && !suspended) - { - // Continue being suspended if we are above the _prefetch_LowWaterMark - suspended = _unacknowledgedMessageMap.size() > _prefetch_LowWaterMark; - } boolean wasSuspended = _suspended.getAndSet(suspended); if (wasSuspended != suspended) { if (wasSuspended) { - _log.debug("Unsuspending channel " + this); // may need to deliver queued messages - for (AMQQueue q : _consumerTag2QueueMap.values()) + for (Subscription s : _tag2SubscriptionMap.values()) { - q.deliverAsync(); + s.getQueue().deliverAsync(s); } } - else - { - _log.debug("Suspending channel " + this); - } } } @@ -961,12 +859,7 @@ public class AMQChannel public String toString() { - StringBuilder sb = new StringBuilder(30); - sb.append("Channel: id ").append(_channelId).append(", transaction mode: ").append(isTransactional()); - sb.append(", prefetch marks: ").append(_prefetch_LowWaterMark); - sb.append("/").append(_prefetch_HighWaterMark); - - return sb.toString(); + return "["+_session.toString()+":"+_channelId+"]"; } public void setDefaultQueue(AMQQueue queue) @@ -984,14 +877,14 @@ public class AMQChannel return _storeContext; } - public void processReturns(AMQProtocolSession session) throws AMQException + public void processReturns() throws AMQException { if (!_returnMessages.isEmpty()) { for (RequiredDeliveryException bouncedMessage : _returnMessages) { AMQMessage message = bouncedMessage.getAMQMessage(); - session.getProtocolOutputConverter().writeReturn(message, _channelId, bouncedMessage.getReplyCode().getCode(), + _session.getProtocolOutputConverter().writeReturn(message, _channelId, bouncedMessage.getReplyCode().getCode(), new AMQShortString(bouncedMessage.getMessage())); message.decrementReference(_storeContext); @@ -1001,40 +894,68 @@ public class AMQChannel } } - public boolean wouldSuspend(AMQMessage msg) + + public TransactionalContext getTransactionalContext() { - if (isSuspended()) - { - return true; - } - else - { - boolean willSuspend = - ((_prefetch_HighWaterMark != 0) && ((_unacknowledgedMessageMap.size() + 1) > _prefetch_HighWaterMark)); - if (!willSuspend) - { - final long unackedSize = _unacknowledgedMessageMap.getUnacknowledgeBytes(); + return _txnContext; + } - willSuspend = (_prefetchSize != 0) && (unackedSize != 0) && (_prefetchSize < (msg.getSize() + unackedSize)); - } + public boolean isClosing() + { + return _closing; + } - if (willSuspend) - { - setSuspended(true); - } + public AMQProtocolSession getProtocolSession() + { + return _session; + } - return willSuspend; - } + public FlowCreditManager getCreditManager() + { + return _creditManager; + } + public void setCredit(final long prefetchSize, final int prefetchCount) + { + _creditManager.setCreditLimits(prefetchSize, prefetchCount); } - public TransactionalContext getTransactionalContext() + public List<RequiredDeliveryException> getReturnMessages() { - return _txnContext; + return _returnMessages; } - public boolean isClosing() + public MessageStore getMessageStore() { - return _closing; + 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/Main.java b/java/broker/src/main/java/org/apache/qpid/server/Main.java index d8a8cfb6d1..41d7f6c067 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/Main.java +++ b/java/broker/src/main/java/org/apache/qpid/server/Main.java @@ -279,6 +279,12 @@ public class Main ByteBuffer.setAllocator(new FixedSizeByteBufferAllocator()); } + + if(connectorConfig.useBiasedWrites) + { + System.setProperty("org.apache.qpid.use_write_biased_pool","true"); + } + int port = connectorConfig.port; String portStr = commandLine.getOptionValue("p"); diff --git a/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java b/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java index d61bb8916a..3f1947d65a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java +++ b/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java @@ -39,19 +39,30 @@ import org.apache.qpid.server.queue.AMQMessage; */ public abstract class RequiredDeliveryException extends AMQException { - private final AMQMessage _amqMessage; + private AMQMessage _amqMessage; public RequiredDeliveryException(String message, AMQMessage payload) { super(message); + setMessage(payload); + } + + + public RequiredDeliveryException(String message) + { + super(message); + } + + public void setMessage(final AMQMessage payload) + { + // Increment the reference as this message is in the routing phase // and so will have the ref decremented as routing fails. // we need to keep this message around so we can return it in the // handler. So increment here. _amqMessage = payload.takeReference(); - // payload.incrementReference(); } public AMQMessage getAMQMessage() diff --git a/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java b/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java index 6ad704a5d8..caf34f13bd 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java +++ b/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java @@ -22,11 +22,14 @@ package org.apache.qpid.server.ack; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.HashMap; import java.util.ArrayList; import org.apache.qpid.AMQException; import org.apache.qpid.server.store.StoreContext; import org.apache.qpid.server.txn.TxnOp; +import org.apache.qpid.server.queue.QueueEntry; /** * A TxnOp implementation for handling accumulated acks @@ -34,7 +37,7 @@ import org.apache.qpid.server.txn.TxnOp; public class TxAck implements TxnOp { private final UnacknowledgedMessageMap _map; - private final List <UnacknowledgedMessage> _unacked = new ArrayList<UnacknowledgedMessage>(); + private final Map<Long, QueueEntry> _unacked = new HashMap<Long,QueueEntry>(); private List<Long> _individual; private long _deliveryTag; private boolean _multiple; @@ -46,11 +49,12 @@ public class TxAck implements TxnOp public void update(long deliveryTag, boolean multiple) { + _unacked.clear(); if (!multiple) { if(_individual == null) { - _individual = new ArrayList<Long>(); + _individual = new ArrayList<Long>(); } //have acked a single message that is not part of //the previously acked region so record @@ -64,36 +68,29 @@ public class TxAck implements TxnOp _deliveryTag = deliveryTag; _multiple = true; } - _unacked.clear(); } public void consolidate() { if(_unacked.isEmpty()) { - consolidate(_unacked); - } - - } - - private void consolidate(List<UnacknowledgedMessage> unacked) - { - //lookup all the unacked messages that have been acked in this transaction - if (_multiple) - { - //get all the unacked messages for the accumulated - //multiple acks - _map.collect(_deliveryTag, true, unacked); - } - //get any unacked messages for individual acks outside the - //range covered by multiple acks - if(_individual != null) - { - for (Long tag : _individual) + //lookup all the unacked messages that have been acked in this transaction + if (_multiple) { - if(_deliveryTag < tag) + //get all the unacked messages for the accumulated + //multiple acks + _map.collect(_deliveryTag, true, _unacked); + } + if(_individual != null) + { + //get any unacked messages for individual acks outside the + //range covered by multiple acks + for (long tag : _individual) { - _map.collect(tag, false, unacked); + if(_deliveryTag < tag) + { + _map.collect(tag, false, _unacked); + } } } } @@ -101,12 +98,10 @@ public class TxAck implements TxnOp public boolean checkPersistent() throws AMQException { - - consolidate(); //if any of the messages in unacked are persistent the txn //buffer must be marked as persistent: - for (UnacknowledgedMessage msg : _unacked) + for (QueueEntry msg : _unacked.values()) { if (msg.getMessage().isPersistent()) { @@ -119,8 +114,9 @@ public class TxAck implements TxnOp public void prepare(StoreContext storeContext) throws AMQException { //make persistent changes, i.e. dequeue and decrementReference - for (UnacknowledgedMessage msg : _unacked) + for (QueueEntry msg : _unacked.values()) { + msg.restoreCredit(); //Message has been ack so discard it. This will dequeue and decrement the reference. msg.discard(storeContext); @@ -133,7 +129,7 @@ public class TxAck implements TxnOp //in memory counter) so if we failed in prepare for full //txn, this op will have to compensate by fixing the count //in memory (persistent changes will be rolled back by store) - for (UnacknowledgedMessage msg : _unacked) + for (QueueEntry msg : _unacked.values()) { msg.getMessage().takeReference(); } @@ -142,7 +138,7 @@ public class TxAck implements TxnOp public void commit(StoreContext storeContext) { //remove the unacked messages from the channels map - _map.remove(_unacked); + _map.remove(_unacked); } public void rollback(StoreContext storeContext) diff --git a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java deleted file mode 100644 index 0112d3b388..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java +++ /dev/null @@ -1,104 +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.ack; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.store.StoreContext; - -public class UnacknowledgedMessage -{ - public final QueueEntry entry; - public final AMQShortString consumerTag; - public final long deliveryTag; - - private boolean _queueDeleted; - private final UnacknowledgedMessageMap _unacknowledgeMessageMap; - - - public UnacknowledgedMessage(QueueEntry entry, - AMQShortString consumerTag, - long deliveryTag, - final UnacknowledgedMessageMap unacknowledgedMessageMap) - { - this.entry = entry; - this.consumerTag = consumerTag; - this.deliveryTag = deliveryTag; - _unacknowledgeMessageMap = unacknowledgedMessageMap; - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append("Q:"); - sb.append(entry.getQueue()); - sb.append(" M:"); - sb.append(entry.getMessage()); - sb.append(" CT:"); - sb.append(consumerTag); - sb.append(" DT:"); - sb.append(deliveryTag); - - return sb.toString(); - } - - public void discard(StoreContext storeContext) throws AMQException - { - synchronized(_unacknowledgeMessageMap) - { - if(_unacknowledgeMessageMap.contains(deliveryTag)) - { - - if (entry.getQueue() != null) - { - entry.getQueue().dequeue(storeContext, entry); - } - //if the queue is null then the message is waiting to be acked, but has been removed. - entry.getMessage().decrementReference(storeContext); - } - } - - } - - public AMQMessage getMessage() - { - return entry.getMessage(); - } - - public AMQQueue getQueue() - { - return entry.getQueue(); - } - - public void setQueueDeleted(boolean queueDeleted) - { - _queueDeleted = queueDeleted; - } - - public boolean isQueueDeleted() - { - return _queueDeleted; - } -} - 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 5b0f3cf5eb..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 @@ -23,41 +23,41 @@ package org.apache.qpid.server.ack; import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.Map; import org.apache.qpid.AMQException; import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.queue.QueueEntry; public interface UnacknowledgedMessageMap { public interface Visitor { /** - * @param message the message being iterated over - * @return true to stop iteration, false to continue + * @param deliveryTag + *@param message the message being iterated over @return true to stop iteration, false to continue * @throws AMQException */ - boolean callback(UnacknowledgedMessage message) throws AMQException; + boolean callback(final long deliveryTag, QueueEntry message) throws AMQException; void visitComplete(); } void visit(Visitor visitor) throws AMQException; - Object getLock(); + void add(long deliveryTag, QueueEntry message); - void add(long deliveryTag, UnacknowledgedMessage message); - - void collect(Long deliveryTag, boolean multiple, List<UnacknowledgedMessage> msgs); + void collect(long deliveryTag, boolean multiple, Map<Long, QueueEntry> msgs); boolean contains(long deliveryTag) throws AMQException; - void remove(List<UnacknowledgedMessage> msgs); + void remove(Map<Long,QueueEntry> msgs); - UnacknowledgedMessage remove(long deliveryTag); + QueueEntry remove(long deliveryTag); - void drainTo(Collection<UnacknowledgedMessage> destination, long deliveryTag) throws AMQException; + void drainTo(Collection<QueueEntry> destination, long deliveryTag) throws AMQException; - Collection<UnacknowledgedMessage> cancelAllMessages(); + Collection<QueueEntry> cancelAllMessages(); void acknowledgeMessage(long deliveryTag, boolean multiple, TransactionalContext txnContext) throws AMQException; @@ -65,7 +65,7 @@ public interface UnacknowledgedMessageMap void clear(); - UnacknowledgedMessage get(long deliveryTag); + QueueEntry get(long deliveryTag); /** * Get the set of delivery tags that are outstanding. 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 5204f13e81..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 @@ -28,6 +28,10 @@ import java.util.Map; import java.util.Set; import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.txn.TransactionalContext; public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap @@ -36,7 +40,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap private long _unackedSize; - private Map<Long, UnacknowledgedMessage> _map; + private Map<Long, QueueEntry> _map; private long _lastDeliveryTag; @@ -45,10 +49,10 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap public UnacknowledgedMessageMapImpl(int prefetchLimit) { _prefetchLimit = prefetchLimit; - _map = new LinkedHashMap<Long, UnacknowledgedMessage>(prefetchLimit); + _map = new LinkedHashMap<Long, QueueEntry>(prefetchLimit); } - public void collect(Long deliveryTag, boolean multiple, List<UnacknowledgedMessage> msgs) + public void collect(long deliveryTag, boolean multiple, Map<Long, QueueEntry> msgs) { if (multiple) { @@ -56,7 +60,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } else { - msgs.add(get(deliveryTag)); + msgs.put(deliveryTag, get(deliveryTag)); } } @@ -69,26 +73,27 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } } - public void remove(List<UnacknowledgedMessage> msgs) + public void remove(Map<Long,QueueEntry> msgs) { synchronized (_lock) { - for (UnacknowledgedMessage msg : msgs) + for (Long deliveryTag : msgs.keySet()) { - remove(msg.deliveryTag); + remove(deliveryTag); } } } - public UnacknowledgedMessage remove(long deliveryTag) + public QueueEntry remove(long deliveryTag) { synchronized (_lock) { - UnacknowledgedMessage message = _map.remove(deliveryTag); + QueueEntry message = _map.remove(deliveryTag); if(message != null) { _unackedSize -= message.getMessage().getSize(); + message.restoreCredit(); } return message; @@ -99,21 +104,16 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap { synchronized (_lock) { - Collection<UnacknowledgedMessage> currentEntries = _map.values(); - for (UnacknowledgedMessage msg : currentEntries) + Set<Map.Entry<Long, QueueEntry>> currentEntries = _map.entrySet(); + for (Map.Entry<Long, QueueEntry> entry : currentEntries) { - visitor.callback(msg); + visitor.callback(entry.getKey().longValue(), entry.getValue()); } visitor.visitComplete(); } } - public Object getLock() - { - return _lock; - } - - public void add(long deliveryTag, UnacknowledgedMessage message) + public void add(long deliveryTag, QueueEntry message) { synchronized (_lock) { @@ -123,12 +123,12 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } } - public Collection<UnacknowledgedMessage> cancelAllMessages() + public Collection<QueueEntry> cancelAllMessages() { synchronized (_lock) { - Collection<UnacknowledgedMessage> currentEntries = _map.values(); - _map = new LinkedHashMap<Long, UnacknowledgedMessage>(_prefetchLimit); + Collection<QueueEntry> currentEntries = _map.values(); + _map = new LinkedHashMap<Long, QueueEntry>(_prefetchLimit); _unackedSize = 0l; return currentEntries; } @@ -160,14 +160,14 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } } - public void drainTo(Collection<UnacknowledgedMessage> destination, long deliveryTag) throws AMQException + public void drainTo(Collection<QueueEntry> destination, long deliveryTag) throws AMQException { synchronized (_lock) { - Iterator<Map.Entry<Long, UnacknowledgedMessage>> it = _map.entrySet().iterator(); + Iterator<Map.Entry<Long, QueueEntry>> it = _map.entrySet().iterator(); while (it.hasNext()) { - Map.Entry<Long, UnacknowledgedMessage> unacked = it.next(); + Map.Entry<Long, QueueEntry> unacked = it.next(); if (unacked.getKey() > deliveryTag) { @@ -175,10 +175,13 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap throw new AMQException("UnacknowledgedMessageMap is out of order:" + unacked.getKey() + " When deliveryTag is:" + deliveryTag + "ES:" + _map.entrySet().toString()); } - it.remove(); + _unackedSize -= unacked.getValue().getMessage().getSize(); + unacked.getValue().restoreCredit(); + + destination.add(unacked.getValue()); if (unacked.getKey() == deliveryTag) { @@ -188,7 +191,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } } - public UnacknowledgedMessage get(long key) + public QueueEntry get(long key) { synchronized (_lock) { @@ -204,14 +207,14 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } } - private void collect(Long key, List<UnacknowledgedMessage> msgs) + private void collect(long key, Map<Long, QueueEntry> msgs) { synchronized (_lock) { - for (Map.Entry<Long, UnacknowledgedMessage> entry : _map.entrySet()) + for (Map.Entry<Long, QueueEntry> entry : _map.entrySet()) { - msgs.add(entry.getValue()); - if (entry.getKey().equals(key)) + msgs.put(entry.getKey(),entry.getValue()); + if (entry.getKey() == key) { break; } 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 8573902af4..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,11 +30,13 @@ 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; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -176,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 = new AMQQueue(queueName, + 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()) { @@ -221,7 +234,7 @@ public class VirtualHostConfiguration AMQShortString routingKey = new AMQShortString(String.valueOf(routingKeyNameObj)); - queue.bind(routingKey, null, exchange); + queue.bind(exchange, routingKey, null); _logger.info("Queue '" + queue.getName() + "' bound to exchange:" + exchangeName + " RK:'" + routingKey + "'"); @@ -229,7 +242,7 @@ public class VirtualHostConfiguration if(exchange != virtualHost.getExchangeRegistry().getDefaultExchange()) { - queue.bind(queue.getName(), null, virtualHost.getExchangeRegistry().getDefaultExchange()); + queue.bind(virtualHost.getExchangeRegistry().getDefaultExchange(), queue.getName(), null); } } 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 9ebb893362..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 @@ -191,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/DefaultExchangeFactory.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java index 636aa7eb03..9d4c090971 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java @@ -43,8 +43,8 @@ public class DefaultExchangeFactory implements ExchangeFactory public DefaultExchangeFactory(VirtualHost host) { _host = host; - registerExchangeType(DestNameExchange.TYPE); - registerExchangeType(DestWildExchange.TYPE); + registerExchangeType(DirectExchange.TYPE); + registerExchangeType(TopicExchange.TYPE); registerExchangeType(HeadersExchange.TYPE); registerExchangeType(FanoutExchange.TYPE); } @@ -67,7 +67,7 @@ public class DefaultExchangeFactory implements ExchangeFactory if (exchType == null) { - throw new AMQUnknownExchangeType("Unknown exchange type: " + type, null); + throw new AMQUnknownExchangeType("Unknown exchange type: " + type,null); } Exchange e = exchType.newInstance(_host, exchange, durable, ticket, autoDelete); return e; diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java index 98abf7977a..0ab8208d88 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java @@ -25,6 +25,7 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.protocol.ExchangeInitialiser; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -121,9 +122,9 @@ public class DefaultExchangeRegistry implements ExchangeRegistry * @param payload * @throws AMQException if something goes wrong delivering data */ - public void routeContent(AMQMessage payload) throws AMQException + public void routeContent(IncomingMessage payload) throws AMQException { - final AMQShortString exchange = payload.getMessagePublishInfo().getExchange(); + final AMQShortString exchange = payload.getExchange(); final Exchange exch = getExchange(exchange); // there is a small window of opportunity for the exchange to be deleted in between // the BasicPublish being received (where the exchange is validated) and the final diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java deleted file mode 100644 index 6fa3686152..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java +++ /dev/null @@ -1,579 +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.exchange; - -import org.apache.log4j.Logger; -import org.apache.qpid.AMQException; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.exchange.ExchangeDefaults; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.AMQShortStringTokenizer; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.server.management.MBeanConstructor; -import org.apache.qpid.server.management.MBeanDescription; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.virtualhost.VirtualHost; - -import javax.management.JMException; -import javax.management.MBeanException; -import javax.management.openmbean.CompositeData; -import javax.management.openmbean.CompositeDataSupport; -import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.TabularData; -import javax.management.openmbean.TabularDataSupport; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; - -public class DestWildExchange extends AbstractExchange -{ - - public static final ExchangeType<DestWildExchange> TYPE = new ExchangeType<DestWildExchange>() - { - - public AMQShortString getName() - { - return ExchangeDefaults.TOPIC_EXCHANGE_CLASS; - } - - public Class<DestWildExchange> getExchangeClass() - { - return DestWildExchange.class; - } - - public DestWildExchange newInstance(VirtualHost host, - AMQShortString name, - boolean durable, - int ticket, - boolean autoDelete) throws AMQException - { - DestWildExchange exch = new DestWildExchange(); - exch.initialise(host, name, durable, ticket, autoDelete); - return exch; - } - - public AMQShortString getDefaultExchangeName() - { - return ExchangeDefaults.TOPIC_EXCHANGE_NAME; - } - }; - - - private static final Logger _logger = Logger.getLogger(DestWildExchange.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 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)'*'; - - /** DestWildExchangeMBean class implements the management interface for the Topic exchanges. */ - @MBeanDescription("Management Bean for Topic Exchange") - private final class DestWildExchangeMBean extends ExchangeMBean - { - @MBeanConstructor("Creates an MBean for AMQ topic exchange") - public DestWildExchangeMBean() throws JMException - { - super(); - _exchangeType = "topic"; - init(); - } - - /** returns exchange bindings in tabular form */ - public TabularData bindings() throws OpenDataException - { - _bindingList = new TabularDataSupport(_bindinglistDataType); - for (Map.Entry<AMQShortString, List<AMQQueue>> entry : _bindingKey2queues.entrySet()) - { - AMQShortString key = entry.getKey(); - List<String> queueList = new ArrayList<String>(); - - List<AMQQueue> queues = getMatchedQueues(key); - for (AMQQueue q : queues) - { - queueList.add(q.getName().toString()); - } - - Object[] bindingItemValues = {key.toString(), queueList.toArray(new String[0])}; - CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues); - _bindingList.put(bindingData); - } - - return _bindingList; - } - - public void createNewBinding(String queueName, String binding) throws JMException - { - AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName)); - if (queue == null) - { - throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange."); - } - - try - { - queue.bind(new AMQShortString(binding), null, DestWildExchange.this); - } - catch (AMQException ex) - { - throw new MBeanException(ex); - } - } - - } // End of MBean class - - public AMQShortString getType() - { - return ExchangeDefaults.TOPIC_EXCHANGE_CLASS; - } - - public synchronized void registerQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException - { - assert queue != null; - assert rKey != null; - - _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>()); - - - - - - - - // 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) - { - queueList = _bindingKey2queues.get(rKey); - } - - - - if (!queueList.contains(queue)) - { - queueList.add(queue); - - - if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE)) - { - AMQShortString routingKey = normalize(rKey); - List<AMQQueue> queueList2 = _wildCardBindingKey2queues.putIfAbsent(routingKey, new CopyOnWriteArrayList<AMQQueue>()); - - if(queueList2 == null) - { - 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()])); - } - queueList2.add(queue); - - } - else - { - List<AMQQueue> queueList2 = _simpleBindingKey2queues.putIfAbsent(rKey, new CopyOnWriteArrayList<AMQQueue>()); - if(queueList2 == null) - { - queueList2 = _simpleBindingKey2queues.get(rKey); - } - queueList2.add(queue); - - } - - - - - } - else if (_logger.isDebugEnabled()) - { - _logger.debug("Queue " + queue + " is already registered with routing key " + rKey); - } - - - - } - - private AMQShortString normalize(AMQShortString routingKey) - { - if(routingKey == null) - { - routingKey = AMQShortString.EMPTY_STRING; - } - - AMQShortStringTokenizer routingTokens = routingKey.tokenize(TOPIC_SEPARATOR); - - List<AMQShortString> subscriptionList = new ArrayList<AMQShortString>(); - - while (routingTokens.hasMoreTokens()) - { - subscriptionList.add(routingTokens.nextToken()); - } - - int size = subscriptionList.size(); - - for (int index = 0; index < size; index++) - { - // if there are more levels - if ((index + 1) < size) - { - if (subscriptionList.get(index).equals(AMQP_HASH_TOKEN)) - { - if (subscriptionList.get(index + 1).equals(AMQP_HASH_TOKEN)) - { - // we don't need #.# delete this one - subscriptionList.remove(index); - size--; - // redo this normalisation - index--; - } - - if (subscriptionList.get(index + 1).equals(AMQP_STAR_TOKEN)) - { - // we don't want #.* swap to *.# - // remove it and put it in at index + 1 - subscriptionList.add(index + 1, subscriptionList.remove(index)); - } - } - } // if we have more levels - } - - - - AMQShortString normalizedString = AMQShortString.join(subscriptionList, TOPIC_SEPARATOR_AS_SHORTSTRING); - - return normalizedString; - } - - public void route(AMQMessage payload) throws AMQException - { - MessagePublishInfo info = payload.getMessagePublishInfo(); - - final AMQShortString routingKey = info.getRoutingKey(); - - List<AMQQueue> queues = getMatchedQueues(routingKey); - // if we have no registered queues we have nothing to do - // TODO: add support for the immediate flag - if ((queues == null) || queues.isEmpty()) - { - if (info.isMandatory() || info.isImmediate()) - { - String msg = "Topic " + routingKey + " is not known to " + this; - throw new NoRouteException(msg, payload); - } - else - { - _logger.warn("No queues found for routing key " + routingKey); - _logger.warn("Routing map contains: " + _bindingKey2queues); - - return; - } - } - - payload.enqueue(queues); - - } - - public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) - { - return isBound(routingKey, queue); - } - - public boolean isBound(AMQShortString routingKey, AMQQueue queue) - { - List<AMQQueue> queues = _bindingKey2queues.get(normalize(routingKey)); - - return (queues != null) && queues.contains(queue); - } - - public boolean isBound(AMQShortString routingKey) - { - List<AMQQueue> queues = _bindingKey2queues.get(normalize(routingKey)); - - return (queues != null) && !queues.isEmpty(); - } - - public boolean isBound(AMQQueue queue) - { - for (List<AMQQueue> queues : _bindingKey2queues.values()) - { - if (queues.contains(queue)) - { - return true; - } - } - - return false; - } - - public boolean hasBindings() - { - return !_bindingKey2queues.isEmpty(); - } - - public synchronized void deregisterQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException - { - 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"); - - } - - boolean removedQ = queues.remove(queue); - if (!removedQ) - { - throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() - + " with routing key " + rKey); - } - - - if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE)) - { - AMQShortString bindingKey = normalize(rKey); - List<AMQQueue> queues2 = _wildCardBindingKey2queues.get(bindingKey); - queues2.remove(queue); - if(queues2.isEmpty()) - { - _wildCardBindingKey2queues.remove(bindingKey); - _bindingKey2Tokenized.remove(bindingKey); - } - - } - else - { - List<AMQQueue> queues2 = _simpleBindingKey2queues.get(rKey); - queues2.remove(queue); - if(queues2.isEmpty()) - { - _simpleBindingKey2queues.remove(rKey); - } - - } - - - - - if (queues.isEmpty()) - { - _bindingKey2queues.remove(rKey); - } - } - - protected ExchangeMBean createMBean() throws AMQException - { - try - { - return new DestWildExchangeMBean(); - } - catch (JMException ex) - { - _logger.error("Exception occured in creating the topic exchenge mbean", ex); - throw new AMQException("Exception occured in creating the topic exchenge mbean", ex); - } - } - - public Map<AMQShortString, List<AMQQueue>> getBindings() - { - return _bindingKey2queues; - } - - private List<AMQQueue> getMatchedQueues(AMQShortString routingKey) - { - - List<AMQQueue> list = null; - - if(!_wildCardBindingKey2queues.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(); - - routingkeyTokens[token++] = next; - } - } - 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)); - } - } - } - - } - if(!_simpleBindingKey2queues.isEmpty()) - { - 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) - { - list.addAll(queues); - } - - } - - return list; - - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java index 12347c0278..5dcc2cf143 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java @@ -38,23 +38,22 @@ import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.management.MBeanConstructor; import org.apache.qpid.server.management.MBeanDescription; -import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.virtualhost.VirtualHost; -public class DestNameExchange extends AbstractExchange +public class DirectExchange extends AbstractExchange { - private static final Logger _logger = Logger.getLogger(DestNameExchange.class); + private static final Logger _logger = Logger.getLogger(DirectExchange.class); /** * Maps from queue name to queue instances */ private final Index _index = new Index(); - public static final ExchangeType<DestNameExchange> TYPE = new ExchangeType<DestNameExchange>() + public static final ExchangeType<DirectExchange> TYPE = new ExchangeType<DirectExchange>() { public AMQShortString getName() @@ -62,18 +61,18 @@ public class DestNameExchange extends AbstractExchange return ExchangeDefaults.DIRECT_EXCHANGE_CLASS; } - public Class<DestNameExchange> getExchangeClass() + public Class<DirectExchange> getExchangeClass() { - return DestNameExchange.class; + return DirectExchange.class; } - public DestNameExchange newInstance(VirtualHost host, + public DirectExchange newInstance(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { - DestNameExchange exch = new DestNameExchange(); + DirectExchange exch = new DirectExchange(); exch.initialise(host,name,durable,ticket,autoDelete); return exch; } @@ -88,10 +87,10 @@ public class DestNameExchange extends AbstractExchange * MBean class implementing the management interfaces. */ @MBeanDescription("Management Bean for Direct Exchange") - private final class DestNameExchangeMBean extends ExchangeMBean + private final class DirectExchangeMBean extends ExchangeMBean { @MBeanConstructor("Creates an MBean for AMQ direct exchange") - public DestNameExchangeMBean() throws JMException + public DirectExchangeMBean() throws JMException { super(); _exchangeType = "direct"; @@ -132,7 +131,7 @@ public class DestNameExchange extends AbstractExchange try { - queue.bind(new AMQShortString(binding), null, DestNameExchange.this); + queue.bind(DirectExchange.this, new AMQShortString(binding), null); } catch (AMQException ex) { @@ -147,7 +146,7 @@ public class DestNameExchange extends AbstractExchange { try { - return new DestNameExchangeMBean(); + return new DirectExchangeMBean(); } catch (JMException ex) { @@ -187,35 +186,21 @@ public class DestNameExchange extends AbstractExchange } } - public void route(AMQMessage payload) throws AMQException + public void route(IncomingMessage payload) throws AMQException { - final MessagePublishInfo info = payload.getMessagePublishInfo(); - final AMQShortString routingKey = info.getRoutingKey() == null ? AMQShortString.EMPTY_STRING : info.getRoutingKey(); + + final AMQShortString routingKey = payload.getRoutingKey() == null ? AMQShortString.EMPTY_STRING : payload.getRoutingKey(); + final List<AMQQueue> queues = (routingKey == null) ? null : _index.get(routingKey); - if (queues == null || queues.isEmpty()) + + if (_logger.isDebugEnabled()) { - String msg = "Routing key " + routingKey + " is not known to " + this; - if (info.isMandatory() || info.isImmediate()) - { - throw new NoRouteException(msg, payload); - } - else - { - _logger.error("MESSAGE LOSS: Message should be sent on a Dead Letter Queue"); - _logger.warn(msg); - } + _logger.debug("Publishing message to queue " + queues); } - else - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Publishing message to queue " + queues); - } payload.enqueue(queues); - } } public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) 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 37cd85a8f8..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 @@ -23,7 +23,8 @@ package org.apache.qpid.server.exchange; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.queue.AMQMessage; + +import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -53,7 +54,7 @@ public interface Exchange void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException; - void route(AMQMessage message) throws AMQException; + void route(IncomingMessage message) throws AMQException; /** @@ -92,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/FanoutExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java index f1b383eac9..e9fd4d548b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java @@ -26,10 +26,9 @@ import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.management.MBeanConstructor; import org.apache.qpid.server.management.MBeanDescription; -import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -95,7 +94,7 @@ public class FanoutExchange extends AbstractExchange try { - queue.bind(new AMQShortString(binding), null, FanoutExchange.this); + queue.bind(FanoutExchange.this, new AMQShortString(binding), null); } catch (AMQException ex) { @@ -183,32 +182,17 @@ public class FanoutExchange extends AbstractExchange } } - public void route(AMQMessage payload) throws AMQException + public void route(IncomingMessage payload) throws AMQException { - final MessagePublishInfo publishInfo = payload.getMessagePublishInfo(); - final AMQShortString routingKey = publishInfo.getRoutingKey(); - if ((_queues == null) || _queues.isEmpty()) + + + if (_logger.isDebugEnabled()) { - String msg = "No queues bound to " + this; - if (publishInfo.isMandatory() || publishInfo.isImmediate()) - { - throw new NoRouteException(msg, payload); - } - else - { - _logger.warn(msg); - } + _logger.debug("Publishing message to queue " + _queues); } - else - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Publishing message to queue " + _queues); - } - payload.enqueue(new ArrayList(_queues)); + payload.enqueue(new ArrayList(_queues)); - } } public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) 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 68ad88c4cb..d1bea3410b 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,7 @@ 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.AMQMessage; +import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -50,6 +50,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Collection; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -240,7 +241,7 @@ public class HeadersExchange extends AbstractExchange } } - public void route(AMQMessage payload) throws AMQException + public void route(IncomingMessage payload) throws AMQException { FieldTable headers = getHeaders(payload.getContentHeaderBody()); if (_logger.isDebugEnabled()) @@ -248,8 +249,10 @@ public class HeadersExchange extends AbstractExchange _logger.debug("Exchange " + getName() + ": routing message with headers " + headers); } boolean routed = false; + Collection<AMQQueue> queues = new ArrayList<AMQQueue>(); for (Registration e : _bindings) { + if (e.binding.matches(headers)) { if (_logger.isDebugEnabled()) @@ -257,25 +260,12 @@ public class HeadersExchange extends AbstractExchange _logger.debug("Exchange " + getName() + ": delivering message with headers " + headers + " to " + e.queue.getName()); } - payload.enqueue(e.queue); - routed = true; - } - } - if (!routed) - { - - String msg = "Exchange " + getName() + ": message not routable."; + queues.add(e.queue); - if (payload.getMessagePublishInfo().isMandatory() || payload.getMessagePublishInfo().isImmediate()) - { - throw new NoRouteException(msg, payload); - } - else - { - _logger.warn(msg); + routed = true; } - } + payload.enqueue(queues); } public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java index eacdad8a8e..4f1f550e94 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java @@ -32,7 +32,7 @@ import org.apache.qpid.server.queue.AMQQueue; /** * An index of queues against routing key. Allows multiple queues to be stored - * against the same key. Used in the DestNameExchange. + * against the same key. Used in the DirectExchange. */ class Index { diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java index 7508e80f7f..db9beb6da7 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java @@ -22,6 +22,7 @@ package org.apache.qpid.server.exchange; import org.apache.qpid.AMQException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.IncomingMessage; /** * Separated out from the ExchangeRegistry interface to allow components @@ -36,5 +37,5 @@ public interface MessageRouter * * @throws org.apache.qpid.AMQException if something goes wrong delivering data */ - void routeContent(AMQMessage message) throws AMQException; + void routeContent(IncomingMessage message) throws AMQException; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java index 1d6ab3842d..d18ad7ab14 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.exchange; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.RequiredDeliveryException; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.IncomingMessage; /** * NoRouteException is a {@link RequiredDeliveryException} that represents the failure case where a manadatory message @@ -36,9 +37,9 @@ import org.apache.qpid.server.queue.AMQMessage; */ public class NoRouteException extends RequiredDeliveryException { - public NoRouteException(String msg, AMQMessage message) + public NoRouteException(String msg, AMQMessage amqMessage) { - super(msg, message); + super(msg, amqMessage); } public AMQConstant getReplyCode() 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 new file mode 100644 index 0000000000..d07501a188 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java @@ -0,0 +1,651 @@ +/* + * + * 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.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; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.AMQShortStringTokenizer; +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; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.TabularData; +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 +{ + + public static final ExchangeType<TopicExchange> TYPE = new ExchangeType<TopicExchange>() + { + + public AMQShortString getName() + { + return ExchangeDefaults.TOPIC_EXCHANGE_CLASS; + } + + public Class<TopicExchange> getExchangeClass() + { + return TopicExchange.class; + } + + public TopicExchange newInstance(VirtualHost host, + AMQShortString name, + boolean durable, + int ticket, + boolean autoDelete) throws AMQException + { + TopicExchange exch = new TopicExchange(); + exch.initialise(host, name, durable, ticket, autoDelete); + return exch; + } + + public AMQShortString getDefaultExchangeName() + { + return ExchangeDefaults.TOPIC_EXCHANGE_NAME; + } + }; + + + 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 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 Collection<AMQQueue> processMessage(IncomingMessage msg, Collection<AMQQueue> queues) + { + if(queues == null) + { + if(_filteredQueues.isEmpty()) + { + return new ArrayList<AMQQueue>(_unfilteredQueues.keySet()); + } + else + { + queues = new HashSet<AMQQueue>(); + } + } + else if(!(queues instanceof Set)) + { + queues = new HashSet<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 + { + @MBeanConstructor("Creates an MBean for AMQ topic exchange") + public TopicExchangeMBean() throws JMException + { + super(); + _exchangeType = "topic"; + init(); + } + + /** returns exchange bindings in tabular form */ + public TabularData bindings() throws OpenDataException + { + _bindingList = new TabularDataSupport(_bindinglistDataType); + Map<String, List<String>> bindingData = new HashMap<String, List<String>>(); + for (Binding binding : _bindings.keySet()) + { + String key = binding.getBindingKey().toString(); + List<String> queueNames = bindingData.get(key); + if(queueNames == null) + { + queueNames = new ArrayList<String>(); + bindingData.put(key, queueNames); + } + queueNames.add(binding.getQueue().getName().toString()); + + } + 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; + } + + public void createNewBinding(String queueName, String binding) throws JMException + { + AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName)); + if (queue == null) + { + throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange."); + } + + try + { + queue.bind(TopicExchange.this, new AMQShortString(binding), null); + } + catch (AMQException ex) + { + throw new MBeanException(ex); + } + } + + } // End of MBean class + + public AMQShortString getType() + { + return ExchangeDefaults.TOPIC_EXCHANGE_CLASS; + } + + public synchronized void registerQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + assert rKey != null; + + _logger.debug("Registering queue " + queue.getName() + " with routing key " + rKey); + + + AMQShortString routingKey; + + if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE)) + { + routingKey = normalize(rKey); + } + else + { + routingKey = rKey; + } + + Binding binding = new Binding(rKey, queue); + + if(_bindings.containsKey(binding)) + { + FieldTable oldArgs = _bindings.get(binding); + TopicExchangeResult result = _topicExchangeResults.get(routingKey); + + if(argumentsContainSelector(args)) + { + if(argumentsContainSelector(oldArgs)) + { + result.replaceQueueFilter(queue,createSelectorFilter(oldArgs), createSelectorFilter(args)); + } + else + { + result.addFilteredQueue(queue,createSelectorFilter(args)); + result.removeUnfilteredQueue(queue); + } + } + else + { + if(argumentsContainSelector(oldArgs)) + { + result.addUnfilteredQueue(queue); + result.removeFilteredQueue(queue, createSelectorFilter(oldArgs)); + } + else + { + // TODO - fix control flow + return; + } + } + + } + 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 + { + + 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) + { + 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) + { + if(routingKey == null) + { + routingKey = AMQShortString.EMPTY_STRING; + } + + AMQShortStringTokenizer routingTokens = routingKey.tokenize(TOPIC_SEPARATOR); + + List<AMQShortString> subscriptionList = new ArrayList<AMQShortString>(); + + while (routingTokens.hasMoreTokens()) + { + subscriptionList.add(routingTokens.nextToken()); + } + + int size = subscriptionList.size(); + + for (int index = 0; index < size; index++) + { + // if there are more levels + if ((index + 1) < size) + { + if (subscriptionList.get(index).equals(AMQP_HASH_TOKEN)) + { + if (subscriptionList.get(index + 1).equals(AMQP_HASH_TOKEN)) + { + // we don't need #.# delete this one + subscriptionList.remove(index); + size--; + // redo this normalisation + index--; + } + + if (subscriptionList.get(index + 1).equals(AMQP_STAR_TOKEN)) + { + // we don't want #.* swap to *.# + // remove it and put it in at index + 1 + subscriptionList.add(index + 1, subscriptionList.remove(index)); + } + } + } // if we have more levels + } + + + + AMQShortString normalizedString = AMQShortString.join(subscriptionList, TOPIC_SEPARATOR_AS_SHORTSTRING); + + return normalizedString; + } + + public void route(IncomingMessage payload) throws AMQException + { + + final AMQShortString routingKey = payload.getRoutingKey(); + + Collection<AMQQueue> queues = getMatchedQueues(payload, routingKey); + + if(queues == null || queues.isEmpty()) + { + _logger.info("Message routing key: " + payload.getRoutingKey() + " No routes."); + } + + payload.enqueue(queues); + + } + + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) + { + return isBound(routingKey, queue); + } + + public boolean isBound(AMQShortString routingKey, AMQQueue queue) + { + Binding binding = new Binding(routingKey, queue); + + return _bindings.containsKey(binding); + } + + public boolean isBound(AMQShortString routingKey) + { + for(Binding b : _bindings.keySet()) + { + if(b.getBindingKey().equals(routingKey)) + { + return true; + } + } + + return false; + } + + public boolean isBound(AMQQueue queue) + { + for(Binding b : _bindings.keySet()) + { + if(b.getQueue().equals(queue)) + { + return true; + } + } + + return false; + } + + public boolean hasBindings() + { + return !_bindings.isEmpty(); + } + + public synchronized void deregisterQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + assert rKey != null; + + Binding binding = new Binding(rKey, queue); + + + if (!_bindings.containsKey(binding)) + { + throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue.getName() + " was not registered with exchange " + this.getName() + + " with routing key " + rKey + "."); + } + + FieldTable bindingArgs = _bindings.remove(binding); + AMQShortString bindingKey = normalize(rKey); + TopicExchangeResult result = _topicExchangeResults.get(bindingKey); + if(argumentsContainSelector(bindingArgs)) + { + result.removeFilteredQueue(queue, createSelectorFilter(bindingArgs)); + } + else + { + result.removeUnfilteredQueue(queue); + } + + } + + protected ExchangeMBean createMBean() throws AMQException + { + try + { + return new TopicExchangeMBean(); + } + catch (JMException ex) + { + _logger.error("Exception occured in creating the topic exchenge mbean", ex); + throw new AMQException("Exception occured in creating the topic exchenge mbean", ex); + } + } + + private Collection<AMQQueue> getMatchedQueues(IncomingMessage message, AMQShortString routingKey) + { + + Collection<TopicMatcherResult> results = _parser.parse(routingKey); + if(results.isEmpty()) + { + return Collections.EMPTY_SET; + } + else + { + Collection<AMQQueue> queues = results.size() == 1 ? null : new HashSet<AMQQueue>(); + for(TopicMatcherResult result : results) + { + + queues = ((TopicExchangeResult)result).processMessage(message, queues); + } + return queues; + } + + + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKey.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKey.java new file mode 100644 index 0000000000..8fdb91cbef --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKey.java @@ -0,0 +1,40 @@ +package org.apache.qpid.server.exchange.headers; + +import org.apache.qpid.framing.AMQShortString; + +/* +* +* 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 HeaderKey +{ + public static final HeaderKey UNKNOWN = new HeaderKey(new AMQShortString("<< UNKNOWN >>")); + private AMQShortString _key; + + public HeaderKey(final AMQShortString key) + { + _key = key; + } + + public String toString() + { + return _key.toString(); + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKeyDictionary.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKeyDictionary.java new file mode 100644 index 0000000000..7be99a88c9 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKeyDictionary.java @@ -0,0 +1,50 @@ +package org.apache.qpid.server.exchange.headers; + +import org.apache.qpid.framing.AMQShortString; + +import java.util.Map; +import java.util.HashMap; + +/* +* +* 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 HeaderKeyDictionary +{ + + private final Map<AMQShortString, HeaderKey> _dictionary = new HashMap<AMQShortString, HeaderKey>(); + + + public HeaderKey get(final AMQShortString key) + { + HeaderKey headerKey = _dictionary.get(key); + return headerKey == null ? HeaderKey.UNKNOWN : headerKey; + } + + public HeaderKey getOrCreate(final AMQShortString key) + { + HeaderKey headerKey = _dictionary.get(key); + if(headerKey == null) + { + headerKey = new HeaderKey(key); + _dictionary.put(key, headerKey); + } + return headerKey; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderMatcherResult.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderMatcherResult.java new file mode 100644 index 0000000000..518064bb29 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderMatcherResult.java @@ -0,0 +1,25 @@ +package org.apache.qpid.server.exchange.headers; + +/* +* +* 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 HeaderMatcherResult +{ +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersMatcherDFAState.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersMatcherDFAState.java new file mode 100644 index 0000000000..9da93d483a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersMatcherDFAState.java @@ -0,0 +1,339 @@ +package org.apache.qpid.server.exchange.headers; + +import org.apache.qpid.framing.AMQTypedValue; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.exchange.topic.TopicMatcherDFAState; +import org.apache.qpid.server.exchange.topic.TopicWord; +import org.apache.qpid.server.exchange.topic.TopicMatcherResult; + +import java.util.*; + +/* +* +* 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 HeadersMatcherDFAState +{ + + + private final Collection<HeaderMatcherResult> _results; + private final Map<HeaderKey, Map<AMQTypedValue,HeadersMatcherDFAState>> _nextStateMap; + private final HeaderKeyDictionary _dictionary; + + public HeadersMatcherDFAState(Map<HeaderKey, Map<AMQTypedValue,HeadersMatcherDFAState>> nextStateMap, + Collection<HeaderMatcherResult> results, + HeaderKeyDictionary dictionary) + { + _nextStateMap = nextStateMap; + _results = results; + _dictionary = dictionary; + } + + + public Collection<HeaderMatcherResult> match(final FieldTable table) + { + return match(table.iterator()); + } + + + + public Collection<HeaderMatcherResult> match(Iterator<Map.Entry<AMQShortString,AMQTypedValue>> fieldTableIterator) + { + + if(_nextStateMap.isEmpty()) + { + return _results; + } + + while(fieldTableIterator.hasNext()) + { + + Map.Entry<AMQShortString, AMQTypedValue> fieldTableEntry = fieldTableIterator.next(); + HeaderKey key = _dictionary.get(fieldTableEntry.getKey()); + if(key != HeaderKey.UNKNOWN) + { + Map<AMQTypedValue, HeadersMatcherDFAState> valueToStateMap = _nextStateMap.get(key); + + if(valueToStateMap != null) + { + HeadersMatcherDFAState nextState = valueToStateMap.get(fieldTableEntry.getValue()); + + if(nextState == null) + { + nextState = valueToStateMap.get(null); + } + if(nextState != null && nextState != this) + { + return nextState.match(fieldTableIterator); + } + } + + } + } + + return _results; + + } + + + HeadersMatcherDFAState mergeStateMachines(HeadersMatcherDFAState otherStateMachine) + { + + assert(otherStateMachine._dictionary == _dictionary); + + Map<Set<HeadersMatcherDFAState>, HeadersMatcherDFAState> newStateMap= new HashMap<Set<HeadersMatcherDFAState>, HeadersMatcherDFAState>(); + + Collection<HeaderMatcherResult> results; + + if(_results.isEmpty()) + { + results = otherStateMachine._results; + } + else if(otherStateMachine._results.isEmpty()) + { + results = _results; + } + else + { + results = new HashSet<HeaderMatcherResult>(_results); + results.addAll(otherStateMachine._results); + } + + + final Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> newNextStateMap = new HashMap<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>>(); + + HeadersMatcherDFAState newState = new HeadersMatcherDFAState(newNextStateMap, results, _dictionary); + + + Set<HeadersMatcherDFAState> oldStates = new HashSet<HeadersMatcherDFAState>(); + oldStates.add(this); + oldStates.add(otherStateMachine); + + newStateMap.put(oldStates, newState); + + mergeStateMachines(oldStates, newNextStateMap, newStateMap); + + return newState; + + + } + + private void mergeStateMachines(final Set<HeadersMatcherDFAState> oldStates, + final Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> newNextStateMap, + final Map<Set<HeadersMatcherDFAState>, HeadersMatcherDFAState> newStateMap) + { + Map<HeaderKey, Map<AMQTypedValue, Set<HeadersMatcherDFAState>>> nfaMap = new HashMap<HeaderKey, Map<AMQTypedValue, Set<HeadersMatcherDFAState>>>(); + + Set<HeaderKey> distinctKeys = new HashSet<HeaderKey>(); + + for(HeadersMatcherDFAState state : oldStates) + { + Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> map = state._nextStateMap; + + for(Map.Entry<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> entry : map.entrySet()) + { + Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStatesMap = nfaMap.get(entry.getKey()); + + if(valueToStatesMap == null) + { + valueToStatesMap = new HashMap<AMQTypedValue, Set<HeadersMatcherDFAState>>(); + nfaMap.put(entry.getKey(), valueToStatesMap); + } + + for(Map.Entry<AMQTypedValue, HeadersMatcherDFAState> valueToStateEntry : entry.getValue().entrySet()) + { + Set<HeadersMatcherDFAState> states = valueToStatesMap.get(valueToStateEntry.getKey()); + if(states == null) + { + states = new HashSet<HeadersMatcherDFAState>(); + valueToStatesMap.put(valueToStateEntry.getKey(),states); + } + states.add(valueToStateEntry.getValue()); + } + + distinctKeys.add(entry.getKey()); + } + } + + Map<HeaderKey, Set<HeadersMatcherDFAState>> anyValueStates = new HashMap<HeaderKey, Set<HeadersMatcherDFAState>>(); + + for(HeaderKey distinctKey : distinctKeys) + { + Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStateMap = nfaMap.get(distinctKey); + if(valueToStateMap != null) + { + Set<HeadersMatcherDFAState> statesForKeyDefault = valueToStateMap.get(null); + if(statesForKeyDefault != null) + { + anyValueStates.put(distinctKey, statesForKeyDefault); + } + } + } + + // add the defaults for "null" to all other specified values of a given header key + + for( Map.Entry<HeaderKey,Map<AMQTypedValue,Set<HeadersMatcherDFAState>>> entry : nfaMap.entrySet()) + { + Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStatesMap = entry.getValue(); + for(Map.Entry<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStates : valueToStatesMap.entrySet()) + { + if(valueToStates.getKey() != null) + { + + + Set<HeadersMatcherDFAState> defaults = anyValueStates.get(entry.getKey()); + if(defaults != null) + { + valueToStates.getValue().addAll(defaults); + } + } + } + } + + // if a given header key is not mentioned in the map of a machine; then that machine would stay at the same state + // for that key. + for(HeaderKey distinctKey : distinctKeys) + { + Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStatesMap = nfaMap.get(distinctKey); + for(HeadersMatcherDFAState oldState : oldStates) + { + if(!oldState._nextStateMap.containsKey(distinctKey)) + { + for(Set<HeadersMatcherDFAState> endStates : valueToStatesMap.values()) + { + endStates.add(oldState); + } + } + } + } + + + + + for(Map.Entry<HeaderKey,Map<AMQTypedValue,Set<HeadersMatcherDFAState>>> transitionClass : nfaMap.entrySet()) + { + Map<AMQTypedValue, HeadersMatcherDFAState> valueToDFAState = newNextStateMap.get(transitionClass.getKey()); + if(valueToDFAState == null) + { + valueToDFAState = new HashMap<AMQTypedValue, HeadersMatcherDFAState>(); + newNextStateMap.put(transitionClass.getKey(), valueToDFAState); + } + + for(Map.Entry<AMQTypedValue,Set<HeadersMatcherDFAState>> transition : transitionClass.getValue().entrySet()) + { + Set<HeadersMatcherDFAState> destinations = transition.getValue(); + + + HeadersMatcherDFAState nextState = newStateMap.get(destinations); + + if(nextState == null) + { + + if(destinations.size() == 1) + { + nextState = destinations.iterator().next(); + newStateMap.put(destinations, nextState); + } + else + { + Collection<HeaderMatcherResult> results; + + Set<Collection<HeaderMatcherResult>> resultSets = new HashSet<Collection<HeaderMatcherResult>>(); + for(HeadersMatcherDFAState destination : destinations) + { + resultSets.add(destination._results); + } + resultSets.remove(Collections.EMPTY_SET); + if(resultSets.size() == 0) + { + results = Collections.EMPTY_SET; + } + else if(resultSets.size() == 1) + { + results = resultSets.iterator().next(); + } + else + { + results = new HashSet<HeaderMatcherResult>(); + for(Collection<HeaderMatcherResult> oldResult : resultSets) + { + results.addAll(oldResult); + } + } + + final Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> nextStateMap = new HashMap<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>>(); + + nextState = new HeadersMatcherDFAState(nextStateMap, results, _dictionary); + newStateMap.put(destinations, nextState); + + mergeStateMachines( + destinations, + nextStateMap, + newStateMap); + + + } + + + } + valueToDFAState.put(transition.getKey(),nextState); + } + } + + + + final ArrayList<HeaderKey> removeKeyList = new ArrayList<HeaderKey>(); + + for(Map.Entry<HeaderKey,Map<AMQTypedValue,HeadersMatcherDFAState>> entry : _nextStateMap.entrySet()) + { + final ArrayList<AMQTypedValue> removeValueList = new ArrayList<AMQTypedValue>(); + + for(Map.Entry<AMQTypedValue,HeadersMatcherDFAState> valueToDFAState : entry.getValue().entrySet()) + { + if(valueToDFAState.getValue() == this) + { + HeadersMatcherDFAState defaultState = entry.getValue().get(null); + if(defaultState == null || defaultState == this) + { + removeValueList.add(valueToDFAState.getKey()); + } + } + } + + for(AMQTypedValue removeValue : removeValueList) + { + entry.getValue().remove(removeValue); + } + + if(entry.getValue().isEmpty()) + { + removeKeyList.add(entry.getKey()); + } + + } + + for(HeaderKey removeKey : removeKeyList) + { + _nextStateMap.remove(removeKey); + } + + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java new file mode 100644 index 0000000000..85e74122c3 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java @@ -0,0 +1,439 @@ +package org.apache.qpid.server.exchange.headers; + +import org.apache.qpid.framing.*; + +import java.util.*; + +/* +* +* 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 HeadersParser +{ + + private final HeaderKeyDictionary _dictionary = new HeaderKeyDictionary(); + private static final AMQShortString MATCHING_TYPE_KEY = new AMQShortString("x-match"); + private static final String ANY_MATCHING = "any"; + private static final AMQShortString RESERVED_KEY_PREFIX = new AMQShortString("x-"); + + + HeadersMatcherDFAState createStateMachine(FieldTable bindingArguments, HeaderMatcherResult result) + { + String matchingType = bindingArguments.getString(MATCHING_TYPE_KEY); + boolean matchAny = matchingType.equalsIgnoreCase(ANY_MATCHING); + if(matchAny) + { + return createStateMachineForAnyMatch(bindingArguments, result); + } + else + { + return createStateMachineForAllMatch(bindingArguments, result); + } + + + } + + + private HeadersMatcherDFAState createStateMachineForAnyMatch(final FieldTable bindingArguments, + final HeaderMatcherResult result) + { + + // DFAs for "any" matches have only two states, "not-matched" and "matched"... they start in the former + // and upon meeting any of the criteria they move to the latter + + //noinspection unchecked + final HeadersMatcherDFAState successState = + new HeadersMatcherDFAState(Collections.EMPTY_MAP,Collections.singleton(result),_dictionary); + + Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> nextStateMap = + new HashMap<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>>(); + + Set<AMQShortString> seenKeys = new HashSet<AMQShortString>(); + + Iterator<Map.Entry<AMQShortString, AMQTypedValue>> tableIterator = bindingArguments.iterator(); + + while(tableIterator.hasNext()) + { + final Map.Entry<AMQShortString, AMQTypedValue> entry = tableIterator.next(); + final AMQShortString key = entry.getKey(); + final AMQTypedValue value = entry.getValue(); + + + if(seenKeys.add(key) && !key.startsWith(RESERVED_KEY_PREFIX)) + { + final AMQType type = value.getType(); + + final HeaderKey headerKey = _dictionary.getOrCreate(key); + final Map<AMQTypedValue, HeadersMatcherDFAState> valueMap; + + if(type == AMQType.VOID || + ((type == AMQType.ASCII_STRING || type == AMQType.WIDE_STRING) && ((CharSequence)value.getValue()).length() == 0)) + { + valueMap = Collections.singletonMap(null,successState); + + } + else + { + valueMap = Collections.singletonMap(value,successState); + } + nextStateMap.put(headerKey,valueMap); + + } + + } + + if(seenKeys.size() == 0) + { + return successState; + } + else + { + return new HeadersMatcherDFAState(nextStateMap,Collections.EMPTY_SET,_dictionary); + } + + + } + + + private HeadersMatcherDFAState createStateMachineForAllMatch(final FieldTable bindingArguments, + final HeaderMatcherResult result) + { + // DFAs for "all" matches have a "success" state, a "fail" state, and states for every subset of + // matches which are possible, starting with the empty subset. For example if we have a binding + // { x-match="all" + // a=1 + // b=1 + // c=1 + // d=1 } + // Then we would have the following states + // (1) Seen none of a, b, c, or d + // (2) Seen a=1 ; none of b,c, or d + // (3) Seen b=1 ; none of a,c, or d + // (4) Seen c=1 ; none of a,b, or d + // (5) Seen d=1 ; none of a,b, or c + // (6) Seen a=1,b=1 ; none of c,d + // (7) Seen a=1,c=1 ; none of b,d + // (8) Seen a=1,d=1 ; none of b,c + // (9) Seen b=1,c=1 ; none of a,d + //(10) Seen b=1,d=1 ; none of c,d + //(11) Seen c=1,d=1 ; none of a,b + //(12) Seen a=1,b=1,c=1 ; not d + //(13) Seen a=1,b=1,d=1 ; not c + //(14) Seen a=1,c=1,d=1 ; not b + //(15) Seen b=1,c=1,d=1 ; not a + //(16) success + //(17) fail + // + // All states but (16) can transition to (17); additionally: + // (1) can transition to (2),(3),(4),(5) + // (2) can transition to (6),(7),(8) + // (3) can transition to (6),(9),(10) + // (4) can transition to (7),(9),(11) + // (5) can transition to (8),(10),(11) + // (6) can transition to (12),(13) + // (7) can transition to (12),(14) + // (8) can transition to (13),(14) + // (9) can transition to (12),(15) + //(10) can transition to (13),(15) + //(11) can transition to (14),(15) + //(12)-(15) can transition to (16) + + Set<AMQShortString> seenKeys = new HashSet<AMQShortString>(); + List<KeyValuePair> requiredTerms = new ArrayList<KeyValuePair>(bindingArguments.size()); + + Iterator<Map.Entry<AMQShortString, AMQTypedValue>> tableIterator = bindingArguments.iterator(); + + + + while(tableIterator.hasNext()) + { + final Map.Entry<AMQShortString, AMQTypedValue> entry = tableIterator.next(); + final AMQShortString key = entry.getKey(); + final AMQTypedValue value = entry.getValue(); + + + if(seenKeys.add(key) && !key.startsWith(RESERVED_KEY_PREFIX)) + { + final AMQType type = value.getType(); + + if(type == AMQType.VOID || + ((type == AMQType.ASCII_STRING || type == AMQType.WIDE_STRING) && ((CharSequence)value.getValue()).length() == 0)) + { + requiredTerms.add(new KeyValuePair(_dictionary.getOrCreate(key),null)); + } + else + { + requiredTerms.add(new KeyValuePair(_dictionary.getOrCreate(key),value)); + } + } + + } + + final HeadersMatcherDFAState successState = + new HeadersMatcherDFAState(Collections.EMPTY_MAP,Collections.singleton(result),_dictionary); + + final HeadersMatcherDFAState failState = + new HeadersMatcherDFAState(Collections.EMPTY_MAP,Collections.EMPTY_SET,_dictionary); + + Map<Set<KeyValuePair>, HeadersMatcherDFAState> notSeenTermsToStateMap = + new HashMap<Set<KeyValuePair>, HeadersMatcherDFAState>(); + + notSeenTermsToStateMap.put(Collections.EMPTY_SET, successState); + + + final int numberOfTerms = requiredTerms.size(); + + for(int numMissingTerms = 1; numMissingTerms <= numberOfTerms; numMissingTerms++) + { + int[] pos = new int[numMissingTerms]; + for(int i = 0; i < numMissingTerms; i++) + { + pos[i] = i; + } + + final int maxTermValue = (numberOfTerms - (numMissingTerms - 1)); + + while(pos[0] < maxTermValue) + { + + Set<KeyValuePair> stateSet = new HashSet<KeyValuePair>(); + for(int posIndex = 0; posIndex < pos.length; posIndex++) + { + stateSet.add(requiredTerms.get(pos[posIndex])); + } + + final Map<HeaderKey, Map<AMQTypedValue,HeadersMatcherDFAState>> nextStateMap = + new HashMap<HeaderKey, Map<AMQTypedValue,HeadersMatcherDFAState>>(); + + + for(int posIndex = 0; posIndex < pos.length; posIndex++) + { + KeyValuePair nextTerm = requiredTerms.get(pos[posIndex]); + HashSet<KeyValuePair> nextStateSet = + new HashSet<KeyValuePair>(stateSet); + nextStateSet.remove(nextTerm); + + Map<AMQTypedValue, HeadersMatcherDFAState> valueToStateMap = + new HashMap<AMQTypedValue, HeadersMatcherDFAState>(); + nextStateMap.put(nextTerm._key, valueToStateMap); + + valueToStateMap.put( nextTerm._value,notSeenTermsToStateMap.get(nextStateSet)); + if(nextTerm._value != null) + { + valueToStateMap.put(null, failState); + } + + + } + + + HeadersMatcherDFAState newState = new HeadersMatcherDFAState(nextStateMap, Collections.EMPTY_SET, _dictionary); + + notSeenTermsToStateMap.put(stateSet, newState); + + int i = numMissingTerms; + while(i-- != 0) + { + if(++pos[i] <= numberOfTerms -(numMissingTerms-i)) + { + int k = pos[i]; + for(int j = i+1; j < numMissingTerms; j++) + { + pos[j] = ++k; + } + break; + } + } + } + + + + + } + + + return notSeenTermsToStateMap.get(new HashSet<KeyValuePair>(requiredTerms)); + + + + } + + public static void main(String[] args) throws AMQFrameDecodingException + { + + FieldTable bindingTable = new FieldTable(); + + bindingTable.setString(new AMQShortString("x-match"),"all"); + bindingTable.setInteger("a",1); + bindingTable.setVoid(new AMQShortString("b")); + bindingTable.setString("c",""); + bindingTable.setInteger("d",4); + bindingTable.setInteger("e",1); + + + + FieldTable bindingTable2 = new FieldTable(); + bindingTable2.setString(new AMQShortString("x-match"),"all"); + bindingTable2.setInteger("a",1); + bindingTable2.setVoid(new AMQShortString("b")); + bindingTable2.setString("c",""); + bindingTable2.setInteger("d",4); + bindingTable2.setInteger("e",1); + bindingTable2.setInteger("f",1); + + + FieldTable table = new FieldTable(); + table.setInteger("a",1); + table.setInteger("b",2); + table.setString("c",""); + table.setInteger("d",4); + table.setInteger("e",1); + table.setInteger("f",1); + table.setInteger("h",1); + table.setInteger("i",1); + table.setInteger("j",1); + table.setInteger("k",1); + table.setInteger("l",1); + + org.apache.mina.common.ByteBuffer buffer = org.apache.mina.common.ByteBuffer.allocate( (int) table.getEncodedSize()); + EncodingUtils.writeFieldTableBytes(buffer, table); + buffer.flip(); + + FieldTable table2 = EncodingUtils.readFieldTable(buffer); + + + + FieldTable bindingTable3 = new FieldTable(); + bindingTable3.setString(new AMQShortString("x-match"),"any"); + bindingTable3.setInteger("a",1); + bindingTable3.setInteger("b",3); + + + FieldTable bindingTable4 = new FieldTable(); + bindingTable4.setString(new AMQShortString("x-match"),"any"); + bindingTable4.setVoid(new AMQShortString("a")); + + + FieldTable bindingTable5 = new FieldTable(); + bindingTable5.setString(new AMQShortString("x-match"),"all"); + bindingTable5.setString(new AMQShortString("h"),"hello"); + + for(int i = 0; i < 100; i++) + { + printMatches(new FieldTable[] {bindingTable5} , table2); + } + + + + } + + + + private static void printMatches(final FieldTable[] bindingKeys, final FieldTable routingKey) + { + HeadersMatcherDFAState sm = null; + Map<HeaderMatcherResult, String> resultMap = new HashMap<HeaderMatcherResult, String>(); + + HeadersParser parser = new HeadersParser(); + + for(int i = 0; i < bindingKeys.length; i++) + { + HeaderMatcherResult r = new HeaderMatcherResult(); + resultMap.put(r, bindingKeys[i].toString()); + + + if(i==0) + { + sm = parser.createStateMachine(bindingKeys[i], r); + } + else + { + sm = sm.mergeStateMachines(parser.createStateMachine(bindingKeys[i], r)); + } + } + + Collection<HeaderMatcherResult> results = null; + long beforeTime = System.currentTimeMillis(); + for(int i = 0; i < 1000000; i++) + { + routingKey.size(); + + assert sm != null; + results = sm.match(routingKey); + + } + long elapsed = System.currentTimeMillis() - beforeTime; + System.out.println("1000000 Iterations took: " + elapsed); + Collection<String> resultStrings = new ArrayList<String>(); + + assert results != null; + for(HeaderMatcherResult result : results) + { + resultStrings.add(resultMap.get(result)); + } + + final ArrayList<String> nonMatches = new ArrayList<String>(); + for(FieldTable key : bindingKeys) + { + nonMatches.add(key.toString()); + } + nonMatches.removeAll(resultStrings); + System.out.println("\""+routingKey+"\" matched with " + resultStrings + " DID NOT MATCH with " + nonMatches); + + + } + + + public final static class KeyValuePair + { + public final HeaderKey _key; + public final AMQTypedValue _value; + private final int _hashCode; + + public KeyValuePair(final HeaderKey key, final AMQTypedValue value) + { + _key = key; + _value = value; + int hash = (1 + 31 * _key.hashCode()); + if(_value != null) + { + hash+=_value.hashCode(); + } + _hashCode = hash; + } + + public int hashCode() + { + return _hashCode; + } + + public boolean equals(Object o) + { + KeyValuePair other = (KeyValuePair)o; + return (_key == other._key) && (_value == null ? other._value == null : _value.equals(other._value)); + } + + + public String toString() + { + return "{" + _key + " -> " + _value + "}"; + } + + } +} 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 new file mode 100644 index 0000000000..36076cf75b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java @@ -0,0 +1,295 @@ +package org.apache.qpid.server.exchange.topic; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.AMQShortStringTokenizer; + +import java.util.*; +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 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)'.'; + + + public TopicMatcherDFAState(Map<TopicWord, TopicMatcherDFAState> nextStateMap, + Collection<TopicMatcherResult> results ) + { + _nextStateMap = nextStateMap; + _results = results; + } + + + public TopicMatcherDFAState nextState(TopicWord word) + { + final TopicMatcherDFAState nextState = _nextStateMap.get(word); + return nextState == null ? _nextStateMap.get(TopicWord.ANY_WORD) : nextState; + } + + public Collection<TopicMatcherResult> terminate() + { + return _results; + } + + + public Collection<TopicMatcherResult> parse(TopicWordDictionary dictionary, AMQShortString routingKey) + { + return parse(dictionary, routingKey.tokenize(TOPIC_DELIMITTER)); + } + + private Collection<TopicMatcherResult> parse(final TopicWordDictionary dictionary, + final AMQShortStringTokenizer tokens) + { + if(!tokens.hasMoreTokens()) + { + return _results; + } + TopicWord word = dictionary.getWord(tokens.nextToken()); + TopicMatcherDFAState nextState = _nextStateMap.get(word); + if(nextState == null && word != TopicWord.ANY_WORD) + { + nextState = _nextStateMap.get(TopicWord.ANY_WORD); + } + if(nextState == null) + { + return Collections.EMPTY_SET; + } + // Shortcut if we are at a looping terminal state + if((nextState == this) && (_nextStateMap.size() == 1) && _nextStateMap.containsKey(TopicWord.ANY_WORD)) + { + return _results; + } + + return nextState.parse(dictionary, tokens); + + } + + + public TopicMatcherDFAState mergeStateMachines(TopicMatcherDFAState otherStateMachine) + { + Map<Set<TopicMatcherDFAState>, TopicMatcherDFAState> newStateMap= new HashMap<Set<TopicMatcherDFAState>, TopicMatcherDFAState>(); + + Collection<TopicMatcherResult> results; + + if(_results.isEmpty()) + { + results = otherStateMachine._results; + } + else if(otherStateMachine._results.isEmpty()) + { + results = _results; + } + else + { + results = new HashSet<TopicMatcherResult>(_results); + results.addAll(otherStateMachine._results); + } + + + final Map<TopicWord, TopicMatcherDFAState> newNextStateMap = new HashMap<TopicWord, TopicMatcherDFAState>(); + + TopicMatcherDFAState newState = new TopicMatcherDFAState(newNextStateMap, results); + + + Set<TopicMatcherDFAState> oldStates = new HashSet<TopicMatcherDFAState>(); + oldStates.add(this); + oldStates.add(otherStateMachine); + + newStateMap.put(oldStates, newState); + + mergeStateMachines(oldStates, newNextStateMap, newStateMap); + + return newState; + + } + + private static void mergeStateMachines( + final Set<TopicMatcherDFAState> oldStates, + final Map<TopicWord, TopicMatcherDFAState> newNextStateMap, + final Map<Set<TopicMatcherDFAState>, TopicMatcherDFAState> newStateMap) + { + Map<TopicWord, Set<TopicMatcherDFAState>> nfaMap = new HashMap<TopicWord, Set<TopicMatcherDFAState>>(); + + for(TopicMatcherDFAState state : oldStates) + { + Map<TopicWord, TopicMatcherDFAState> map = state._nextStateMap; + for(Map.Entry<TopicWord, TopicMatcherDFAState> entry : map.entrySet()) + { + Set<TopicMatcherDFAState> states = nfaMap.get(entry.getKey()); + if(states == null) + { + states = new HashSet<TopicMatcherDFAState>(); + nfaMap.put(entry.getKey(), states); + } + states.add(entry.getValue()); + } + } + + Set<TopicMatcherDFAState> anyWordStates = nfaMap.get(TopicWord.ANY_WORD); + + for(Map.Entry<TopicWord, Set<TopicMatcherDFAState>> transition : nfaMap.entrySet()) + { + Set<TopicMatcherDFAState> destinations = transition.getValue(); + + if(anyWordStates != null) + { + destinations.addAll(anyWordStates); + } + + TopicMatcherDFAState nextState = newStateMap.get(destinations); + if(nextState == null) + { + + if(destinations.size() == 1) + { + nextState = destinations.iterator().next(); + newStateMap.put(destinations, nextState); + } + else + { + Collection<TopicMatcherResult> results; + + Set<Collection<TopicMatcherResult>> resultSets = new HashSet<Collection<TopicMatcherResult>>(); + for(TopicMatcherDFAState destination : destinations) + { + resultSets.add(destination._results); + } + resultSets.remove(Collections.EMPTY_SET); + if(resultSets.size() == 0) + { + results = Collections.EMPTY_SET; + } + else if(resultSets.size() == 1) + { + results = resultSets.iterator().next(); + } + else + { + results = new HashSet<TopicMatcherResult>(); + for(Collection<TopicMatcherResult> oldResult : resultSets) + { + results.addAll(oldResult); + } + } + + final Map<TopicWord, TopicMatcherDFAState> nextStateMap = new HashMap<TopicWord, TopicMatcherDFAState>(); + + nextState = new TopicMatcherDFAState(nextStateMap, results); + newStateMap.put(destinations, nextState); + + mergeStateMachines( + destinations, + nextStateMap, + newStateMap); + + + } + + + } + newNextStateMap.put(transition.getKey(),nextState); + } + + // Remove redundant transitions where defined tokenWord has same action as ANY_WORD + TopicMatcherDFAState anyWordState = newNextStateMap.get(TopicWord.ANY_WORD); + if(anyWordState != null) + { + List<TopicWord> removeList = new ArrayList<TopicWord>(); + for(Map.Entry<TopicWord,TopicMatcherDFAState> entry : newNextStateMap.entrySet()) + { + if(entry.getValue() == anyWordState && entry.getKey() != TopicWord.ANY_WORD) + { + removeList.add(entry.getKey()); + } + } + for(TopicWord removeKey : removeList) + { + newNextStateMap.remove(removeKey); + } + } + + + + } + + + 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 new file mode 100644 index 0000000000..71d30adfac --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java @@ -0,0 +1,25 @@ +package org.apache.qpid.server.exchange.topic; + +/* +* +* 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 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 new file mode 100644 index 0000000000..3e9facf412 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java @@ -0,0 +1,613 @@ +package org.apache.qpid.server.exchange.topic; + +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; + +/* +* +* 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 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 + { + private final TopicWord _word; + 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) + { + _position = position; + _word = word; + _selfTransition = selfTransition; + _endState = endState; + } + + + } + + private static final Position ERROR_POSITION = new Position(Integer.MAX_VALUE,null, true, false); + + private static class SimpleState + { + Set<Position> _positions; + Map<TopicWord, SimpleState> _nextState; + } + + + 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); + int wildCards = 0; + for(TopicWord word : wordList) + { + if(word == TopicWord.WILDCARD_WORD) + { + wildCards++; + } + } + if(wildCards == 0) + { + TopicMatcherDFAState[] states = new TopicMatcherDFAState[wordList.size()+1]; + states[states.length-1] = new TopicMatcherDFAState(Collections.EMPTY_MAP, Collections.singleton(result)); + for(int i = states.length-2; i >= 0; i--) + { + states[i] = new TopicMatcherDFAState(Collections.singletonMap(wordList.get(i),states[i+1]),Collections.EMPTY_SET); + + } + return states[0]; + } + else if(wildCards == wordList.size()) + { + Map<TopicWord,TopicMatcherDFAState> stateMap = new HashMap<TopicWord,TopicMatcherDFAState>(); + TopicMatcherDFAState state = new TopicMatcherDFAState(stateMap, Collections.singleton(result)); + stateMap.put(TopicWord.ANY_WORD, state); + return state; + } + + + int positionCount = wordList.size() - wildCards; + + Position[] positions = new Position[positionCount+1]; + + int lastWord; + + if(wordList.get(wordList.size()-1)== TopicWord.WILDCARD_WORD) + { + lastWord = wordList.size()-1; + positions[positionCount] = new Position(positionCount, TopicWord.ANY_WORD, true, true); + } + else + { + lastWord = wordList.size(); + positions[positionCount] = new Position(positionCount, TopicWord.ANY_WORD, false, true); + } + + + int pos = 0; + int wordPos = 0; + + + while(wordPos < lastWord) + { + TopicWord word = wordList.get(wordPos++); + + if(word == TopicWord.WILDCARD_WORD) + { + int nextWordPos = wordPos++; + word = wordList.get(nextWordPos); + + positions[pos] = new Position(pos++,word,true,false); + } + else + { + positions[pos] = new Position(pos++,word,false,false); + } + + } + + + 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... + + Map<Set<Position>,SimpleState> stateMap = new HashMap<Set<Position>,SimpleState>(); + + + SimpleState state = new SimpleState(); + state._positions = Collections.singleton( positions[0] ); + stateMap.put(state._positions, state); + + calculateNextStates(state, stateMap, positions); + + SimpleState[] simpleStates = stateMap.values().toArray(new SimpleState[stateMap.size()]); + HashMap<TopicWord, TopicMatcherDFAState>[] dfaStateMaps = new HashMap[simpleStates.length]; + Map<SimpleState, TopicMatcherDFAState> simple2DFAMap = new HashMap<SimpleState, TopicMatcherDFAState>(); + + for(int i = 0; i < simpleStates.length; i++) + { + + Collection<TopicMatcherResult> results; + boolean endState = false; + + for(Position p : simpleStates[i]._positions) + { + if(p._endState) + { + endState = true; + break; + } + } + + if(endState) + { + results = Collections.singleton(result); + } + else + { + results = Collections.EMPTY_SET; + } + + dfaStateMaps[i] = new HashMap<TopicWord, TopicMatcherDFAState>(); + simple2DFAMap.put(simpleStates[i], new TopicMatcherDFAState(dfaStateMaps[i],results)); + + } + for(int i = 0; i < simpleStates.length; i++) + { + SimpleState simpleState = simpleStates[i]; + + Map<TopicWord, SimpleState> nextSimpleStateMap = simpleState._nextState; + for(Map.Entry<TopicWord, SimpleState> stateMapEntry : nextSimpleStateMap.entrySet()) + { + dfaStateMaps[i].put(stateMapEntry.getKey(), simple2DFAMap.get(stateMapEntry.getValue())); + } + + } + + return simple2DFAMap.get(state); + + } + + + + private void calculateNextStates(final SimpleState state, + final Map<Set<Position>, SimpleState> stateMap, + final Position[] positions) + { + Map<TopicWord, Set<Position>> transitions = new HashMap<TopicWord,Set<Position>>(); + + for(Position pos : state._positions) + { + if(pos._selfTransition) + { + Set<Position> dest = transitions.get(TopicWord.ANY_WORD); + if(dest == null) + { + dest = new HashSet<Position>(); + transitions.put(TopicWord.ANY_WORD,dest); + } + dest.add(pos); + } + + final int nextPos = pos._position + 1; + Position nextPosition = nextPos == positions.length ? ERROR_POSITION : positions[nextPos]; + + Set<Position> dest = transitions.get(pos._word); + if(dest == null) + { + dest = new HashSet<Position>(); + transitions.put(pos._word,dest); + } + dest.add(nextPosition); + + } + + Set<Position> anyWordTransitions = transitions.get(TopicWord.ANY_WORD); + if(anyWordTransitions != null) + { + for(Set<Position> dest : transitions.values()) + { + dest.addAll(anyWordTransitions); + } + } + + state._nextState = new HashMap<TopicWord, SimpleState>(); + + for(Map.Entry<TopicWord,Set<Position>> dest : transitions.entrySet()) + { + + if(dest.getValue().size()>1) + { + dest.getValue().remove(ERROR_POSITION); + } + Position loopingTerminal = null; + for(Position destPos : dest.getValue()) + { + if(destPos._selfTransition && destPos._endState) + { + loopingTerminal = destPos; + break; + } + } + + if(loopingTerminal!=null) + { + 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) + { + stateForEntry = new SimpleState(); + stateForEntry._positions = dest.getValue(); + stateMap.put(dest.getValue(),stateForEntry); + calculateNextStates(stateForEntry, + stateMap, + positions); + } + state._nextState.put(dest.getKey(),stateForEntry); + + + + } + + // remove redundant transitions + SimpleState anyWordState = state._nextState.get(TopicWord.ANY_WORD); + if(anyWordState != null) + { + List<TopicWord> removeList = new ArrayList<TopicWord>(); + for(Map.Entry<TopicWord,SimpleState> entry : state._nextState.entrySet()) + { + if(entry.getValue() == anyWordState && entry.getKey() != TopicWord.ANY_WORD) + { + removeList.add(entry.getKey()); + } + } + for(TopicWord removeKey : removeList) + { + state._nextState.remove(removeKey); + } + } + + + } + + private List<TopicWord> createTopicWordList(final AMQShortString bindingKey) + { + AMQShortStringTokenizer tokens = bindingKey.tokenize(TOPIC_DELIMITER); + TopicWord previousWord = null; + + List<TopicWord> wordList = new ArrayList<TopicWord>(); + + while(tokens.hasMoreTokens()) + { + TopicWord nextWord = _dictionary.getOrCreateWord(tokens.nextToken()); + if(previousWord == TopicWord.WILDCARD_WORD) + { + + if(nextWord == TopicWord.WILDCARD_WORD) + { + // consecutive wildcards can be merged + // i.e. subsequent wildcards can be discarded + continue; + } + else if(nextWord == TopicWord.ANY_WORD) + { + // wildcard and anyword can be reordered to always put anyword first + wordList.set(wordList.size()-1,TopicWord.ANY_WORD); + nextWord = TopicWord.WILDCARD_WORD; + } + } + wordList.add(nextWord); + previousWord = nextWord; + + } + return wordList; + } + + + 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"); + printMatches("a",""); + printMatches("","a"); + printMatches("a.b","a.b"); + printMatches("a","a.b"); + printMatches("a.b","a"); + printMatches("*","a"); + printMatches("*.b","a.b"); + printMatches("*.*","a.b"); + printMatches("a.*","a.b"); + printMatches("a.*.#","a.b"); + printMatches("a.#.b","a.b"); + + printMatches("#.b","a"); + printMatches("#.b","a.b"); + printMatches("#.a.b","a.b"); + + + printMatches("#",""); + printMatches("#","a"); + printMatches("#","a.b"); + printMatches("#.#","a.b"); + printMatches("#.*","a.b"); + + printMatches("#.a.b","a.b"); + printMatches("a.b.#","a.b"); + printMatches("a.#","a.b"); + printMatches("#.*.#","a.b"); + printMatches("#.*.b.#","a.b"); + printMatches("#.a.*.#","a.b"); + printMatches("#.a.#.b.#","a.b"); + printMatches("#.*.#.*.#","a.b"); + printMatches("*.#.*.#","a.b"); + printMatches("#.*.#.*","a.b"); + + + printMatches(new String[]{"a.#.b.#","a.*.#.b.#"},"a.b.b.b.b.b.b.b.c"); + + + printMatches(new String[]{"a.b", "a.c"},"a.b"); + printMatches(new String[]{"a.#", "a.c", "#.b"},"a.b"); + printMatches(new String[]{"a.#", "a.c", "#.b", "#", "*.*"},"a.b"); + + printMatches(new String[]{"a.b.c.d.e.#", "a.b.c.d.#", "a.b.c.d.*", "a.b.c.#", "#.e", "a.*.c.d.e","#.c.*.#.*.*"},"a.b.c.d.e"); + printMatches(new String[]{"a.b.c.d.e.#", "a.b.c.d.#", "a.b.c.d.*", "a.b.c.#", "#.e", "a.*.c.d.e","#.c.*.#.*.*"},"a.b.c.d.f.g"); + + + + + } + + private static void printMatches(final String[] bindingKeys, final String routingKey) + { + TopicMatcherDFAState sm = null; + Map<TopicMatcherResult, String> resultMap = new HashMap<TopicMatcherResult, String>(); + + TopicParser parser = new TopicParser(); + + long start = System.currentTimeMillis(); + for(int i = 0; i < bindingKeys.length; i++) + { + 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); + } + else + { + 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); + + Collection<TopicMatcherResult> results = sm.parse(parser._dictionary, routingKeyShortString); + Collection<String> resultStrings = new ArrayList<String>(); + + for(TopicMatcherResult result : results) + { + resultStrings.add(resultMap.get(result)); + } + + final ArrayList<String> nonMatches = new ArrayList<String>(Arrays.asList(bindingKeys)); + nonMatches.removeAll(resultStrings); + System.out.println("\""+routingKeyShortString+"\" matched with " + resultStrings + " DID NOT MATCH with " + nonMatches); + + + } + + private static void printMatches(String bindingKey, String routingKey) + { + printMatches(new String[] { bindingKey }, routingKey); + } + + + private static boolean matches(String bindingKey, String routingKey) + { + AMQShortString bindingKeyShortString = new AMQShortString(bindingKey); + AMQShortString routingKeyShortString = new AMQShortString(routingKey); + TopicParser parser = new TopicParser(); + + 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 new file mode 100644 index 0000000000..f14d70f8a1 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java @@ -0,0 +1,54 @@ +package org.apache.qpid.server.exchange.topic; + +import org.apache.qpid.framing.AMQShortString; + +import java.util.Map; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +/* +* +* 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 final class 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 new file mode 100644 index 0000000000..65a0cd3107 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java @@ -0,0 +1,63 @@ +package org.apache.qpid.server.exchange.topic; + +import org.apache.qpid.framing.AMQShortString; + +import java.util.concurrent.ConcurrentHashMap; + +/* +* +* 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 TopicWordDictionary +{ + private final ConcurrentHashMap<AMQShortString,TopicWord> _dictionary = + new ConcurrentHashMap<AMQShortString,TopicWord>(); + + + + public TopicWordDictionary() + { + _dictionary.put(new AMQShortString("*"), TopicWord.ANY_WORD); + _dictionary.put(new AMQShortString("#"), TopicWord.WILDCARD_WORD); + } + + + + + public TopicWord getOrCreateWord(AMQShortString name) + { + TopicWord word = _dictionary.putIfAbsent(name, new TopicWord(name)); + if(word == null) + { + word = _dictionary.get(name); + } + return word; + } + + + public TopicWord getWord(AMQShortString name) + { + TopicWord word = _dictionary.get(name); + if(word == null) + { + word = TopicWord.ANY_WORD; + } + return word; + } +} 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 44281a3aae..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 @@ -28,21 +28,21 @@ import java.util.List; import java.util.regex.Pattern; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; 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)); } @@ -73,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; @@ -81,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); @@ -138,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); @@ -148,11 +148,6 @@ public abstract class ComparisonExpression extends BinaryExpression implements B return null; } - if(rv instanceof AMQShortString) - { - rv = rv.toString(); - } - if (!(rv instanceof String)) { return @@ -163,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); @@ -241,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) @@ -429,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) @@ -454,40 +413,7 @@ public abstract class ComparisonExpression extends BinaryExpression implements B // try to convert up to allow the comparison. if (lc != rc) { - if(lc == AMQShortString.class) - { - if(rc == String.class) - { - rv = new AMQShortString((String) rv); - - if(right instanceof ConstantExpression) - { - ((ConstantExpression)right).setValue(rv); - } - } - else - { - return Boolean.FALSE; - } - } - else if(lc == String.class) - { - if(rc == AMQShortString.class) - { - lv = new AMQShortString((String) lv); - - if(left instanceof ConstantExpression) - { - ((ConstantExpression)left).setValue(lv); - } - } - else - { - return Boolean.FALSE; - } - - } - else if (lc == Byte.class) + if (lc == Byte.class) { if (rc == Short.class) { @@ -624,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 73c4c66ad7..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 @@ -26,23 +26,23 @@ package org.apache.qpid.server.filter; import java.math.BigDecimal; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; 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); @@ -121,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; } @@ -131,12 +131,6 @@ public class ConstantExpression implements Expression return value; } - public void setValue(final Object value) - { - this.value = value; - } - - /** * @see java.lang.Object#toString() */ 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 48b6602bda..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,45 +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); - if(_logger.isDebugEnabled()) - { - _logger.debug(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 e5e9acf9bb..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; - } - catch (AMQException e) - { - _logger.warn(e); - - return null; - } - - } - - }); - - 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; - } - 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("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("AMQMessageID", new Expression() - { - public Object evaluate(AMQMessage message) - { - - try - { - CommonContentHeaderProperties _properties = - (CommonContentHeaderProperties) - message.getContentHeaderBody().properties; - AMQShortString messageId = _properties.getMessageId(); - - return (messageId == null) ? null : messageId; - } - catch (AMQException e) - { - _logger.warn(e); - - return null; - } - - } - }); - - JMS_PROPERTY_EXPRESSIONS.put("JMSTimestamp", new Expression() - { - 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; - } - catch (AMQException e) - { - _logger.warn(e); + JMS_PROPERTY_EXPRESSIONS.put("JMSReplyTo", new ReplyToExpression()); - return null; - } + JMS_PROPERTY_EXPRESSIONS.put("JMSType", new TypeExpression()); - } - }); + JMS_PROPERTY_EXPRESSIONS.put("JMSDeliveryMode", new DeliveryModeExpression()); - JMS_PROPERTY_EXPRESSIONS.put("JMSExpiration", new Expression() - { - public Object evaluate(AMQMessage message) - { + JMS_PROPERTY_EXPRESSIONS.put("JMSPriority", new PriorityExpression()); - try - { - CommonContentHeaderProperties _properties = - (CommonContentHeaderProperties) - message.getContentHeaderBody().properties; + JMS_PROPERTY_EXPRESSIONS.put("AMQMessageID", new MessageIDExpression()); - return _properties.getExpiration(); - } - 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("JMSRedelivered", new Expression() + JMS_PROPERTY_EXPRESSIONS.put("JMSRedelivered", new Expression<E>() { - public Object evaluate(AMQMessage message) + public Object evaluate(Filterable message) throws E { return message.isRedelivered(); } }); - } - private final AMQShortString name; - private final Expression jmsPropertyExpression; + private final String name; + private final Expression<E> jmsPropertyExpression; + + public boolean outerTest() + { + return false; + } public PropertyExpression(String name) { - this.name = new AMQShortString(name); - jmsPropertyExpression = JMS_PROPERTY_EXPRESSIONS.get(name); + this.name = 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) @@ -283,7 +120,7 @@ public class PropertyExpression implements Expression } } - public AMQShortString getName() + public String getName() { return name; } @@ -293,7 +130,7 @@ public class PropertyExpression implements Expression */ public String toString() { - return name.toString(); + return name; } /** @@ -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 new file mode 100644 index 0000000000..895db7b15b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java @@ -0,0 +1,62 @@ +/* +* +* 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.flow; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.Set; +import java.util.HashSet; + +public abstract class AbstractFlowCreditManager implements FlowCreditManager +{ + protected final AtomicBoolean _suspended = new AtomicBoolean(false); + private final Set<FlowCreditManagerListener> _listeners = new HashSet<FlowCreditManagerListener>(); + + public final void addStateListener(FlowCreditManagerListener listener) + { + _listeners.add(listener); + } + + public final boolean removeListener(FlowCreditManagerListener listener) + { + return _listeners.remove(listener); + } + + private void notifyListeners(final boolean suspended) + { + for(FlowCreditManagerListener listener : _listeners) + { + listener.creditStateChanged(!suspended); + } + } + + protected final void setSuspended(final boolean suspended) + { + if(_suspended.compareAndSet(!suspended, suspended)) + { + notifyListeners(suspended); + } + } + + protected final void notifyIncreaseBytesCredit() + { + notifyListeners(false); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java new file mode 100644 index 0000000000..96a1071135 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java @@ -0,0 +1,77 @@ +package org.apache.qpid.server.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.Set; +import java.util.HashSet; + +/* +* +* 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 BytesOnlyCreditManager extends AbstractFlowCreditManager +{ + private final AtomicLong _bytesCredit; + + public BytesOnlyCreditManager(long initialCredit) + { + _bytesCredit = new AtomicLong(initialCredit); + } + + public void addCredit(long messageCredit, long bytesCredit) + { + _bytesCredit.addAndGet(bytesCredit); + setSuspended(false); + } + + public void removeAllCredit() + { + _bytesCredit.set(0L); + } + + public boolean hasCredit() + { + return _bytesCredit.get() > 0L; + } + + public boolean useCreditForMessage(AMQMessage msg) + { + final long msgSize = msg.getSize(); + if(hasCredit()) + { + if(_bytesCredit.addAndGet(-msgSize) >= 0) + { + return true; + } + else + { + _bytesCredit.addAndGet(msgSize); + setSuspended(true); + return false; + } + } + else + { + return false; + } + + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java new file mode 100644 index 0000000000..a249a6e63a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java @@ -0,0 +1,44 @@ +package org.apache.qpid.server.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +/* +* +* 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 interface FlowCreditManager +{ + + public static interface FlowCreditManagerListener + { + void creditStateChanged(boolean hasCredit); + } + + void addStateListener(FlowCreditManagerListener listener); + + boolean removeListener(FlowCreditManagerListener listener); + + public void addCredit(long messageCredit, long bytesCredit); + + public void removeAllCredit(); + + public boolean hasCredit(); + + public boolean useCreditForMessage(AMQMessage msg); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java new file mode 100644 index 0000000000..d63431c3eb --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java @@ -0,0 +1,44 @@ +package org.apache.qpid.server.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +/* +* +* 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 LimitlessCreditManager extends AbstractFlowCreditManager implements FlowCreditManager +{ + public void addCredit(long messageCredit, long bytesCredit) + { + } + + public void removeAllCredit() + { + } + + public boolean hasCredit() + { + return true; + } + + public boolean useCreditForMessage(AMQMessage msg) + { + return true; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java new file mode 100644 index 0000000000..9c377481de --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java @@ -0,0 +1,79 @@ +package org.apache.qpid.server.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +import java.util.concurrent.atomic.AtomicLong; + +/* +* +* 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 MessageAndBytesCreditManager extends AbstractFlowCreditManager implements FlowCreditManager +{ + private long _messageCredit; + private long _bytesCredit; + + MessageAndBytesCreditManager(final long messageCredit, final long bytesCredit) + { + _messageCredit = messageCredit; + _bytesCredit = bytesCredit; + } + + public synchronized void addCredit(long messageCredit, long bytesCredit) + { + _messageCredit += messageCredit; + _bytesCredit += bytesCredit; + setSuspended(hasCredit()); + } + + public synchronized void removeAllCredit() + { + _messageCredit = 0L; + _bytesCredit = 0L; + setSuspended(true); + } + + public synchronized boolean hasCredit() + { + return (_messageCredit > 0L) && ( _bytesCredit > 0L ); + } + + public synchronized boolean useCreditForMessage(AMQMessage msg) + { + if(_messageCredit == 0L) + { + setSuspended(true); + return false; + } + else + { + final long msgSize = msg.getSize(); + if(msgSize > _bytesCredit) + { + setSuspended(true); + return false; + } + _messageCredit--; + _bytesCredit -= msgSize; + setSuspended(false); + return true; + } + + } +} 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 new file mode 100644 index 0000000000..c1b3a09006 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java @@ -0,0 +1,76 @@ +package org.apache.qpid.server.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +import java.util.concurrent.atomic.AtomicLong; + +/* +* +* 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 MessageOnlyCreditManager extends AbstractFlowCreditManager implements FlowCreditManager +{ + private final AtomicLong _messageCredit; + + public MessageOnlyCreditManager(final long initialCredit) + { + _messageCredit = new AtomicLong(initialCredit); + } + + public void addCredit(long messageCredit, long bytesCredit) + { + setSuspended(false); + _messageCredit.addAndGet(messageCredit); + } + + public void removeAllCredit() + { + setSuspended(true); + _messageCredit.set(0L); + } + + public boolean hasCredit() + { + return _messageCredit.get() > 0L; + } + + public boolean useCreditForMessage(AMQMessage msg) + { + if(hasCredit()) + { + if(_messageCredit.addAndGet(-1L) >= 0) + { + setSuspended(false); + return true; + } + else + { + _messageCredit.addAndGet(1L); + setSuspended(true); + return false; + } + } + else + { + setSuspended(true); + return false; + } + + } +} 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 new file mode 100644 index 0000000000..be0300f2c1 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java @@ -0,0 +1,185 @@ +/* +* +* 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.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +public class Pre0_10CreditManager extends AbstractFlowCreditManager implements FlowCreditManager +{ + + private volatile long _bytesCreditLimit; + private volatile long _messageCreditLimit; + + private volatile long _bytesCredit; + private volatile long _messageCredit; + + public Pre0_10CreditManager(long bytesCreditLimit, long messageCreditLimit) + { + _bytesCreditLimit = bytesCreditLimit; + _messageCreditLimit = messageCreditLimit; + _bytesCredit = bytesCreditLimit; + _messageCredit = messageCreditLimit; + } + + + public synchronized void setCreditLimits(final long bytesCreditLimit, final long messageCreditLimit) + { + long bytesCreditChange = bytesCreditLimit - _bytesCreditLimit; + long messageCreditChange = messageCreditLimit - _messageCreditLimit; + + + + if(bytesCreditChange != 0L) + { + if(bytesCreditLimit == 0L) + { + _bytesCredit = 0; + } + else + { + _bytesCredit += bytesCreditChange; + } + } + + + if(messageCreditChange != 0L) + { + if(messageCreditLimit == 0L) + { + _messageCredit = 0; + } + else + { + _messageCredit += messageCreditChange; + } + } + + + _bytesCreditLimit = bytesCreditLimit; + _messageCreditLimit = messageCreditLimit; + + setSuspended(!hasCredit()); + + } + + + 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; + } + + + final long bytesCreditLimit = _bytesCreditLimit; + if(bytesCreditLimit != 0L) + { + long newCredit = _bytesCredit + bytesCredit; + _bytesCredit = newCredit > bytesCreditLimit ? bytesCreditLimit : newCredit; + if(notifyIncrease && bytesCredit>0) + { + notifyIncreaseBytesCredit(); + } + } + + + + setSuspended(!hasCredit()); + + } + + public synchronized void removeAllCredit() + { + _bytesCredit = 0L; + _messageCredit = 0L; + setSuspended(!hasCredit()); + } + + public synchronized boolean hasCredit() + { + return (_bytesCreditLimit == 0L || _bytesCredit > 0) + && (_messageCreditLimit == 0L || _messageCredit > 0); + } + + public synchronized boolean useCreditForMessage(final AMQMessage msg) + { + if(_messageCreditLimit != 0L) + { + if(_messageCredit != 0L) + { + if(_bytesCreditLimit == 0L) + { + _messageCredit--; + + return true; + } + else + { + if((_bytesCredit >= msg.getSize()) || (_bytesCredit == _bytesCreditLimit)) + { + _messageCredit--; + _bytesCredit -= msg.getSize(); + + return true; + } + else + { + //setSuspended(true); + return false; + } + } + } + else + { + setSuspended(true); + return false; + } + } + else + { + if(_bytesCreditLimit == 0L) + { + + return true; + } + else + { + if((_bytesCredit >= msg.getSize()) || (_bytesCredit == _bytesCreditLimit)) + { + _bytesCredit -= msg.getSize(); + + return true; + } + else + { + //setSuspended(true); + return false; + } + } + + } + + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java index 133c97a146..c13a69b793 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java @@ -1,65 +1,40 @@ -/* - * - * 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.handler; - -import org.apache.qpid.framing.*; -import org.apache.qpid.server.state.StateAwareMethodListener; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.AMQException; - -/** - * @author Apache Software Foundation - * - * - */ -public class AccessRequestHandler implements StateAwareMethodListener<AccessRequestBody> -{ - private static final AccessRequestHandler _instance = new AccessRequestHandler(); - - - public static AccessRequestHandler getInstance() - { - return _instance; - } - - private AccessRequestHandler() - { - } - - public void methodReceived(AMQStateManager stateManager, AccessRequestBody body, int channelId) throws AMQException - { - AMQProtocolSession session = stateManager.getProtocolSession(); - - MethodRegistry methodRegistry = session.getMethodRegistry(); - - // We don't implement access control class, but to keep clients happy that expect it - // always use the "0" ticket. - AccessRequestOkBody response = methodRegistry.createAccessRequestOkBody(0); - - session.writeFrame(response.generateFrame(channelId)); - } -} +package org.apache.qpid.server.handler;
+
+import org.apache.qpid.framing.*;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.AMQException;
+
+/**
+ * @author Apache Software Foundation
+ *
+ *
+ */
+public class AccessRequestHandler implements StateAwareMethodListener<AccessRequestBody>
+{
+ private static final AccessRequestHandler _instance = new AccessRequestHandler();
+
+
+ public static AccessRequestHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private AccessRequestHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, AccessRequestBody body, int channelId) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+ MethodRegistry methodRegistry = session.getMethodRegistry();
+
+ // We don't implement access control class, but to keep clients happy that expect it
+ // always use the "0" ticket.
+ AccessRequestOkBody response = methodRegistry.createAccessRequestOkBody(0);
+
+ session.writeFrame(response.generateFrame(channelId));
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java index bda1c16cf6..29054f55c1 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java @@ -21,11 +21,9 @@ package org.apache.qpid.server.handler; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQFrame; import org.apache.qpid.framing.BasicCancelBody; import org.apache.qpid.framing.BasicCancelOkBody; import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.state.AMQStateManager; @@ -65,7 +63,7 @@ public class BasicCancelMethodHandler implements StateAwareMethodListener<BasicC " nowait:" + body.getNowait()); } - channel.unsubscribeConsumer(session, body.getConsumerTag()); + channel.unsubscribeConsumer(body.getConsumerTag()); if (!body.getNowait()) { MethodRegistry methodRegistry = session.getMethodRegistry(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java index 7cd4afdb77..5342a7f518 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java @@ -111,7 +111,7 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener<Basic try { - AMQShortString consumerTag = channel.subscribeToQueue(consumerTagName, queue, session, !body.getNoAck(), + AMQShortString consumerTag = channel.subscribeToQueue(consumerTagName, queue, !body.getNoAck(), body.getArguments(), body.getNoLocal(), body.getExclusive()); if (!body.getNowait()) { @@ -121,8 +121,7 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener<Basic } - //now allow queue to start async processing of any backlog of messages - queue.deliverAsync(); + } catch (org.apache.qpid.AMQInvalidArgumentException ise) { 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 3731116cba..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 @@ -1,101 +1,187 @@ -/* - * 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.handler; - -import org.apache.log4j.Logger; -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.protocol.AMQConstant; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.security.access.Permission; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.server.state.StateAwareMethodListener; -import org.apache.qpid.server.virtualhost.VirtualHost; - -public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetBody> -{ - private static final Logger _log = Logger.getLogger(BasicGetMethodHandler.class); - - private static final BasicGetMethodHandler _instance = new BasicGetMethodHandler(); - - public static BasicGetMethodHandler getInstance() - { - return _instance; - } - - private BasicGetMethodHandler() - { - } - - public void methodReceived(AMQStateManager stateManager, BasicGetBody body, int channelId) throws AMQException - { - AMQProtocolSession session = stateManager.getProtocolSession(); - - - VirtualHost vHost = session.getVirtualHost(); - - AMQChannel channel = session.getChannel(channelId); - if (channel == null) - { - throw body.getChannelNotFoundException(channelId); - } - else - { - AMQQueue queue = body.getQueue() == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.getQueue()); - - if (queue == null) - { - _log.info("No queue for '" + body.getQueue() + "'"); - if(body.getQueue()!=null) - { - throw body.getConnectionException(AMQConstant.NOT_FOUND, - "No such queue, '" + body.getQueue()+ "'"); - } - else - { - throw body.getConnectionException(AMQConstant.NOT_ALLOWED, - "No queue name provided, no default queue defined."); - } - } - else - { - - //Perform ACLs - vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue); - - if (!queue.performGet(session, channel, !body.getNoAck())) - { - MethodRegistry methodRegistry = session.getMethodRegistry(); - // TODO - set clusterId - BasicGetEmptyBody responseBody = methodRegistry.createBasicGetEmptyBody(null); - - - session.writeFrame(responseBody.generateFrame(channelId)); - } - } - } - } -} +/*
+ * 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.handler;
+
+import org.apache.log4j.Logger;
+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.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;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetBody>
+{
+ private static final Logger _log = Logger.getLogger(BasicGetMethodHandler.class);
+
+ private static final BasicGetMethodHandler _instance = new BasicGetMethodHandler();
+
+ public static BasicGetMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicGetMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, BasicGetBody body, int channelId) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+
+ VirtualHost vHost = session.getVirtualHost();
+
+ AMQChannel channel = session.getChannel(channelId);
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(channelId);
+ }
+ else
+ {
+ AMQQueue queue = body.getQueue() == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.getQueue());
+ if (queue == null)
+ {
+ _log.info("No queue for '" + body.getQueue() + "'");
+ if(body.getQueue()!=null)
+ {
+ throw body.getConnectionException(AMQConstant.NOT_FOUND,
+ "No such queue, '" + body.getQueue()+ "'");
+ }
+ else
+ {
+ throw body.getConnectionException(AMQConstant.NOT_ALLOWED,
+ "No queue name provided, no default queue defined.");
+ }
+ }
+ else
+ {
+
+ //Perform ACLs
+ vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue);
+
+ if (!performGet(queue,session, channel, !body.getNoAck()))
+ {
+ MethodRegistry methodRegistry = session.getMethodRegistry();
+ // TODO - set clusterId
+ BasicGetEmptyBody responseBody = methodRegistry.createBasicGetEmptyBody(null);
+
+
+ session.writeFrame(responseBody.generateFrame(channelId));
+ }
+ }
+ }
+ }
+
+ 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/BasicPublishMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java index 0f99a21ee5..e8e42454de 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java @@ -91,7 +91,7 @@ public class BasicPublishMethodHandler implements StateAwareMethodListener<Basic MessagePublishInfo info = session.getMethodRegistry().getProtocolVersionMethodConverter().convertToInfo(body); info.setExchange(exchange); - channel.setPublishFrame(info, session, e); + channel.setPublishFrame(info, e); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java index 3c95180dca..dd3281c65f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java @@ -22,10 +22,8 @@ package org.apache.qpid.server.handler; import org.apache.qpid.AMQException; import org.apache.qpid.framing.BasicQosBody; -import org.apache.qpid.framing.BasicQosOkBody; import org.apache.qpid.framing.MethodRegistry; import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; @@ -49,8 +47,8 @@ public class BasicQosHandler implements StateAwareMethodListener<BasicQosBody> throw body.getChannelNotFoundException(channelId); } - channel.setPrefetchCount(body.getPrefetchCount()); - channel.setPrefetchSize(body.getPrefetchSize()); + channel.setCredit(body.getPrefetchSize(), body.getPrefetchCount()); + MethodRegistry methodRegistry = session.getMethodRegistry(); AMQMethodBody responseBody = methodRegistry.createBasicQosOkBody(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java index bca35be535..15484273c8 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java @@ -1,75 +1,54 @@ -/* - * - * 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.handler; - -import org.apache.log4j.Logger; - -import org.apache.qpid.framing.BasicRecoverBody; -import org.apache.qpid.framing.ProtocolVersion; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.framing.BasicRecoverSyncBody; -import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; -import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0; -import org.apache.qpid.server.state.StateAwareMethodListener; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.AMQException; - -public class BasicRecoverSyncMethodHandler implements StateAwareMethodListener<BasicRecoverSyncBody> -{ - private static final Logger _logger = Logger.getLogger(BasicRecoverSyncMethodHandler.class); - - private static final BasicRecoverSyncMethodHandler _instance = new BasicRecoverSyncMethodHandler(); - - public static BasicRecoverSyncMethodHandler getInstance() - { - return _instance; - } - - public void methodReceived(AMQStateManager stateManager, BasicRecoverSyncBody body, int channelId) throws AMQException - { - AMQProtocolSession session = stateManager.getProtocolSession(); - - _logger.debug("Recover received on protocol session " + session + " and channel " + channelId); - AMQChannel channel = session.getChannel(channelId); - - - if (channel == null) - { - throw body.getChannelNotFoundException(channelId); - } - - channel.resend(body.getRequeue()); - - // Qpid 0-8 hacks a synchronous -ok onto recover. - // In Qpid 0-9 we create a separate sync-recover, sync-recover-ok pair to be "more" compliant - if(session.getProtocolVersion().equals(ProtocolVersion.v0_9)) - { - MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry(); - AMQMethodBody recoverOk = methodRegistry.createBasicRecoverSyncOkBody(); - session.writeFrame(recoverOk.generateFrame(channelId)); - - } - - } -} +package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.framing.BasicRecoverBody;
+import org.apache.qpid.framing.ProtocolVersion;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.framing.BasicRecoverSyncBody;
+import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9;
+import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.AMQException;
+
+public class BasicRecoverSyncMethodHandler implements StateAwareMethodListener<BasicRecoverSyncBody>
+{
+ private static final Logger _logger = Logger.getLogger(BasicRecoverSyncMethodHandler.class);
+
+ private static final BasicRecoverSyncMethodHandler _instance = new BasicRecoverSyncMethodHandler();
+
+ public static BasicRecoverSyncMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, BasicRecoverSyncBody body, int channelId) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+
+ _logger.debug("Recover received on protocol session " + session + " and channel " + channelId);
+ AMQChannel channel = session.getChannel(channelId);
+
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(channelId);
+ }
+
+ channel.resend(body.getRequeue());
+
+ // Qpid 0-8 hacks a synchronous -ok onto recover.
+ // In Qpid 0-9 we create a separate sync-recover, sync-recover-ok pair to be "more" compliant
+ if(session.getProtocolVersion().equals(ProtocolVersion.v0_9))
+ {
+ MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry();
+ AMQMethodBody recoverOk = methodRegistry.createBasicRecoverSyncOkBody();
+ session.writeFrame(recoverOk.generateFrame(channelId));
+
+ }
+
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java index 069cc6ea2c..f3cab10ed7 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java @@ -22,9 +22,8 @@ package org.apache.qpid.server.handler; import org.apache.qpid.AMQException; import org.apache.qpid.framing.BasicRejectBody; -import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.ack.UnacknowledgedMessage; +import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; @@ -49,16 +48,6 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR { AMQProtocolSession session = stateManager.getProtocolSession(); - - -// if (_logger.isDebugEnabled()) -// { -// _logger.debug("Rejecting:" + evt.getMethod().deliveryTag + -// ": Requeue:" + evt.getMethod().requeue + -//// ": Resend:" + evt.getMethod().resend + -// " on channel:" + channelId); -// } - AMQChannel channel = session.getChannel(channelId); if (channel == null) @@ -76,7 +65,7 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR long deliveryTag = body.getDeliveryTag(); - UnacknowledgedMessage message = channel.getUnacknowledgedMessageMap().get(deliveryTag); + QueueEntry message = channel.getUnacknowledgedMessageMap().get(deliveryTag); if (message == null) { @@ -85,11 +74,16 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR } else { - if (message.isQueueDeleted() || message.getQueue().isDeleted()) + if (message.isQueueDeleted()) { _logger.warn("Message's Queue as already been purged, unable to Reject. " + "Dropping message should use Dead Letter Queue"); - //sendtoDeadLetterQueue(msg) + message = channel.getUnacknowledgedMessageMap().remove(deliveryTag); + if(message != null) + { + message.discard(channel.getStoreContext()); + } + //sendtoDeadLetterQueue(msg) return; } @@ -111,7 +105,7 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR // If we haven't requested message to be resent to this consumer then reject it from ever getting it. //if (!evt.getMethod().resend) { - message.entry.reject(); + message.reject(); } if (body.getRequeue()) @@ -121,6 +115,7 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR else { _logger.warn("Dropping message as requeue not required and there is no dead letter queue"); + message = channel.getUnacknowledgedMessageMap().remove(deliveryTag); //sendtoDeadLetterQueue(AMQMessage message) // message.queue = channel.getDefaultDeadLetterQueue(); // channel.requeue(deliveryTag); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java index 491a2f80db..ccd42204d9 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java @@ -22,12 +22,10 @@ package org.apache.qpid.server.handler; import org.apache.qpid.AMQException; import org.apache.qpid.framing.*; -import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0; -import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; import org.apache.qpid.server.virtualhost.VirtualHost; diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java index 97d25f650d..39b048aecb 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java @@ -101,7 +101,7 @@ public class ExchangeDeclareHandler implements StateAwareMethodListener<Exchange else if (!exchange.getType().equals(body.getType())) { - throw new AMQConnectionException(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " + body.getExchange() + " of type " + exchange.getType() + " to " + body.getType() +".",body.getClazz(), body.getMethod(),body.getMajor(),body.getMinor(), null); + throw new AMQConnectionException(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " + body.getExchange() + " of type " + exchange.getType() + " to " + body.getType() +".",body.getClazz(), body.getMethod(),body.getMajor(),body.getMinor(),null); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java index 0f6dc7a19d..46182e8c00 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java @@ -112,7 +112,7 @@ public class QueueBindHandler implements StateAwareMethodListener<QueueBindBody> if (!exch.isBound(routingKey, body.getArguments(), queue)) { - queue.bind(routingKey, body.getArguments(), exch); + queue.bind(exch, routingKey, body.getArguments()); } } catch (AMQInvalidRoutingKeyException rke) 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 7df864f189..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 @@ -34,9 +34,10 @@ import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; import org.apache.qpid.server.store.MessageStore; @@ -123,7 +124,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar { Exchange defaultExchange = exchangeRegistry.getDefaultExchange(); - queue.bind(queueName, null, defaultExchange); + queue.bind(defaultExchange, queueName, null); _logger.info("Queue " + queueName + " bound to default exchange(" + defaultExchange.getName() + ")"); } } @@ -173,7 +174,9 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar { final QueueRegistry registry = virtualHost.getQueueRegistry(); AMQShortString owner = body.getExclusive() ? session.getContextKey() : null; - final AMQQueue queue = new AMQQueue(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/handler/QueueDeleteHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java index 310a73ffeb..dfc36f5b93 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java @@ -24,11 +24,10 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.QueueDeleteBody; import org.apache.qpid.framing.QueueDeleteOkBody; import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; import org.apache.qpid.server.store.MessageStore; @@ -104,15 +103,16 @@ public class QueueDeleteHandler implements StateAwareMethodListener<QueueDeleteB } else { - + //Perform ACLs virtualHost.getAccessManager().authorise(session, Permission.DELETE, body, queue); - int purged = queue.delete(body.getIfUnused(), body.getIfEmpty()); + int purged = queue.delete(); + if (queue.isDurable()) { - store.removeQueue(queue.getName()); + store.removeQueue(queue); } MethodRegistry methodRegistry = session.getMethodRegistry(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java index a854c97f60..7377862875 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java @@ -1,121 +1,119 @@ -/* - * 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.handler; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.QueuePurgeBody; -import org.apache.qpid.framing.QueuePurgeOkBody; -import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.protocol.AMQMethodEvent; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.server.state.StateAwareMethodListener; -import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.security.access.Permission; - -public class QueuePurgeHandler implements StateAwareMethodListener<QueuePurgeBody> -{ - private static final QueuePurgeHandler _instance = new QueuePurgeHandler(); - - public static QueuePurgeHandler getInstance() - { - return _instance; - } - - private final boolean _failIfNotFound; - - public QueuePurgeHandler() - { - this(true); - } - - public QueuePurgeHandler(boolean failIfNotFound) - { - _failIfNotFound = failIfNotFound; - } - - public void methodReceived(AMQStateManager stateManager, QueuePurgeBody body, int channelId) throws AMQException - { - AMQProtocolSession session = stateManager.getProtocolSession(); - VirtualHost virtualHost = session.getVirtualHost(); - QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); - - AMQChannel channel = session.getChannel(channelId); - - - AMQQueue queue; - if(body.getQueue() == null) - { - - if (channel == null) - { - throw body.getChannelNotFoundException(channelId); - } - - //get the default queue on the channel: - queue = channel.getDefaultQueue(); - - if(queue == null) - { - if(_failIfNotFound) - { - throw body.getConnectionException(AMQConstant.NOT_ALLOWED,"No queue specified."); - } - } - } - else - { - queue = queueRegistry.getQueue(body.getQueue()); - } - - if(queue == null) - { - if(_failIfNotFound) - { - throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist."); - } - } - else - { - - //Perform ACLs - virtualHost.getAccessManager().authorise(session, Permission.PURGE, body, queue); - - long purged = queue.clearQueue(channel.getStoreContext()); - - - if(!body.getNowait()) - { - - MethodRegistry methodRegistry = session.getMethodRegistry(); - AMQMethodBody responseBody = methodRegistry.createQueuePurgeOkBody(purged); - session.writeFrame(responseBody.generateFrame(channelId)); - - } - } - } -} +/*
+ * 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.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.QueuePurgeBody;
+import org.apache.qpid.framing.MethodRegistry;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.security.access.Permission;
+
+public class QueuePurgeHandler implements StateAwareMethodListener<QueuePurgeBody>
+{
+ private static final QueuePurgeHandler _instance = new QueuePurgeHandler();
+
+ public static QueuePurgeHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private final boolean _failIfNotFound;
+
+ public QueuePurgeHandler()
+ {
+ this(true);
+ }
+
+ public QueuePurgeHandler(boolean failIfNotFound)
+ {
+ _failIfNotFound = failIfNotFound;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueuePurgeBody body, int channelId) throws AMQException
+ {
+ AMQProtocolSession session = stateManager.getProtocolSession();
+ VirtualHost virtualHost = session.getVirtualHost();
+ QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
+
+ AMQChannel channel = session.getChannel(channelId);
+
+
+ AMQQueue queue;
+ if(body.getQueue() == null)
+ {
+
+ if (channel == null)
+ {
+ throw body.getChannelNotFoundException(channelId);
+ }
+
+ //get the default queue on the channel:
+ queue = channel.getDefaultQueue();
+
+ if(queue == null)
+ {
+ if(_failIfNotFound)
+ {
+ throw body.getConnectionException(AMQConstant.NOT_ALLOWED,"No queue specified.");
+ }
+ }
+ }
+ else
+ {
+ queue = queueRegistry.getQueue(body.getQueue());
+ }
+
+ if(queue == null)
+ {
+ if(_failIfNotFound)
+ {
+ throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist.");
+ }
+ }
+ else
+ {
+
+ //Perform ACLs
+ virtualHost.getAccessManager().authorise(session, Permission.PURGE, body, queue);
+
+ long purged = queue.clearQueue(channel.getStoreContext());
+
+
+ if(!body.getNowait())
+ {
+
+ MethodRegistry methodRegistry = session.getMethodRegistry();
+ AMQMethodBody responseBody = methodRegistry.createQueuePurgeOkBody(purged);
+ session.writeFrame(responseBody.generateFrame(channelId));
+
+ }
+ }
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java index 6b2924031a..d73e33d6c8 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java @@ -1,24 +1,3 @@ -/* - * - * 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.handler; import org.apache.log4j.Logger; @@ -63,7 +42,7 @@ public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindB final AMQQueue queue; - final AMQShortString routingKey; + final AMQShortString routingKey; if (body.getQueue() == null) { @@ -105,7 +84,7 @@ public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindB try { - queue.unBind(routingKey, body.getArguments(), exch); + queue.unBind(exch, routingKey, body.getArguments()); } catch (AMQInvalidRoutingKeyException rke) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java index d24e4f6ffa..9475b83c8f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java @@ -1,566 +1,566 @@ -/* - * - * 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.handler; - -import java.util.Map; -import java.util.HashMap; - -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.framing.*; -import org.apache.qpid.AMQException; - -public class ServerMethodDispatcherImpl implements MethodDispatcher -{ - private final AMQStateManager _stateManager; - - private static interface DispatcherFactory - { - public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager); - } - - private static final Map<ProtocolVersion, DispatcherFactory> _dispatcherFactories = - new HashMap<ProtocolVersion, DispatcherFactory>(); - - - static - { - _dispatcherFactories.put(ProtocolVersion.v8_0, - new DispatcherFactory() - { - public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager) - { - return new ServerMethodDispatcherImpl_8_0(stateManager); - } - }); - - _dispatcherFactories.put(ProtocolVersion.v0_9, - new DispatcherFactory() - { - public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager) - { - return new ServerMethodDispatcherImpl_0_9(stateManager); - } - }); - - } - - - private static final AccessRequestHandler _accessRequestHandler = AccessRequestHandler.getInstance(); - private static final ChannelCloseHandler _channelCloseHandler = ChannelCloseHandler.getInstance(); - private static final ChannelOpenHandler _channelOpenHandler = ChannelOpenHandler.getInstance(); - private static final ChannelCloseOkHandler _channelCloseOkHandler = ChannelCloseOkHandler.getInstance(); - private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance(); - private static final ConnectionCloseOkMethodHandler _connectionCloseOkMethodHandler = ConnectionCloseOkMethodHandler.getInstance(); - private static final ConnectionOpenMethodHandler _connectionOpenMethodHandler = ConnectionOpenMethodHandler.getInstance(); - private static final ConnectionTuneOkMethodHandler _connectionTuneOkMethodHandler = ConnectionTuneOkMethodHandler.getInstance(); - private static final ConnectionSecureOkMethodHandler _connectionSecureOkMethodHandler = ConnectionSecureOkMethodHandler.getInstance(); - private static final ConnectionStartOkMethodHandler _connectionStartOkMethodHandler = ConnectionStartOkMethodHandler.getInstance(); - private static final ExchangeDeclareHandler _exchangeDeclareHandler = ExchangeDeclareHandler.getInstance(); - private static final ExchangeDeleteHandler _exchangeDeleteHandler = ExchangeDeleteHandler.getInstance(); - private static final ExchangeBoundHandler _exchangeBoundHandler = ExchangeBoundHandler.getInstance(); - private static final BasicAckMethodHandler _basicAckMethodHandler = BasicAckMethodHandler.getInstance(); - private static final BasicRecoverMethodHandler _basicRecoverMethodHandler = BasicRecoverMethodHandler.getInstance(); - private static final BasicConsumeMethodHandler _basicConsumeMethodHandler = BasicConsumeMethodHandler.getInstance(); - private static final BasicGetMethodHandler _basicGetMethodHandler = BasicGetMethodHandler.getInstance(); - private static final BasicCancelMethodHandler _basicCancelMethodHandler = BasicCancelMethodHandler.getInstance(); - private static final BasicPublishMethodHandler _basicPublishMethodHandler = BasicPublishMethodHandler.getInstance(); - private static final BasicQosHandler _basicQosHandler = BasicQosHandler.getInstance(); - private static final QueueBindHandler _queueBindHandler = QueueBindHandler.getInstance(); - private static final QueueDeclareHandler _queueDeclareHandler = QueueDeclareHandler.getInstance(); - private static final QueueDeleteHandler _queueDeleteHandler = QueueDeleteHandler.getInstance(); - private static final QueuePurgeHandler _queuePurgeHandler = QueuePurgeHandler.getInstance(); - private static final ChannelFlowHandler _channelFlowHandler = ChannelFlowHandler.getInstance(); - private static final TxSelectHandler _txSelectHandler = TxSelectHandler.getInstance(); - private static final TxCommitHandler _txCommitHandler = TxCommitHandler.getInstance(); - private static final TxRollbackHandler _txRollbackHandler = TxRollbackHandler.getInstance(); - private static final BasicRejectMethodHandler _basicRejectMethodHandler = BasicRejectMethodHandler.getInstance(); - - - - public static MethodDispatcher createMethodDispatcher(AMQStateManager stateManager, ProtocolVersion protocolVersion) - { - return _dispatcherFactories.get(protocolVersion).createMethodDispatcher(stateManager); - } - - - public ServerMethodDispatcherImpl(AMQStateManager stateManager) - { - _stateManager = stateManager; - } - - - protected AMQStateManager getStateManager() - { - return _stateManager; - } - - - - public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException - { - _accessRequestHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException - { - _basicAckMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException - { - _basicCancelMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException - { - _basicConsumeMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException - { - _basicGetMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException - { - _basicPublishMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException - { - _basicQosHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException - { - _basicRecoverMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException - { - _basicRejectMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException - { - _channelOpenHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException - { - _channelCloseHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException - { - _channelCloseOkHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException - { - _channelFlowHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - - public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException - { - _connectionOpenMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException - { - _connectionCloseMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException - { - _connectionCloseOkMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - - public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException - { - _connectionSecureOkMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException - { - _connectionStartOkMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException - { - _connectionTuneOkMethodHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException - { - _exchangeBoundHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException - { - _exchangeDeclareHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException - { - _exchangeDeleteHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException - { - _queueBindHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException - { - _queueDeclareHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException - { - _queueDeleteHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException - { - _queuePurgeHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException - { - _txCommitHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException - { - _txRollbackHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException - { - _txSelectHandler.methodReceived(_stateManager, body, channelId); - return true; - } - - - - -} +/*
+ *
+ * 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.handler;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.AMQException;
+
+public class ServerMethodDispatcherImpl implements MethodDispatcher
+{
+ private final AMQStateManager _stateManager;
+
+ private static interface DispatcherFactory
+ {
+ public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager);
+ }
+
+ private static final Map<ProtocolVersion, DispatcherFactory> _dispatcherFactories =
+ new HashMap<ProtocolVersion, DispatcherFactory>();
+
+
+ static
+ {
+ _dispatcherFactories.put(ProtocolVersion.v8_0,
+ new DispatcherFactory()
+ {
+ public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager)
+ {
+ return new ServerMethodDispatcherImpl_8_0(stateManager);
+ }
+ });
+
+ _dispatcherFactories.put(ProtocolVersion.v0_9,
+ new DispatcherFactory()
+ {
+ public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager)
+ {
+ return new ServerMethodDispatcherImpl_0_9(stateManager);
+ }
+ });
+
+ }
+
+
+ private static final AccessRequestHandler _accessRequestHandler = AccessRequestHandler.getInstance();
+ private static final ChannelCloseHandler _channelCloseHandler = ChannelCloseHandler.getInstance();
+ private static final ChannelOpenHandler _channelOpenHandler = ChannelOpenHandler.getInstance();
+ private static final ChannelCloseOkHandler _channelCloseOkHandler = ChannelCloseOkHandler.getInstance();
+ private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance();
+ private static final ConnectionCloseOkMethodHandler _connectionCloseOkMethodHandler = ConnectionCloseOkMethodHandler.getInstance();
+ private static final ConnectionOpenMethodHandler _connectionOpenMethodHandler = ConnectionOpenMethodHandler.getInstance();
+ private static final ConnectionTuneOkMethodHandler _connectionTuneOkMethodHandler = ConnectionTuneOkMethodHandler.getInstance();
+ private static final ConnectionSecureOkMethodHandler _connectionSecureOkMethodHandler = ConnectionSecureOkMethodHandler.getInstance();
+ private static final ConnectionStartOkMethodHandler _connectionStartOkMethodHandler = ConnectionStartOkMethodHandler.getInstance();
+ private static final ExchangeDeclareHandler _exchangeDeclareHandler = ExchangeDeclareHandler.getInstance();
+ private static final ExchangeDeleteHandler _exchangeDeleteHandler = ExchangeDeleteHandler.getInstance();
+ private static final ExchangeBoundHandler _exchangeBoundHandler = ExchangeBoundHandler.getInstance();
+ private static final BasicAckMethodHandler _basicAckMethodHandler = BasicAckMethodHandler.getInstance();
+ private static final BasicRecoverMethodHandler _basicRecoverMethodHandler = BasicRecoverMethodHandler.getInstance();
+ private static final BasicConsumeMethodHandler _basicConsumeMethodHandler = BasicConsumeMethodHandler.getInstance();
+ private static final BasicGetMethodHandler _basicGetMethodHandler = BasicGetMethodHandler.getInstance();
+ private static final BasicCancelMethodHandler _basicCancelMethodHandler = BasicCancelMethodHandler.getInstance();
+ private static final BasicPublishMethodHandler _basicPublishMethodHandler = BasicPublishMethodHandler.getInstance();
+ private static final BasicQosHandler _basicQosHandler = BasicQosHandler.getInstance();
+ private static final QueueBindHandler _queueBindHandler = QueueBindHandler.getInstance();
+ private static final QueueDeclareHandler _queueDeclareHandler = QueueDeclareHandler.getInstance();
+ private static final QueueDeleteHandler _queueDeleteHandler = QueueDeleteHandler.getInstance();
+ private static final QueuePurgeHandler _queuePurgeHandler = QueuePurgeHandler.getInstance();
+ private static final ChannelFlowHandler _channelFlowHandler = ChannelFlowHandler.getInstance();
+ private static final TxSelectHandler _txSelectHandler = TxSelectHandler.getInstance();
+ private static final TxCommitHandler _txCommitHandler = TxCommitHandler.getInstance();
+ private static final TxRollbackHandler _txRollbackHandler = TxRollbackHandler.getInstance();
+ private static final BasicRejectMethodHandler _basicRejectMethodHandler = BasicRejectMethodHandler.getInstance();
+
+
+
+ public static MethodDispatcher createMethodDispatcher(AMQStateManager stateManager, ProtocolVersion protocolVersion)
+ {
+ return _dispatcherFactories.get(protocolVersion).createMethodDispatcher(stateManager);
+ }
+
+
+ public ServerMethodDispatcherImpl(AMQStateManager stateManager)
+ {
+ _stateManager = stateManager;
+ }
+
+
+ protected AMQStateManager getStateManager()
+ {
+ return _stateManager;
+ }
+
+
+
+ public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException
+ {
+ _accessRequestHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException
+ {
+ _basicAckMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException
+ {
+ _basicCancelMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException
+ {
+ _basicConsumeMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException
+ {
+ _basicGetMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException
+ {
+ _basicPublishMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException
+ {
+ _basicQosHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException
+ {
+ _basicRecoverMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException
+ {
+ _basicRejectMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException
+ {
+ _channelOpenHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+
+ public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException
+ {
+ _channelCloseHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+
+ public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException
+ {
+ _channelCloseOkHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+
+ public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException
+ {
+ _channelFlowHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+
+ public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException
+ {
+ _connectionOpenMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+
+ public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException
+ {
+ _connectionCloseMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+
+ public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException
+ {
+ _connectionCloseOkMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+
+ public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException
+ {
+ _connectionSecureOkMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException
+ {
+ _connectionStartOkMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException
+ {
+ _connectionTuneOkMethodHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException
+ {
+ _exchangeBoundHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException
+ {
+ _exchangeDeclareHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException
+ {
+ _exchangeDeleteHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException
+ {
+ _queueBindHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException
+ {
+ _queueDeclareHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException
+ {
+ _queueDeleteHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException
+ {
+ _queuePurgeHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException
+ {
+ _txCommitHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException
+ {
+ _txRollbackHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+ public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException
+ {
+ _txSelectHandler.methodReceived(_stateManager, body, channelId);
+ return true;
+ }
+
+
+
+
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java index 382a85347b..8b1dca77ba 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java @@ -1,164 +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.handler; - - -import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9; -import org.apache.qpid.framing.*; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.AMQException; - - - -public class ServerMethodDispatcherImpl_0_9 - extends ServerMethodDispatcherImpl - implements MethodDispatcher_0_9 - -{ - - private static final BasicRecoverSyncMethodHandler _basicRecoverSyncMethodHandler = - BasicRecoverSyncMethodHandler.getInstance(); - private static final QueueUnbindHandler _queueUnbindHandler = - QueueUnbindHandler.getInstance(); - - - public ServerMethodDispatcherImpl_0_9(AMQStateManager stateManager) - { - super(stateManager); - } - - public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException - { - _basicRecoverSyncMethodHandler.methodReceived(getStateManager(), body, channelId); - return true; - } - - public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException - { - _queueUnbindHandler.methodReceived(getStateManager(),body,channelId); - return true; - } -} +/*
+ *
+ * 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.handler;
+
+
+import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.AMQException;
+
+
+
+public class ServerMethodDispatcherImpl_0_9
+ extends ServerMethodDispatcherImpl
+ implements MethodDispatcher_0_9
+
+{
+
+ private static final BasicRecoverSyncMethodHandler _basicRecoverSyncMethodHandler =
+ BasicRecoverSyncMethodHandler.getInstance();
+ private static final QueueUnbindHandler _queueUnbindHandler =
+ QueueUnbindHandler.getInstance();
+
+
+ public ServerMethodDispatcherImpl_0_9(AMQStateManager stateManager)
+ {
+ super(stateManager);
+ }
+
+ public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException
+ {
+ _basicRecoverSyncMethodHandler.methodReceived(getStateManager(), body, channelId);
+ return true;
+ }
+
+ public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException
+ {
+ _queueUnbindHandler.methodReceived(getStateManager(),body,channelId);
+ return true;
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java index 22f64cf7d3..d599ca3d4e 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java @@ -1,86 +1,86 @@ -/* - * - * 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.handler; - -import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0; -import org.apache.qpid.framing.*; -import org.apache.qpid.server.state.AMQStateManager; -import org.apache.qpid.AMQException; - -public class ServerMethodDispatcherImpl_8_0 - extends ServerMethodDispatcherImpl - implements MethodDispatcher_8_0 -{ - public ServerMethodDispatcherImpl_8_0(AMQStateManager stateManager) - { - super(stateManager); - } - - public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException - { - throw new UnexpectedMethodException(body); - } - - public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException - { - return false; - } - - public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException - { - return false; - } -} +/*
+ *
+ * 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.handler;
+
+import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.AMQException;
+
+public class ServerMethodDispatcherImpl_8_0
+ extends ServerMethodDispatcherImpl
+ implements MethodDispatcher_8_0
+{
+ public ServerMethodDispatcherImpl_8_0(AMQStateManager stateManager)
+ {
+ super(stateManager);
+ }
+
+ public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException
+ {
+ throw new UnexpectedMethodException(body);
+ }
+
+ public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+
+ public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException
+ {
+ return false;
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java index 79cc722e0e..9b23d88838 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java @@ -23,10 +23,8 @@ package org.apache.qpid.server.handler; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.TxCommitBody; -import org.apache.qpid.framing.TxCommitOkBody; import org.apache.qpid.framing.MethodRegistry; import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.state.AMQStateManager; @@ -70,7 +68,7 @@ public class TxCommitHandler implements StateAwareMethodListener<TxCommitBody> AMQMethodBody responseBody = methodRegistry.createTxCommitOkBody(); session.writeFrame(responseBody.generateFrame(channelId)); - channel.processReturns(session); + channel.processReturns(); } catch (AMQException e) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java b/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java index 0abb3cdd7d..fb18519fe1 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java @@ -1,33 +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.handler; - - -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.AMQException; - -public class UnexpectedMethodException extends AMQException -{ - public UnexpectedMethodException(AMQMethodBody body) - { - super("Unexpected method recevied: " + body.getClass().getName()); - } -} +/*
+ *
+ * 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.handler;
+
+
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.AMQException;
+
+public class UnexpectedMethodException extends AMQException
+{
+ public UnexpectedMethodException(AMQMethodBody body)
+ {
+ super("Unexpected method recevied: " + body.getClass().getName());
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java b/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java index 576d577b40..e01c5aabbf 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java @@ -1,57 +1,57 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -/* - * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. - * Supported AMQP versions: - * 8-0 - */ -package org.apache.qpid.server.output; - -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.AMQDataBlock; -import org.apache.qpid.AMQException; - -public interface ProtocolOutputConverter -{ - void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag); - - interface Factory - { - ProtocolOutputConverter newInstance(AMQProtocolSession session); - } - - void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) - throws AMQException; - - void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException; - - byte getProtocolMinorVersion(); - - byte getProtocolMajorVersion(); - - void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) - throws AMQException; - - void writeFrame(AMQDataBlock block); -} +/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by Qpid Gentools v.0.1 - do not modify.
+ * Supported AMQP versions:
+ * 8-0
+ */
+package org.apache.qpid.server.output;
+
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.AMQException;
+
+public interface ProtocolOutputConverter
+{
+ void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag);
+
+ interface Factory
+ {
+ ProtocolOutputConverter newInstance(AMQProtocolSession session);
+ }
+
+ void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
+ throws AMQException;
+
+ void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException;
+
+ byte getProtocolMinorVersion();
+
+ byte getProtocolMajorVersion();
+
+ void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText)
+ throws AMQException;
+
+ void writeFrame(AMQDataBlock block);
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java index 02fb1429c0..36e7e88fd6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java @@ -1,61 +1,61 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -/* - * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. - * Supported AMQP versions: - * 8-0 - */ -package org.apache.qpid.server.output; - -import org.apache.qpid.server.output.ProtocolOutputConverter.Factory; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.framing.ProtocolVersion; - -import java.util.Map; -import java.util.HashMap; - -public class ProtocolOutputConverterRegistry -{ - - private static final Map<ProtocolVersion, Factory> _registry = - new HashMap<ProtocolVersion, Factory>(); - - - static - { - register(ProtocolVersion.v8_0, org.apache.qpid.server.output.amqp0_8.ProtocolOutputConverterImpl.getInstanceFactory()); - register(ProtocolVersion.v0_9, org.apache.qpid.server.output.amqp0_9.ProtocolOutputConverterImpl.getInstanceFactory()); - - } - - private static void register(ProtocolVersion version, Factory converter) - { - - _registry.put(version,converter); - } - - - public static ProtocolOutputConverter getConverter(AMQProtocolSession session) - { - return _registry.get(session.getProtocolVersion()).newInstance(session); - } -} +/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by Qpid Gentools v.0.1 - do not modify.
+ * Supported AMQP versions:
+ * 8-0
+ */
+package org.apache.qpid.server.output;
+
+import org.apache.qpid.server.output.ProtocolOutputConverter.Factory;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.framing.ProtocolVersion;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public class ProtocolOutputConverterRegistry
+{
+
+ private static final Map<ProtocolVersion, Factory> _registry =
+ new HashMap<ProtocolVersion, Factory>();
+
+
+ static
+ {
+ register(ProtocolVersion.v8_0, org.apache.qpid.server.output.amqp0_8.ProtocolOutputConverterImpl.getInstanceFactory());
+ register(ProtocolVersion.v0_9, org.apache.qpid.server.output.amqp0_9.ProtocolOutputConverterImpl.getInstanceFactory());
+
+ }
+
+ private static void register(ProtocolVersion version, Factory converter)
+ {
+
+ _registry.put(version,converter);
+ }
+
+
+ public static ProtocolOutputConverter getConverter(AMQProtocolSession session)
+ {
+ return _registry.get(session.getProtocolVersion()).newInstance(session);
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java index d4cb7c878f..2b55d294b5 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java @@ -1,285 +1,284 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -/* - * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. - * Supported AMQP versions: - * 8-0 - */ -package org.apache.qpid.server.output.amqp0_8; - -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.AMQMessageHandle; -import org.apache.qpid.server.store.StoreContext; -import org.apache.qpid.server.output.ProtocolOutputConverter; -import org.apache.qpid.framing.*; -import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.AMQException; - -import org.apache.mina.common.ByteBuffer; - -import java.util.Iterator; - -public class ProtocolOutputConverterImpl implements ProtocolOutputConverter -{ - - - public static Factory getInstanceFactory() - { - return new Factory() - { - - public ProtocolOutputConverter newInstance(AMQProtocolSession session) - { - return new ProtocolOutputConverterImpl(session); - } - }; - } - - private final AMQProtocolSession _protocolSession; - - private ProtocolOutputConverterImpl(AMQProtocolSession session) - { - _protocolSession = session; - } - - - public AMQProtocolSession getProtocolSession() - { - return _protocolSession; - } - - public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) - throws AMQException - { - AMQDataBlock deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag); - AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, - message.getContentHeaderBody()); - - final AMQMessageHandle messageHandle = message.getMessageHandle(); - final StoreContext storeContext = message.getStoreContext(); - final Long messageId = message.getMessageId(); - - final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); - - if(bodyCount == 0) - { - SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, - contentHeader); - - writeFrame(compositeBlock); - } - else - { - - - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); - - AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); - AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); - writeFrame(compositeBlock); - - // - // Now start writing out the other content bodies - // - for(int i = 1; i < bodyCount; i++) - { - cb = messageHandle.getContentChunk(storeContext,messageId, i); - writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); - } - - - } - - - } - - - public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException - { - - final AMQMessageHandle messageHandle = message.getMessageHandle(); - final StoreContext storeContext = message.getStoreContext(); - final long messageId = message.getMessageId(); - - AMQDataBlock deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize); - - - AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, - message.getContentHeaderBody()); - - final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); - if(bodyCount == 0) - { - SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, - contentHeader); - writeFrame(compositeBlock); - } - else - { - - - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); - - AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); - AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); - writeFrame(compositeBlock); - - // - // Now start writing out the other content bodies - // - for(int i = 1; i < bodyCount; i++) - { - cb = messageHandle.getContentChunk(storeContext, messageId, i); - writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); - } - - - } - - - } - - - private AMQDataBlock createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) - throws AMQException - { - final MessagePublishInfo pb = message.getMessagePublishInfo(); - final AMQMessageHandle messageHandle = message.getMessageHandle(); - - MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); - BasicDeliverBody deliverBody = - methodRegistry.createBasicDeliverBody(consumerTag, - deliveryTag, - messageHandle.isRedelivered(), - pb.getExchange(), - pb.getRoutingKey()); - AMQFrame deliverFrame = deliverBody.generateFrame(channelId); - - - return deliverFrame; - } - - private AMQDataBlock createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize) - throws AMQException - { - final MessagePublishInfo pb = message.getMessagePublishInfo(); - final AMQMessageHandle messageHandle = message.getMessageHandle(); - - MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); - BasicGetOkBody getOkBody = - methodRegistry.createBasicGetOkBody(deliveryTag, - messageHandle.isRedelivered(), - pb.getExchange(), - pb.getRoutingKey(), - queueSize); - AMQFrame getOkFrame = getOkBody.generateFrame(channelId); - - return getOkFrame; - } - - public byte getProtocolMinorVersion() - { - return getProtocolSession().getProtocolMinorVersion(); - } - - public byte getProtocolMajorVersion() - { - return getProtocolSession().getProtocolMajorVersion(); - } - - private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException - { - MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); - BasicReturnBody basicReturnBody = - methodRegistry.createBasicReturnBody(replyCode, - replyText, - message.getMessagePublishInfo().getExchange(), - message.getMessagePublishInfo().getRoutingKey()); - AMQFrame returnFrame = basicReturnBody.generateFrame(channelId); - - return returnFrame; - } - - public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) - throws AMQException - { - AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText); - - AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, - message.getContentHeaderBody()); - - Iterator<AMQDataBlock> bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId); - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - if (bodyFrameIterator.hasNext()) - { - AMQDataBlock firstContentBody = bodyFrameIterator.next(); - AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody}; - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); - writeFrame(compositeBlock); - } - else - { - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader}); - - writeFrame(compositeBlock); - } - - // - // Now start writing out the other content bodies - // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded - // - while (bodyFrameIterator.hasNext()) - { - writeFrame(bodyFrameIterator.next()); - } - } - - - public void writeFrame(AMQDataBlock block) - { - getProtocolSession().writeFrame(block); - } - - - public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) - { - MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); - BasicCancelOkBody basicCancelOkBody = methodRegistry.createBasicCancelOkBody(consumerTag); - writeFrame(basicCancelOkBody.generateFrame(channelId)); - - } -} +/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * This file is auto-generated by Qpid Gentools v.0.1 - do not modify.
+ * Supported AMQP versions:
+ * 8-0
+ */
+package org.apache.qpid.server.output.amqp0_8;
+
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQMessageHandle;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.server.output.ProtocolOutputConverter;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.AMQException;
+
+import org.apache.mina.common.ByteBuffer;
+
+import java.util.Iterator;
+
+public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
+{
+
+
+ public static Factory getInstanceFactory()
+ {
+ return new Factory()
+ {
+
+ public ProtocolOutputConverter newInstance(AMQProtocolSession session)
+ {
+ return new ProtocolOutputConverterImpl(session);
+ }
+ };
+ }
+
+ private final AMQProtocolSession _protocolSession;
+
+ private ProtocolOutputConverterImpl(AMQProtocolSession session)
+ {
+ _protocolSession = session;
+ }
+
+
+ public AMQProtocolSession getProtocolSession()
+ {
+ return _protocolSession;
+ }
+
+ public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
+ throws AMQException
+ {
+ AMQDataBlock deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag);
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ message.getContentHeaderBody());
+
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+ final StoreContext storeContext = message.getStoreContext();
+
+
+ final int bodyCount = messageHandle.getBodyCount(storeContext);
+
+ if(bodyCount == 0)
+ {
+ SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
+ contentHeader);
+
+ writeFrame(compositeBlock);
+ }
+ else
+ {
+
+
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ ContentChunk cb = messageHandle.getContentChunk(storeContext, 0);
+
+ AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
+ AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
+ writeFrame(compositeBlock);
+
+ //
+ // Now start writing out the other content bodies
+ //
+ for(int i = 1; i < bodyCount; i++)
+ {
+ cb = messageHandle.getContentChunk(storeContext, i);
+ writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
+ }
+
+
+ }
+
+
+ }
+
+
+ public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException
+ {
+
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+ final StoreContext storeContext = message.getStoreContext();
+
+ AMQDataBlock deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize);
+
+
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ message.getContentHeaderBody());
+
+ final int bodyCount = messageHandle.getBodyCount(storeContext);
+ if(bodyCount == 0)
+ {
+ SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
+ contentHeader);
+ writeFrame(compositeBlock);
+ }
+ else
+ {
+
+
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ ContentChunk cb = messageHandle.getContentChunk(storeContext, 0);
+
+ AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
+ AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
+ writeFrame(compositeBlock);
+
+ //
+ // Now start writing out the other content bodies
+ //
+ for(int i = 1; i < bodyCount; i++)
+ {
+ cb = messageHandle.getContentChunk(storeContext, i);
+ writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
+ }
+
+
+ }
+
+
+ }
+
+
+ private AMQDataBlock createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
+ throws AMQException
+ {
+ final MessagePublishInfo pb = message.getMessagePublishInfo();
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+
+ MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0);
+ BasicDeliverBody deliverBody =
+ methodRegistry.createBasicDeliverBody(consumerTag,
+ deliveryTag,
+ messageHandle.isRedelivered(),
+ pb.getExchange(),
+ pb.getRoutingKey());
+ AMQFrame deliverFrame = deliverBody.generateFrame(channelId);
+
+
+ return deliverFrame;
+ }
+
+ private AMQDataBlock createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize)
+ throws AMQException
+ {
+ final MessagePublishInfo pb = message.getMessagePublishInfo();
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+
+ MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0);
+ BasicGetOkBody getOkBody =
+ methodRegistry.createBasicGetOkBody(deliveryTag,
+ messageHandle.isRedelivered(),
+ pb.getExchange(),
+ pb.getRoutingKey(),
+ queueSize);
+ AMQFrame getOkFrame = getOkBody.generateFrame(channelId);
+
+ return getOkFrame;
+ }
+
+ public byte getProtocolMinorVersion()
+ {
+ return getProtocolSession().getProtocolMinorVersion();
+ }
+
+ public byte getProtocolMajorVersion()
+ {
+ return getProtocolSession().getProtocolMajorVersion();
+ }
+
+ private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException
+ {
+ MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0);
+ BasicReturnBody basicReturnBody =
+ methodRegistry.createBasicReturnBody(replyCode,
+ replyText,
+ message.getMessagePublishInfo().getExchange(),
+ message.getMessagePublishInfo().getRoutingKey());
+ AMQFrame returnFrame = basicReturnBody.generateFrame(channelId);
+
+ return returnFrame;
+ }
+
+ public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText)
+ throws AMQException
+ {
+ AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText);
+
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ message.getContentHeaderBody());
+
+ Iterator<AMQDataBlock> bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId);
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ if (bodyFrameIterator.hasNext())
+ {
+ AMQDataBlock firstContentBody = bodyFrameIterator.next();
+ AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
+ writeFrame(compositeBlock);
+ }
+ else
+ {
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader});
+
+ writeFrame(compositeBlock);
+ }
+
+ //
+ // Now start writing out the other content bodies
+ // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded
+ //
+ while (bodyFrameIterator.hasNext())
+ {
+ writeFrame(bodyFrameIterator.next());
+ }
+ }
+
+
+ public void writeFrame(AMQDataBlock block)
+ {
+ getProtocolSession().writeFrame(block);
+ }
+
+
+ public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag)
+ {
+ MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0);
+ BasicCancelOkBody basicCancelOkBody = methodRegistry.createBasicCancelOkBody(consumerTag);
+ writeFrame(basicCancelOkBody.generateFrame(channelId));
+
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java index f87d3bcae1..c76c262edd 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java @@ -1,397 +1,370 @@ -/* - * - * 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.output.amqp0_9; - -import org.apache.mina.common.ByteBuffer; - -import java.util.Iterator; - -import org.apache.qpid.server.output.ProtocolOutputConverter; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.AMQMessageHandle; -import org.apache.qpid.server.store.StoreContext; -import org.apache.qpid.framing.*; -import org.apache.qpid.framing.abstraction.ContentChunk; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; -import org.apache.qpid.AMQException; -import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; - -public class ProtocolOutputConverterImpl implements ProtocolOutputConverter -{ - private static final MethodRegistry METHOD_REGISTRY = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9); - private static final ProtocolVersionMethodConverter PROTOCOL_METHOD_CONVERTER = METHOD_REGISTRY.getProtocolVersionMethodConverter(); - - - public static Factory getInstanceFactory() - { - return new Factory() - { - - public ProtocolOutputConverter newInstance(AMQProtocolSession session) - { - return new ProtocolOutputConverterImpl(session); - } - }; - } - - private final AMQProtocolSession _protocolSession; - - private ProtocolOutputConverterImpl(AMQProtocolSession session) - { - _protocolSession = session; - } - - - public AMQProtocolSession getProtocolSession() - { - return _protocolSession; - } - - public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) - throws AMQException - { - AMQBody deliverBody = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag); - final ContentHeaderBody contentHeaderBody = message.getContentHeaderBody(); - - - final AMQMessageHandle messageHandle = message.getMessageHandle(); - final StoreContext storeContext = message.getStoreContext(); - final Long messageId = message.getMessageId(); - - final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); - - if(bodyCount == 0) - { - SmallCompositeAMQBodyBlock compositeBlock = new SmallCompositeAMQBodyBlock(channelId, deliverBody, - contentHeaderBody); - - writeFrame(compositeBlock); - } - else - { - - - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); - - AMQBody firstContentBody = PROTOCOL_METHOD_CONVERTER.convertToBody(cb); - - CompositeAMQBodyBlock compositeBlock = new CompositeAMQBodyBlock(channelId, deliverBody, contentHeaderBody, firstContentBody); - writeFrame(compositeBlock); - - // - // Now start writing out the other content bodies - // - for(int i = 1; i < bodyCount; i++) - { - cb = messageHandle.getContentChunk(storeContext,messageId, i); - writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb))); - } - - - } - - - } - - private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody) - { - - AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, - contentHeaderBody); - return contentHeader; - } - - - public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException - { - - final AMQMessageHandle messageHandle = message.getMessageHandle(); - final StoreContext storeContext = message.getStoreContext(); - final long messageId = message.getMessageId(); - - AMQFrame deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize); - - - AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody()); - - final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); - if(bodyCount == 0) - { - SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, - contentHeader); - writeFrame(compositeBlock); - } - else - { - - - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0); - - AMQDataBlock firstContentBody = new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)); - AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); - writeFrame(compositeBlock); - - // - // Now start writing out the other content bodies - // - for(int i = 1; i < bodyCount; i++) - { - cb = messageHandle.getContentChunk(storeContext, messageId, i); - writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb))); - } - - - } - - - } - - - private AMQBody createEncodedDeliverFrame(AMQMessage message, final int channelId, final long deliveryTag, final AMQShortString consumerTag) - throws AMQException - { - - - final MessagePublishInfo pb = message.getMessagePublishInfo(); - final AMQMessageHandle messageHandle = message.getMessageHandle(); - - - final AMQBody returnBlock = new AMQBody() - { - - - - private final boolean _isRedelivered = messageHandle.isRedelivered(); - private final AMQShortString _exchangeName = pb.getExchange(); - private final AMQShortString _routingKey = pb.getRoutingKey(); - - - public AMQBody _underlyingBody; - - public AMQBody createAMQBody() - { - return METHOD_REGISTRY.createBasicDeliverBody(consumerTag, - deliveryTag, - _isRedelivered, - _exchangeName, - _routingKey); - - - - - - } - - public byte getFrameType() - { - return AMQMethodBody.TYPE; - } - - public int getSize() - { - if(_underlyingBody == null) - { - _underlyingBody = createAMQBody(); - } - return _underlyingBody.getSize(); - } - - public void writePayload(ByteBuffer buffer) - { - if(_underlyingBody == null) - { - _underlyingBody = createAMQBody(); - } - _underlyingBody.writePayload(buffer); - } - - public void handle(final int channelId, final AMQVersionAwareProtocolSession amqMinaProtocolSession) - throws AMQException - { - throw new AMQException("This block should never be dispatched!"); - } - }; - return returnBlock; - } - - private AMQFrame createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize) - throws AMQException - { - final MessagePublishInfo pb = message.getMessagePublishInfo(); - final AMQMessageHandle messageHandle = message.getMessageHandle(); - - - BasicGetOkBody getOkBody = - METHOD_REGISTRY.createBasicGetOkBody(deliveryTag, - messageHandle.isRedelivered(), - pb.getExchange(), - pb.getRoutingKey(), - queueSize); - AMQFrame getOkFrame = getOkBody.generateFrame(channelId); - - return getOkFrame; - } - - public byte getProtocolMinorVersion() - { - return getProtocolSession().getProtocolMinorVersion(); - } - - public byte getProtocolMajorVersion() - { - return getProtocolSession().getProtocolMajorVersion(); - } - - private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException - { - - BasicReturnBody basicReturnBody = - METHOD_REGISTRY.createBasicReturnBody(replyCode, - replyText, - message.getMessagePublishInfo().getExchange(), - message.getMessagePublishInfo().getRoutingKey()); - AMQFrame returnFrame = basicReturnBody.generateFrame(channelId); - - return returnFrame; - } - - public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) - throws AMQException - { - AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText); - - AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody()); - - Iterator<AMQDataBlock> bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId); - // - // Optimise the case where we have a single content body. In that case we create a composite block - // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. - // - if (bodyFrameIterator.hasNext()) - { - AMQDataBlock firstContentBody = bodyFrameIterator.next(); - AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody}; - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); - writeFrame(compositeBlock); - } - else - { - CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader}); - - writeFrame(compositeBlock); - } - - // - // Now start writing out the other content bodies - // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded - // - while (bodyFrameIterator.hasNext()) - { - writeFrame(bodyFrameIterator.next()); - } - } - - - public void writeFrame(AMQDataBlock block) - { - getProtocolSession().writeFrame(block); - } - - - public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) - { - - BasicCancelOkBody basicCancelOkBody = METHOD_REGISTRY.createBasicCancelOkBody(consumerTag); - writeFrame(basicCancelOkBody.generateFrame(channelId)); - - } - - - public static final class CompositeAMQBodyBlock extends AMQDataBlock - { - public static final int OVERHEAD = 3 * AMQFrame.getFrameOverhead(); - - private final AMQBody _methodBody; - private final AMQBody _headerBody; - private final AMQBody _contentBody; - private final int _channel; - - - public CompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody, AMQBody contentBody) - { - _channel = channel; - _methodBody = methodBody; - _headerBody = headerBody; - _contentBody = contentBody; - - } - - public long getSize() - { - return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() + _contentBody.getSize(); - } - - public void writePayload(ByteBuffer buffer) - { - AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody, _contentBody); - } - } - - public static final class SmallCompositeAMQBodyBlock extends AMQDataBlock - { - public static final int OVERHEAD = 2 * AMQFrame.getFrameOverhead(); - - private final AMQBody _methodBody; - private final AMQBody _headerBody; - private final int _channel; - - - public SmallCompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody) - { - _channel = channel; - _methodBody = methodBody; - _headerBody = headerBody; - - } - - public long getSize() - { - return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() ; - } - - public void writePayload(ByteBuffer buffer) - { - AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody); - } - } - -} +package org.apache.qpid.server.output.amqp0_9;
+
+import org.apache.mina.common.ByteBuffer;
+
+import java.util.Iterator;
+
+import org.apache.qpid.server.output.ProtocolOutputConverter;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQMessageHandle;
+import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+
+public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
+{
+ private static final MethodRegistry METHOD_REGISTRY = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9);
+ private static final ProtocolVersionMethodConverter PROTOCOL_METHOD_CONVERTER = METHOD_REGISTRY.getProtocolVersionMethodConverter();
+
+
+ public static Factory getInstanceFactory()
+ {
+ return new Factory()
+ {
+
+ public ProtocolOutputConverter newInstance(AMQProtocolSession session)
+ {
+ return new ProtocolOutputConverterImpl(session);
+ }
+ };
+ }
+
+ private final AMQProtocolSession _protocolSession;
+
+ private ProtocolOutputConverterImpl(AMQProtocolSession session)
+ {
+ _protocolSession = session;
+ }
+
+
+ public AMQProtocolSession getProtocolSession()
+ {
+ return _protocolSession;
+ }
+
+ public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag)
+ throws AMQException
+ {
+ AMQBody deliverBody = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag);
+ final ContentHeaderBody contentHeaderBody = message.getContentHeaderBody();
+
+
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+ final StoreContext storeContext = message.getStoreContext();
+
+
+ final int bodyCount = messageHandle.getBodyCount(storeContext);
+
+ if(bodyCount == 0)
+ {
+ SmallCompositeAMQBodyBlock compositeBlock = new SmallCompositeAMQBodyBlock(channelId, deliverBody,
+ contentHeaderBody);
+
+ writeFrame(compositeBlock);
+ }
+ else
+ {
+
+
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ ContentChunk cb = messageHandle.getContentChunk(storeContext, 0);
+
+ AMQBody firstContentBody = PROTOCOL_METHOD_CONVERTER.convertToBody(cb);
+
+ CompositeAMQBodyBlock compositeBlock = new CompositeAMQBodyBlock(channelId, deliverBody, contentHeaderBody, firstContentBody);
+ writeFrame(compositeBlock);
+
+ //
+ // Now start writing out the other content bodies
+ //
+ for(int i = 1; i < bodyCount; i++)
+ {
+ cb = messageHandle.getContentChunk(storeContext, i);
+ writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)));
+ }
+
+
+ }
+
+ }
+
+ private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody)
+ {
+
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ contentHeaderBody);
+ return contentHeader;
+ }
+
+
+ public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException
+ {
+
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+ final StoreContext storeContext = message.getStoreContext();
+
+ AMQFrame deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize);
+
+
+ AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody());
+
+ final int bodyCount = messageHandle.getBodyCount(storeContext);
+ if(bodyCount == 0)
+ {
+ SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
+ contentHeader);
+ writeFrame(compositeBlock);
+ }
+ else
+ {
+
+
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ ContentChunk cb = messageHandle.getContentChunk(storeContext, 0);
+
+ AMQDataBlock firstContentBody = new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb));
+ AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
+ writeFrame(compositeBlock);
+
+ //
+ // Now start writing out the other content bodies
+ //
+ for(int i = 1; i < bodyCount; i++)
+ {
+ cb = messageHandle.getContentChunk(storeContext, i);
+ writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)));
+ }
+
+
+ }
+
+
+ }
+
+
+ private AMQBody createEncodedDeliverFrame(AMQMessage message, final int channelId, final long deliveryTag, final AMQShortString consumerTag)
+ throws AMQException
+ {
+ final MessagePublishInfo pb = message.getMessagePublishInfo();
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+
+
+ final boolean isRedelivered = messageHandle.isRedelivered();
+ final AMQShortString exchangeName = pb.getExchange();
+ final AMQShortString routingKey = pb.getRoutingKey();
+
+ final AMQBody returnBlock = new AMQBody()
+ {
+
+ public AMQBody _underlyingBody;
+
+ public AMQBody createAMQBody()
+ {
+ return METHOD_REGISTRY.createBasicDeliverBody(consumerTag,
+ deliveryTag,
+ isRedelivered,
+ exchangeName,
+ routingKey);
+
+
+
+
+
+ }
+
+ public byte getFrameType()
+ {
+ return AMQMethodBody.TYPE;
+ }
+
+ public int getSize()
+ {
+ if(_underlyingBody == null)
+ {
+ _underlyingBody = createAMQBody();
+ }
+ return _underlyingBody.getSize();
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ if(_underlyingBody == null)
+ {
+ _underlyingBody = createAMQBody();
+ }
+ _underlyingBody.writePayload(buffer);
+ }
+
+ public void handle(final int channelId, final AMQVersionAwareProtocolSession amqMinaProtocolSession)
+ throws AMQException
+ {
+ throw new AMQException("This block should never be dispatched!");
+ }
+ };
+ return returnBlock;
+ }
+
+ private AMQFrame createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize)
+ throws AMQException
+ {
+ final MessagePublishInfo pb = message.getMessagePublishInfo();
+ final AMQMessageHandle messageHandle = message.getMessageHandle();
+
+
+ BasicGetOkBody getOkBody =
+ METHOD_REGISTRY.createBasicGetOkBody(deliveryTag,
+ messageHandle.isRedelivered(),
+ pb.getExchange(),
+ pb.getRoutingKey(),
+ queueSize);
+ AMQFrame getOkFrame = getOkBody.generateFrame(channelId);
+
+ return getOkFrame;
+ }
+
+ public byte getProtocolMinorVersion()
+ {
+ return getProtocolSession().getProtocolMinorVersion();
+ }
+
+ public byte getProtocolMajorVersion()
+ {
+ return getProtocolSession().getProtocolMajorVersion();
+ }
+
+ private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException
+ {
+
+ BasicReturnBody basicReturnBody =
+ METHOD_REGISTRY.createBasicReturnBody(replyCode,
+ replyText,
+ message.getMessagePublishInfo().getExchange(),
+ message.getMessagePublishInfo().getRoutingKey());
+ AMQFrame returnFrame = basicReturnBody.generateFrame(channelId);
+
+ return returnFrame;
+ }
+
+ public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText)
+ throws AMQException
+ {
+ AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText);
+
+ AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody());
+
+ Iterator<AMQDataBlock> bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId);
+ //
+ // Optimise the case where we have a single content body. In that case we create a composite block
+ // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
+ //
+ if (bodyFrameIterator.hasNext())
+ {
+ AMQDataBlock firstContentBody = bodyFrameIterator.next();
+ AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody};
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
+ writeFrame(compositeBlock);
+ }
+ else
+ {
+ CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader});
+
+ writeFrame(compositeBlock);
+ }
+
+ //
+ // Now start writing out the other content bodies
+ // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded
+ //
+ while (bodyFrameIterator.hasNext())
+ {
+ writeFrame(bodyFrameIterator.next());
+ }
+ }
+
+
+ public void writeFrame(AMQDataBlock block)
+ {
+ getProtocolSession().writeFrame(block);
+ }
+
+
+ public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag)
+ {
+
+ BasicCancelOkBody basicCancelOkBody = METHOD_REGISTRY.createBasicCancelOkBody(consumerTag);
+ writeFrame(basicCancelOkBody.generateFrame(channelId));
+
+ }
+
+
+ public static final class CompositeAMQBodyBlock extends AMQDataBlock
+ {
+ public static final int OVERHEAD = 3 * AMQFrame.getFrameOverhead();
+
+ private final AMQBody _methodBody;
+ private final AMQBody _headerBody;
+ private final AMQBody _contentBody;
+ private final int _channel;
+
+
+ public CompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody, AMQBody contentBody)
+ {
+ _channel = channel;
+ _methodBody = methodBody;
+ _headerBody = headerBody;
+ _contentBody = contentBody;
+
+ }
+
+ public long getSize()
+ {
+ return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() + _contentBody.getSize();
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody, _contentBody);
+ }
+ }
+
+ public static final class SmallCompositeAMQBodyBlock extends AMQDataBlock
+ {
+ public static final int OVERHEAD = 2 * AMQFrame.getFrameOverhead();
+
+ private final AMQBody _methodBody;
+ private final AMQBody _headerBody;
+ private final int _channel;
+
+
+ public SmallCompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody)
+ {
+ _channel = channel;
+ _methodBody = methodBody;
+ _headerBody = headerBody;
+
+ }
+
+ public long getSize()
+ {
+ return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() ;
+ }
+
+ public void writePayload(ByteBuffer buffer)
+ {
+ AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody);
+ }
+ }
+
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java index 4267642b14..bdb16d0fcb 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java @@ -113,6 +113,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable private ProtocolOutputConverter _protocolOutputConverter; private Principal _authorizedID; private MethodDispatcher _dispatcher; + private ProtocolSessionIdentifier _sessionIdentifier; public ManagedObject getManagedObject() { @@ -198,7 +199,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable } private void frameReceived(AMQFrame frame) throws AMQException - { + { int channelId = frame.getChannel(); AMQBody body = frame.getBodyFrame(); @@ -373,7 +374,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable AMQChannel channel = getAndAssertChannel(channelId); - channel.publishContentHeader(body, this); + channel.publishContentHeader(body); } @@ -381,7 +382,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable { AMQChannel channel = getAndAssertChannel(channelId); - channel.publishContentBody(body, this); + channel.publishContentBody(body); } public void heartbeatBodyReceived(int channelId, HeartbeatBody body) @@ -443,7 +444,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable public boolean channelAwaitingClosure(int channelId) { - return _closingChannelsList.contains(channelId); + return !_closingChannelsList.isEmpty() && _closingChannelsList.contains(channelId); } public void addChannel(AMQChannel channel) throws AMQException @@ -536,7 +537,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable { try { - channel.close(this); + channel.close(); markChannelAwaitingCloseOk(channelId); } finally @@ -602,7 +603,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable { for (AMQChannel channel : _channelMap.values()) { - channel.close(this); + channel.close(); } _channelMap.clear(); @@ -633,7 +634,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable public String toString() { - return "AMQProtocolSession(" + _minaProtocolSession.getRemoteAddress() + ")"; + return _minaProtocolSession.getRemoteAddress() + "("+(getAuthorizedID() == null ? "?" : getAuthorizedID().getName()+")"); } public String dump() @@ -702,6 +703,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable _clientVersion = new AMQShortString(_clientProperties.getString(ClientProperties.version.toString())); } } + _sessionIdentifier = new ProtocolSessionIdentifier(this); } private void setProtocolVersion(ProtocolVersion pv) @@ -739,7 +741,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable public Object getClientIdentifier() { - return _minaProtocolSession.getRemoteAddress(); + return (_minaProtocolSession != null) ? _minaProtocolSession.getRemoteAddress() : null; } public VirtualHost getVirtualHost() @@ -789,6 +791,11 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable return _dispatcher; } + public ProtocolSessionIdentifier getSessionIdentifier() + { + return _sessionIdentifier; + } + public String getClientVersion() { return (_clientVersion == null) ? null : _clientVersion.toString(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java index 92f951ce39..a7599a3e0d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java @@ -1,46 +1,46 @@ -/* - * - * 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.protocol; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.protocol.AMQMethodEvent; - -/** - * AMQNoMethodHandlerException represents the case where no method handler exists to handle an AQMP method. - * - * <p/><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * <tr><td> Represents failure to handle an AMQP method. - * </table> - * - * @todo Not an AMQP exception as no status code. - * - * @todo Missing method handler. Unlikely to ever happen, and if it does its a coding error. Consider replacing with a - * Runtime. - */ -public class AMQNoMethodHandlerException extends AMQException -{ - public AMQNoMethodHandlerException(AMQMethodEvent<AMQMethodBody> evt) - { - super("AMQMethodEvent " + evt + " was not processed by any listener on Broker."); - } -} +/*
+ *
+ * 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.protocol;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.protocol.AMQMethodEvent;
+
+/**
+ * AMQNoMethodHandlerException represents the case where no method handler exists to handle an AQMP method.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to handle an AMQP method.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Missing method handler. Unlikely to ever happen, and if it does its a coding error. Consider replacing with a
+ * Runtime.
+ */
+public class AMQNoMethodHandlerException extends AMQException
+{
+ public AMQNoMethodHandlerException(AMQMethodEvent<AMQMethodBody> evt)
+ {
+ super("AMQMethodEvent " + evt + " was not processed by any listener on Broker.");
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java index ad1c507c04..d8dbf97e49 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java @@ -265,6 +265,10 @@ public class AMQPFastProtocolHandler extends IoHandlerAdapter */ public void messageSent(IoSession protocolSession, Object object) throws Exception { + if (_logger.isDebugEnabled()) + { + _logger.debug("Message sent: " + object); + } } protected boolean isSSLClient(ConnectorConfiguration connectionConfig, diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java index c9316f7405..c3400029da 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.protocol; import javax.security.sasl.SaslServer; import org.apache.qpid.AMQException; +import org.apache.qpid.common.ClientProperties; import org.apache.qpid.framing.*; import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; import org.apache.qpid.server.AMQChannel; @@ -35,7 +36,27 @@ import java.security.Principal; public interface AMQProtocolSession extends AMQVersionAwareProtocolSession { - + public static final class ProtocolSessionIdentifier + { + private final Object _sessionIdentifier; + private final Object _sessionInstance; + + ProtocolSessionIdentifier(AMQProtocolSession session) + { + _sessionIdentifier = session.getClientIdentifier(); + _sessionInstance = session.getClientProperties() == null ? null : session.getClientProperties().getObject(ClientProperties.instance.toAMQShortString()); + } + + public Object getSessionIdentifier() + { + return _sessionIdentifier; + } + + public Object getSessionInstance() + { + return _sessionInstance; + } + } public static interface Task { @@ -175,5 +196,7 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession public MethodRegistry getMethodRegistry(); public MethodDispatcher getMethodDispatcher(); + + public ProtocolSessionIdentifier getSessionIdentifier(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java index bb2db8d506..6e72aa062f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java @@ -1,46 +1,46 @@ -/* - * - * 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.protocol; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQDataBlock; - -/** - * UnknnownMessageTypeException represents a failure when Mina passes an unexpected frame type. - * - * <p/><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * <tr><td> Represents failure to cast a frame to its expected type. - * </table> - * - * @todo Not an AMQP exception as no status code. - * - * @todo Seems like this exception was created to handle an unsafe type cast that will never happen in practice. Would - * be better just to leave that as a ClassCastException. However, check the framing layer catches this error - * first. - */ -public class UnknnownMessageTypeException extends AMQException -{ - public UnknnownMessageTypeException(AMQDataBlock message) - { - super("Unknown message type: " + message.getClass().getName() + ": " + message); - } -} +/*
+ *
+ * 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.protocol;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQDataBlock;
+
+/**
+ * UnknnownMessageTypeException represents a failure when Mina passes an unexpected frame type.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represents failure to cast a frame to its expected type.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Seems like this exception was created to handle an unsafe type cast that will never happen in practice. Would
+ * be better just to leave that as a ClassCastException. However, check the framing layer catches this error
+ * first.
+ */
+public class UnknnownMessageTypeException extends AMQException
+{
+ public UnknnownMessageTypeException(AMQDataBlock message)
+ {
+ super("Unknown message type: " + message.getClass().getName() + ": " + message);
+ }
+}
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 a76b13ce74..0e5e7aa68c 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 @@ -25,131 +25,55 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQBody; import org.apache.qpid.framing.AMQDataBlock; import org.apache.qpid.framing.AMQFrame; -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; import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.StoreContext; import org.apache.qpid.server.txn.TransactionalContext; -import org.apache.qpid.server.exchange.Exchange; -import java.util.HashMap; -import java.util.HashSet; + import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; 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); - /** Used in clustering. @todo What for? */ - private Set<Object> _tokens; - - /** Only use in clustering. @todo What for? */ - private AMQProtocolSession _publisher; - - private final Long _messageId; - private final AtomicInteger _referenceCount = new AtomicInteger(1); - private AMQMessageHandle _messageHandle; + private final AMQMessageHandle _messageHandle; /** Holds the transactional context in which this message is being processed. */ - private TransactionalContext _txnContext; + private StoreContext _storeContext; + + /** Flag to indicate that this message requires 'immediate' delivery. */ + + private static final byte IMMEDIATE = 0x01; /** * Flag to indicate whether this message has been delivered to a consumer. Used in implementing return functionality * for messages published with the 'immediate' flag. */ - private boolean _deliveredToConsumer; - /** Flag to indicate that this message requires 'immediate' delivery. */ - private boolean _immediate; + private static final byte DELIVERED_TO_CONSUMER = 0x02; - private TransientMessageData _transientMessageData = new TransientMessageData(); + private byte _flags = 0; private long _expiration; + private final long _size; + private AMQProtocolSession.ProtocolSessionIdentifier _sessionIdentifier; + private static final byte IMMEDIATE_AND_DELIVERED = (byte) (IMMEDIATE | DELIVERED_TO_CONSUMER); - private Exchange _exchange; - private static final boolean SYNCED_CLOCKS = - ApplicationRegistry.getInstance().getConfiguration().getBoolean("advanced.synced-clocks", false); - - private static final long UNKNOWN_SIZE = Long.MIN_VALUE; - - private long _size = UNKNOWN_SIZE; - - - - public String debugIdentity() - { - return "(HC:" + System.identityHashCode(this) + " ID:" + _messageId + " Ref:" + _referenceCount.get() + ")"; - } - - public void setExpiration() - { - long expiration = - ((BasicContentHeaderProperties) _transientMessageData.getContentHeaderBody().properties).getExpiration(); - long timestamp = - ((BasicContentHeaderProperties) _transientMessageData.getContentHeaderBody().properties).getTimestamp(); - - if (SYNCED_CLOCKS) - { - _expiration = expiration; - } - else - { - // Update TTL to be in broker time. - if (expiration != 0L) - { - if (timestamp != 0L) - { - // todo perhaps use arrival time - long diff = (System.currentTimeMillis() - timestamp); - - if ((diff > 1000L) || (diff < 1000L)) - { - _expiration = expiration + diff; - } - } - } - } - - } - - public boolean isReferenced() - { - return _referenceCount.get() > 0; - } - - public void setExchange(final Exchange exchange) - { - _exchange = exchange; - } - - public void route() throws AMQException - { - _exchange.route(this); - } - - public void enqueue(final List<AMQQueue> queues) - { - _transientMessageData.setDestinationQueues(queues); - } /** * Used to iterate through all the body frames associated with this message. Will not keep all the data in memory @@ -172,7 +96,7 @@ public class AMQMessage { try { - return _index < (_messageHandle.getBodyCount(getStoreContext(), _messageId) - 1); + return _index < (_messageHandle.getBodyCount(getStoreContext()) - 1); } catch (AMQException e) { @@ -189,7 +113,7 @@ public class AMQMessage AMQBody cb = getProtocolVersionMethodConverter().convertToBody(_messageHandle.getContentChunk(getStoreContext(), - _messageId, ++_index)); + ++_index)); return new AMQFrame(_channel, cb); } @@ -212,9 +136,14 @@ public class AMQMessage } } + public void clearStoreContext() + { + _storeContext = new StoreContext(); + } + public StoreContext getStoreContext() { - return _txnContext.getStoreContext(); + return _storeContext; } private class BodyContentIterator implements Iterator<ContentChunk> @@ -226,7 +155,7 @@ public class AMQMessage { try { - return _index < (_messageHandle.getBodyCount(getStoreContext(), _messageId) - 1); + return _index < (_messageHandle.getBodyCount(getStoreContext()) - 1); } catch (AMQException e) { @@ -240,7 +169,7 @@ public class AMQMessage { try { - return _messageHandle.getContentChunk(getStoreContext(), _messageId, ++_index); + return _messageHandle.getContentChunk(getStoreContext(), ++_index); } catch (AMQException e) { @@ -254,13 +183,7 @@ public class AMQMessage } } - public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext) - { - _messageId = messageId; - _txnContext = txnContext; - _immediate = info.isImmediate(); - _transientMessageData.setMessagePublishInfo(info); - } + /** * Used when recovering, i.e. when the message store is creating references to messages. In that case, the normal @@ -276,141 +199,85 @@ public class AMQMessage public AMQMessage(Long messageId, MessageStore store, MessageHandleFactory factory, TransactionalContext txnConext) throws AMQException { - _messageId = messageId; _messageHandle = factory.createMessageHandle(messageId, store, true); - _txnContext = txnConext; - _transientMessageData = null; - + _storeContext = txnConext.getStoreContext(); + _size = _messageHandle.getBodySize(txnConext.getStoreContext()); } - /** - * Used in testing only. This allows the passing of the content header immediately on construction. + /** + * Used when recovering, i.e. when the message store is creating references to messages. In that case, the normal + * enqueue/routingComplete is not done since the recovery process is responsible for routing the messages to + * queues. * - * @param messageId - * @param info - * @param txnContext - * @param contentHeader + * @param messageHandle + * + * @throws AMQException */ - public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext, - ContentHeaderBody contentHeader) throws AMQException + public AMQMessage( + AMQMessageHandle messageHandle, + StoreContext storeConext, + MessagePublishInfo info) + throws AMQException { - this(messageId, info, txnContext); - setContentHeaderBody(contentHeader); - } + _messageHandle = messageHandle; + _storeContext = storeConext; - /* * - * Used in testing only. This allows the passing of the content header and some body fragments on construction. - * - * @param messageId - * @param info - * @param txnContext - * @param contentHeader - * @param destinationQueues - * @param contentBodies - * - * @throws AMQException - */ /* - public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext, - ContentHeaderBody contentHeader, List<AMQQueue> destinationQueues, List<ContentChunk> contentBodies, - MessageStore messageStore, StoreContext storeContext, MessageHandleFactory messageHandleFactory) throws AMQException - { - this(messageId, info, txnContext, contentHeader); - _transientMessageData.setDestinationQueues(destinationQueues); - routingComplete(messageStore, storeContext, messageHandleFactory); - for (ContentChunk cb : contentBodies) + if(info.isImmediate()) { - addContentBodyFrame(storeContext, cb); + _flags |= IMMEDIATE; } + _size = messageHandle.getBodySize(storeConext); + } - */ + + protected AMQMessage(AMQMessage msg) throws AMQException { - _messageId = msg._messageId; _messageHandle = msg._messageHandle; - _txnContext = msg._txnContext; - _deliveredToConsumer = msg._deliveredToConsumer; - _transientMessageData = msg._transientMessageData; - } + _storeContext = msg._storeContext; + _flags = msg._flags; + _size = msg._size; - public Iterator<AMQDataBlock> getBodyFrameIterator(AMQProtocolSession protocolSession, int channel) - { - return new BodyFrameIterator(protocolSession, channel); } - public Iterator<ContentChunk> getContentBodyIterator() + + public String debugIdentity() { - return new BodyContentIterator(); + return "(HC:" + System.identityHashCode(this) + " ID:" + getMessageId() + " Ref:" + _referenceCount.get() + ")"; } - public ContentHeaderBody getContentHeaderBody() throws AMQException + public void setExpiration(final long expiration) { - if (_transientMessageData != null) - { - return _transientMessageData.getContentHeaderBody(); - } - else - { - return _messageHandle.getContentHeaderBody(getStoreContext(), _messageId); - } + + _expiration = expiration; + } - public void setContentHeaderBody(ContentHeaderBody contentHeaderBody) throws AMQException + public boolean isReferenced() { - _transientMessageData.setContentHeaderBody(contentHeaderBody); - _size = _transientMessageData.getContentHeaderBody().bodySize; + return _referenceCount.get() > 0; } - public void routingComplete(MessageStore store, StoreContext storeContext, MessageHandleFactory factory) - throws AMQException + public Iterator<AMQDataBlock> getBodyFrameIterator(AMQProtocolSession protocolSession, int channel) { - final boolean persistent = isPersistent(); - _messageHandle = factory.createMessageHandle(_messageId, store, persistent); - if (persistent) - { - _txnContext.beginTranIfNecessary(); - } - - // enqueuing the messages ensure that if required the destinations are recorded to a - // persistent store - - for (AMQQueue q : _transientMessageData.getDestinationQueues()) - { - _messageHandle.enqueue(storeContext, _messageId, q); - } - - if (_transientMessageData.getContentHeaderBody().bodySize == 0) - { - deliver(storeContext); - } - - + return new BodyFrameIterator(protocolSession, channel); } - public boolean addContentBodyFrame(StoreContext storeContext, ContentChunk contentChunk) throws AMQException + public Iterator<ContentChunk> getContentBodyIterator() { - _transientMessageData.addBodyLength(contentChunk.getSize()); - final boolean allContentReceived = isAllContentReceived(); - _messageHandle.addContentBodyFrame(storeContext, _messageId, contentChunk, allContentReceived); - if (allContentReceived) - { - deliver(storeContext); - - return true; - } - else - { - return false; - } + return new BodyContentIterator(); } - public boolean isAllContentReceived() throws AMQException + public ContentHeaderBody getContentHeaderBody() throws AMQException { - return _transientMessageData.isAllContentReceived(); + return _messageHandle.getContentHeaderBody(getStoreContext()); } + + public Long getMessageId() { - return _messageId; + return _messageHandle.getMessageId(); } /** @@ -425,13 +292,18 @@ public class AMQMessage } /** Threadsafe. Increment the reference count on the message. */ - public void incrementReference() + public boolean incrementReference() { - _referenceCount.incrementAndGet(); - // if (_log.isDebugEnabled()) - // { - // _log.debug("Ref count on message " + debugIdentity() + " incremented " + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6)); - // } + if(_referenceCount.incrementAndGet() <= 1) + { + _referenceCount.decrementAndGet(); + return false; + } + else + { + return true; + } + } /** @@ -445,6 +317,7 @@ public class AMQMessage */ public void decrementReference(StoreContext storeContext) throws MessageCleanupException { + int count = _referenceCount.decrementAndGet(); // note that the operation of decrementing the reference count and then removing the message does not @@ -453,25 +326,25 @@ public class AMQMessage // not relying on the all the increments having taken place before the delivery manager decrements. if (count == 0) { + // set the reference count way below 0 so that we can detect that the message has been deleted + // this is to guard against the message being spontaneously recreated (from the mgmt console) + // by copying from other queues at the same time as it is being removed. + _referenceCount.set(Integer.MIN_VALUE/2); + try { - // if (_log.isDebugEnabled()) - // { - // _log.debug("Decremented ref count on message " + debugIdentity() + " is zero; removing message" + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6)); - // } - // must check if the handle is null since there may be cases where we decide to throw away a message // and the handle has not yet been constructed if (_messageHandle != null) { - _messageHandle.removeMessage(storeContext, _messageId); + _messageHandle.removeMessage(storeContext); } } catch (AMQException e) { // to maintain consistency, we revert the count incrementReference(); - throw new MessageCleanupException(_messageId, e); + throw new MessageCleanupException(getMessageId(), e); } } else @@ -484,15 +357,6 @@ public class AMQMessage } } - public void setPublisher(AMQProtocolSession publisher) - { - _publisher = publisher; - } - - public AMQProtocolSession getPublisher() - { - return _publisher; - } /** * Called selectors to determin if the message has already been sent @@ -501,101 +365,30 @@ public class AMQMessage */ public boolean getDeliveredToConsumer() { - return _deliveredToConsumer; - } - - - public boolean checkToken(Object token) - { - - if (_tokens == null) - { - _tokens = new HashSet<Object>(); - } - - if (_tokens.contains(token)) - { - return true; - } - else - { - _tokens.add(token); - - return false; - } - } - - /** - * Registers a queue to which this message is to be delivered. This is called from the exchange when it is routing - * the message. This will be called before any content bodies have been received so that the choice of - * AMQMessageHandle implementation can be picked based on various criteria. - * - * @param queue the queue - * - * @throws org.apache.qpid.AMQException if there is an error enqueuing the message - */ - public void enqueue(AMQQueue queue) throws AMQException - { - _transientMessageData.addDestinationQueue(queue); - } - - /** - * NOTE: Think about why you are using this method. Normal usages would want to do - * AMQQueue.dequeue(StoreContext, AMQMessage) - * This will keep the queue statistics up-to-date. - * Currently this method is only called _correctly_ from AMQQueue dequeue. - * Ideally we would have a better way for the queue to dequeue the message. - * Especially since enqueue isn't the recipriocal of this method. - * @deprecated - * @param storeContext - * @param queue - * @throws AMQException - */ - void dequeue(StoreContext storeContext, AMQQueue queue) throws AMQException - { - _messageHandle.dequeue(storeContext, _messageId, queue); + return (_flags & DELIVERED_TO_CONSUMER) != 0; } public boolean isPersistent() throws AMQException { - if (_transientMessageData != null) - { - return _transientMessageData.isPersistent(); - } - else - { - return _messageHandle.isPersistent(getStoreContext(), _messageId); - } + return _messageHandle.isPersistent(); } /** * Called to enforce the 'immediate' flag. * - * @throws NoConsumersException if the message is marked for immediate delivery but has not been marked as delivered + * @returns true if the message is marked for immediate delivery but has not been marked as delivered * to a consumer */ - public void checkDeliveredToConsumer() throws NoConsumersException + public boolean immediateAndNotDelivered() { - if (_immediate && !_deliveredToConsumer) - { - throw new NoConsumersException(this); - } + return (_flags & IMMEDIATE_AND_DELIVERED) == IMMEDIATE; + } public MessagePublishInfo getMessagePublishInfo() throws AMQException { - MessagePublishInfo pb; - if (_transientMessageData != null) - { - pb = _transientMessageData.getMessagePublishInfo(); - } - else - { - pb = _messageHandle.getMessagePublishInfo(getStoreContext(), _messageId); - } - - return pb; + return _messageHandle.getMessagePublishInfo(getStoreContext()); } public boolean isRedelivered() @@ -641,46 +434,9 @@ public class AMQMessage */ public void setDeliveredToConsumer() { - _deliveredToConsumer = true; + _flags |= DELIVERED_TO_CONSUMER; } - private void deliver(StoreContext storeContext) throws AMQException - { - // 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 = _transientMessageData.getDestinationQueues(); - if (_log.isDebugEnabled()) - { - _log.debug("Delivering message " + debugIdentity() + " to " + destinationQueues); - } - - try - { - // first we allow the handle to know that the message has been fully received. This is useful if it is - // maintaining any calculated values based on content chunks - _messageHandle.setPublishAndContentHeaderBody(storeContext, _messageId, - _transientMessageData.getMessagePublishInfo(), _transientMessageData.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()); - - for (AMQQueue q : destinationQueues) - { - // Increment the references to this message for each queue delivery. - incrementReference(); - // normal deliver so add this message at the end. - _txnContext.deliver(q.createEntry(this), false); - } - } - finally - { - - // Remove refence for routing process . Reference count should now == delivered queue count - decrementReference(storeContext); - _transientMessageData = null; - } - } public AMQMessageHandle getMessageHandle() @@ -690,28 +446,23 @@ public class AMQMessage public long getSize() { - if(_size == UNKNOWN_SIZE) - { - try - { - _size = getContentHeaderBody().bodySize; - } - catch (AMQException e) - { - _log.warn("Unable to retrieve message meta data for message:" + this, e); - return 0; - } - } return _size; + + } + + public Object getPublisherClientInstance() + { + return _sessionIdentifier.getSessionInstance(); + } + + public Object getPublisherIdentifier() + { + return _sessionIdentifier.getSessionIdentifier(); } - public void restoreTransientMessageData() throws AMQException + public void setClientIdentifier(final AMQProtocolSession.ProtocolSessionIdentifier sessionIdentifier) { - TransientMessageData transientMessageData = new TransientMessageData(); - transientMessageData.setMessagePublishInfo(getMessagePublishInfo()); - transientMessageData.setContentHeaderBody(getContentHeaderBody()); - transientMessageData.addBodyLength(getContentHeaderBody().getSize()); - _transientMessageData = transientMessageData; + _sessionIdentifier = sessionIdentifier; } @@ -720,7 +471,7 @@ public class AMQMessage // return "Message[" + debugIdentity() + "]: " + _messageId + "; ref count: " + _referenceCount + "; taken : " + // _taken + " by :" + _takenBySubcription; - return "Message[" + debugIdentity() + "]: " + _messageId + "; ref count: " + _referenceCount; + return "Message[" + debugIdentity() + "]: " + getMessageId() + "; ref count: " + _referenceCount; } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java index ede55b3bbf..0ddd4e4d92 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java @@ -29,23 +29,27 @@ import org.apache.qpid.framing.abstraction.MessagePublishInfo; /** * A pluggable way of getting message data. Implementations can provide intelligent caching for example or * even no caching at all to minimise the broker memory footprint. - * - * The method all take a messageId to avoid having to store it in the instance - the AMQMessage container - * must already keen the messageId so it is pointless storing it twice. */ public interface AMQMessageHandle { - ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException; + ContentHeaderBody getContentHeaderBody(StoreContext context) throws AMQException; + + /** + * + * @return the messageId for the message associated with this handle + */ + Long getMessageId(); + /** * @return the number of body frames associated with this message */ - int getBodyCount(StoreContext context, Long messageId) throws AMQException; + int getBodyCount(StoreContext context) throws AMQException; /** * @return the size of the body */ - long getBodySize(StoreContext context, Long messageId) throws AMQException; + long getBodySize(StoreContext context) throws AMQException; /** * Get a particular content body @@ -53,27 +57,23 @@ public interface AMQMessageHandle * @return a content body * @throws IllegalArgumentException if the index is invalid */ - ContentChunk getContentChunk(StoreContext context, Long messageId, int index) throws IllegalArgumentException, AMQException; + ContentChunk getContentChunk(StoreContext context, int index) throws IllegalArgumentException, AMQException; - void addContentBodyFrame(StoreContext storeContext, Long messageId, ContentChunk contentBody, boolean isLastContentBody) throws AMQException; + void addContentBodyFrame(StoreContext storeContext, ContentChunk contentBody, boolean isLastContentBody) throws AMQException; - MessagePublishInfo getMessagePublishInfo(StoreContext context, Long messageId) throws AMQException; + MessagePublishInfo getMessagePublishInfo(StoreContext context) throws AMQException; boolean isRedelivered(); void setRedelivered(boolean redelivered); - boolean isPersistent(StoreContext context, Long messageId) throws AMQException; + boolean isPersistent(); - void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, MessagePublishInfo messagePublishInfo, + void setPublishAndContentHeaderBody(StoreContext storeContext, MessagePublishInfo messagePublishInfo, ContentHeaderBody contentHeaderBody) throws AMQException; - void removeMessage(StoreContext storeContext, Long messageId) throws AMQException; - - void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException; - - void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException; + void removeMessage(StoreContext storeContext) throws AMQException; long getArrivalTime(); } 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..ba6b392d13 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java @@ -0,0 +1,67 @@ +/* +* +* 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.server.subscription.SubscriptionList; +import org.apache.qpid.server.subscription.Subscription; +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)); + } + + @Override + protected void checkSubscriptionsNotAheadOfDelivery(final QueueEntry entry) + { + // 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(); + } + } + + } + } + + +} 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 7c6db0b4b3..f3e4e7c28b 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 @@ -20,1005 +20,186 @@ */ 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.registry.ApplicationRegistry; -import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.management.Managable; -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.exchange.Exchange; +import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.AMQException; -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 AMQQueue implements Managable, Comparable -{ - - /** - * ExistingExclusiveSubscription signals a failure to create a subscription, because an exclusive subscription - * already exists. - * - * <p/><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * <tr><td> Represent failure to create a subscription, because an exclusive subscription already exists. - * </table> - * - * @todo Not an AMQP exception as no status code. - * - * @todo Move to top level, used outside this class. - */ - public static final class ExistingExclusiveSubscription extends AMQException - { - - public ExistingExclusiveSubscription() - { - super(""); - } - } - - /** - * ExistingSubscriptionPreventsExclusive signals a failure to create an exclusize subscription, as a subscription - * already exists. - * - * <p/><table id="crc"><caption>CRC Card</caption> - * <tr><th> Responsibilities <th> Collaborations - * <tr><td> Represent failure to create an exclusize subscription, as a subscription already exists. - * </table> - * - * @todo Not an AMQP exception as no status code. - * - * @todo Move to top level, used outside this class. - */ - public static final class ExistingSubscriptionPreventsExclusive extends AMQException - { - public ExistingSubscriptionPreventsExclusive() - { - super(""); - } - } - - private static final Logger _logger = Logger.getLogger(AMQQueue.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; - - /** Holds subscribers to the queue. */ - private final SubscriptionSet _subscribers; - - private final SubscriptionFactory _subscriptionFactory; - - 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 DeliveryManager _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); - - - public AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost) - throws AMQException - { - this(name, durable, owner, autoDelete, virtualHost, AsyncDeliveryConfig.getAsyncDeliveryExecutor(), - new SubscriptionSet(), new SubscriptionImpl.Factory()); - } - - protected AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, - VirtualHost virtualHost, SubscriptionSet subscribers) throws AMQException - { - this(name, durable, owner, autoDelete, virtualHost, AsyncDeliveryConfig.getAsyncDeliveryExecutor(), subscribers, - new SubscriptionImpl.Factory()); - } - - protected AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, - VirtualHost virtualHost, Executor asyncDelivery, SubscriptionSet subscribers, - SubscriptionFactory subscriptionFactory) 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 = asyncDelivery; - - _managedObject = createMBean(); - _managedObject.register(); - - _subscribers = subscribers; - _subscriptionFactory = subscriptionFactory; - _deliveryMgr = new ConcurrentSelectorDeliveryManager(_subscribers, 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; - } +import java.util.List; +import java.util.Set; - public boolean isShared() - { - return _owner == null; - } +public interface AMQQueue extends Managable, Comparable<AMQQueue> +{ - public boolean isDurable() - { - return _durable; - } + AMQShortString getName(); - public AMQShortString getOwner() - { - return _owner; - } + boolean isDurable(); - public boolean isAutoDelete() - { - return _autoDelete; - } + boolean isAutoDelete(); - public boolean isDeleted() - { - return _deleted.get(); - } + AMQShortString getOwner(); - /** @return no of messages(undelivered) on the queue. */ - public int getMessageCount() - { - return _deliveryMgr.getQueueMessageCount(); - } + VirtualHost getVirtualHost(); - /** @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); - } + void bind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException; - public long getQueueDepth() - { - return _deliveryMgr.getTotalMessageSize(); - } + void unBind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException; - /** - * @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)); + void registerSubscription(final Subscription subscription, final boolean exclusive) throws AMQException; - MessageStore fromStore = getVirtualHost().getMessageStore(); - MessageStore toStore = toQueue.getVirtualHost().getMessageStore(); + void unregisterSubscription(final Subscription subscription) throws AMQException; - 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. - startMovingMessages(); - toQueue.startMovingMessages(); - - // 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._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); - } - - // 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.stopMovingMessages(); - stopMovingMessages(); - } - } - - /** - * 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. - startMovingMessages(); - toQueue.startMovingMessages(); - - // 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._name, 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.stopMovingMessages(); - stopMovingMessages(); - } - } - - /** - * 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. - startMovingMessages(); - - // 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 - { - stopMovingMessages(); - } - } + int getConsumerCount(); - public void startMovingMessages() - { - _deliveryMgr.startMovingMessages(); - } + int getActiveConsumerCount(); - private void enqueueMovedMessages(StoreContext storeContext, List<QueueEntry> messageList) - { - _deliveryMgr.enqueueMovedMessages(storeContext, messageList); - _totalMessagesReceived.addAndGet(messageList.size()); - } + boolean isUnused(); - public void stopMovingMessages() - { - _deliveryMgr.stopMovingMessages(); - _deliveryMgr.processAsync(_asyncDelivery); - } + boolean isEmpty(); - /** @return MBean object associated with this Queue */ - public ManagedObject getManagedObject() - { - return _managedObject; - } + int getMessageCount(); - public long getMaximumMessageSize() - { - return _maximumMessageSize; - } + int getUndeliveredMessageCount(); - 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 _subscribers.size(); - } + long getQueueDepth(); - public int getActiveConsumerCount() - { - return _subscribers.getWeight(); - } + long getReceivedMessageCount(); - public long getReceivedMessageCount() - { - return _totalMessagesReceived.get(); - } + long getOldestMessageArrivalTime(); - public long getMaximumMessageCount() - { - return _maximumMessageCount; - } + boolean isDeleted(); - public void setMaximumMessageCount(final long maximumMessageCount) - { - _maximumMessageCount = maximumMessageCount; - if(maximumMessageCount == 0L) - { - _notificationChecks.remove(NotificationCheck.MESSAGE_COUNT_ALERT); - } - else - { - _notificationChecks.add(NotificationCheck.MESSAGE_COUNT_ALERT); - } + int delete() throws AMQException; - } + QueueEntry enqueue(StoreContext storeContext, AMQMessage message) throws AMQException; - public long getMaximumQueueDepth() - { - return _maximumQueueDepth; - } + void requeue(StoreContext storeContext, QueueEntry entry) throws AMQException; - // 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); - } + void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException; - } - public long getOldestMessageArrivalTime() - { - return _deliveryMgr.getOldestMessageArrival(); - } + boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException; - /** 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); - } + void addQueueDeleteTask(final Task task); - public void bind(AMQShortString routingKey, FieldTable arguments, Exchange exchange) throws AMQException - { - exchange.registerQueue(routingKey, this, arguments); - if (isDurable() && exchange.isDurable()) - { - _virtualHost.getMessageStore().bindQueue(exchange, routingKey, this, arguments); - } - _bindings.addBinding(routingKey, arguments, exchange); - } + List<QueueEntry> getMessagesOnTheQueue(); - public void unBind(AMQShortString routingKey, FieldTable arguments, Exchange exchange) throws AMQException - { - exchange.deregisterQueue(routingKey, this, arguments); - if (isDurable() && exchange.isDurable()) - { - _virtualHost.getMessageStore().unbindQueue(exchange, routingKey, this, arguments); - } + List<QueueEntry> getMessagesOnTheQueue(long fromMessageId, long toMessageId); - _bindings.remove(routingKey, arguments, exchange); - } + QueueEntry getMessageOnTheQueue(long messageId); - public void registerProtocolSession(AMQProtocolSession ps, int channel, AMQShortString consumerTag, boolean acks, - FieldTable filters, boolean noLocal, 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); - } + void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, + StoreContext storeContext); - if (_logger.isDebugEnabled()) - { - _logger.debug(MessageFormat.format("Registering protocol session {0} with channel {1} and " - + "consumer tag {2} with {3}", ps, channel, consumerTag, this)); - } + void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, StoreContext storeContext); - Subscription subscription = - _subscriptionFactory.createSubscription(channel, ps, consumerTag, acks, filters, noLocal, this); + void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext); - if (subscription.filtersMessages()) - { - if (_deliveryMgr.hasQueuedMessages()) - { - _deliveryMgr.populatePreDeliveryQueue(subscription); - } - } - _subscribers.addSubscriber(subscription); - if(exclusive) - { - _subscribers.setExclusive(true); - } - subscription.start(); - } + long getMaximumMessageSize(); - private boolean isExclusive() - { - return _isExclusive.get(); - } + void setMaximumMessageSize(long value); - private void setExclusive(boolean exclusive) - { - _isExclusive.set(exclusive); - } - private int incrementSubscriberCount() - { - return _subscriberCount.incrementAndGet(); - } + long getMaximumMessageCount(); - private int decrementSubscriberCount() - { - return _subscriberCount.decrementAndGet(); - } + void setMaximumMessageCount(long value); - public void unregisterProtocolSession(AMQProtocolSession ps, int channel, AMQShortString consumerTag) throws AMQException - { - if (_logger.isDebugEnabled()) - { - _logger.debug(MessageFormat.format( - "Unregistering protocol session {0} with channel {1} and consumer tag {2} from {3}", - ps, channel, consumerTag, this)); - } - _subscribers.setExclusive(false); - Subscription removedSubscription; - if ((removedSubscription = _subscribers.removeSubscriber(_subscriptionFactory.createSubscription(channel, ps, - consumerTag))) - == null) - { - throw new AMQException("Protocol session with channel " + channel + " and consumer tag " + consumerTag - + " and protocol session key " + ps.getKey() + " not registered with queue " + this); - } + long getMaximumQueueDepth(); - removedSubscription.close(); - setExclusive(false); - decrementSubscriberCount(); + void setMaximumQueueDepth(long value); - // if we are eligible for auto deletion, unregister from the queue registry - if (_autoDelete && _subscribers.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 - removedSubscription.queueDeleted(this); - } - } - public boolean isUnused() - { - return _subscribers.isEmpty(); - } + long getMaximumMessageAge(); - public boolean isEmpty() - { - return !_deliveryMgr.hasQueuedMessages(); - } + void setMaximumMessageAge(final long maximumMessageAge); - public int delete(boolean checkUnused, boolean checkEmpty) throws AMQException - { - if (checkUnused && !_subscribers.isEmpty()) - { - _logger.info("Will not delete " + this + " as it is in use."); - return 0; - } - else if (checkEmpty && _deliveryMgr.hasQueuedMessages()) - { - _logger.info("Will not delete " + this + " as it is not empty."); + long getMinimumAlertRepeatGap(); - return 0; - } - else - { - delete(); - return _deliveryMgr.getQueueMessageCount(); - } - } + void deleteMessageFromTop(StoreContext storeContext) throws AMQException; - public void delete() throws AMQException - { - if (!_deleted.getAndSet(true)) - { - _subscribers.queueDeleted(this); - _bindings.deregister(); - _virtualHost.getQueueRegistry().unregisterQueue(_name); - _managedObject.unregister(); - for (Task task : _deleteTaskList) - { - task.doTask(this); - } - - _deleteTaskList.clear(); - } - } + long clearQueue(StoreContext storeContext) throws AMQException; - 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.checkDeliveredToConsumer(); - updateReceivedMessageCount(msg); - } - catch (NoConsumersException e) - { - // as this message will be returned, it should be removed - // from the queue: - dequeue(storeContext, msg); - } - }*/ + void removeExpiredIfNoSubscribers() throws AMQException; - // public DeliveryManager getDeliveryManager() - // { - // return _deliveryMgr; - // } + Set<NotificationCheck> getNotificationChecks(); - public void process(StoreContext storeContext, QueueEntry entry, boolean deliverFirst) throws AMQException - { - AMQMessage msg = entry.getMessage(); - _deliveryMgr.deliver(storeContext, _name, entry, deliverFirst); - try - { - msg.checkDeliveredToConsumer(); - updateReceivedMessageCount(entry); - } - catch (NoConsumersException e) - { - // as this message will be returned, it should be removed - // from the queue: - dequeue(storeContext, entry); - } - } + void flushSubscription(final Subscription sub) throws AMQException; - public void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException - { - try - { - entry.getMessage().dequeue(storeContext, this); - } - 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); - } - } + void deliverAsync(final Subscription sub); - public void deliverAsync() - { - _deliveryMgr.processAsync(_asyncDelivery); - } + void deliverAsync(); - protected SubscriptionManager getSubscribers() - { - return _subscribers; - } - protected void updateReceivedMessageCount(QueueEntry entry) throws AMQException + /** + * ExistingExclusiveSubscription signals a failure to create a subscription, because an exclusive subscription + * already exists. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represent failure to create a subscription, because an exclusive subscription already exists. + * </table> + * + * @todo Not an AMQP exception as no status code. + * + * @todo Move to top level, used outside this class. + */ + static final class ExistingExclusiveSubscription extends AMQException { - AMQMessage msg = entry.getMessage(); - - if (!msg.isRedelivered()) - { - _totalMessagesReceived.incrementAndGet(); - } - try - { - _managedObject.checkForNotification(msg); - } - catch (JMException e) + public ExistingExclusiveSubscription() { - throw new AMQException("Unable to get notification from manage queue: " + e, e); + super(""); } } - public boolean equals(Object o) + /** + * ExistingSubscriptionPreventsExclusive signals a failure to create an exclusize subscription, as a subscription + * already exists. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represent failure to create an exclusize subscription, as a subscription already exists. + * </table> + * + * @todo Not an AMQP exception as no status code. + * + * @todo Move to top level, used outside this class. + */ + static final class ExistingSubscriptionPreventsExclusive extends AMQException { - if (this == o) - { - return true; - } - - if ((o == null) || (getClass() != o.getClass())) + public ExistingSubscriptionPreventsExclusive() { - return false; + super(""); } - - final AMQQueue amqQueue = (AMQQueue) o; - - return (_name.equals(amqQueue._name)); - } - - public int hashCode() - { - return _name.hashCode(); } - public String toString() - { - return "Queue(" + _name + ")@" + System.identityHashCode(this); - } - - 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 static interface Task + static interface Task { public void doTask(AMQQueue queue) throws AMQException; } - - public void addQueueDeleteTask(Task task) - { - _deleteTaskList.add(task); - } - - 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, SubscriptionImpl subscription, QueueEntry entry) - { - _deliveryMgr.subscriberHasPendingResend(hasContent, subscription, entry); - } - - public QueueEntry createEntry(AMQMessage amqMessage) - { - return new QueueEntry(this, amqMessage); - } - - public int compareTo(Object o) - { - return _name.compareTo(((AMQQueue) o).getName()); - } - - - public void removeExpiredIfNoSubscribers() throws AMQException - { - synchronized(_subscribers.getChangeLock()) - { - if(_subscribers.isEmpty()) - { - _deliveryMgr.removeExpired(); - } - } - } - - public final Set<NotificationCheck> getNotificationChecks() - { - return _notificationChecks; - } } 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 new file mode 100644 index 0000000000..431b76754f --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java @@ -0,0 +1,52 @@ +/* + * + * 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.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, final FieldTable arguments) + throws AMQException + { + + final int priorities = arguments == null ? 1 : 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/AMQQueueMBean.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java index 348a136f9d..2ed6be77c6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java @@ -292,7 +292,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que } /** - * @see org.apache.qpid.server.queue.AMQQueue#deleteMessageFromTop + * @see AMQQueue#deleteMessageFromTop */ public void deleteMessageFromTop() throws JMException { @@ -307,7 +307,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que } /** - * @see org.apache.qpid.server.queue.AMQQueue#clearQueue + * @see AMQQueue#clearQueue */ public void clearQueue() throws JMException { 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 0e8cff0f2a..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java +++ /dev/null @@ -1,1097 +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.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 implements DeliveryManager -{ - 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 SubscriptionManager _subscriptions; - - /** - * 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 AMQQueue _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<Subscription> _hasContent = Collections.synchronizedSet(new HashSet<Subscription>()); - private final Object _queueHeadLock = new Object(); - private String _processingThreadName = ""; - - ConcurrentSelectorDeliveryManager(SubscriptionManager subscriptions, AMQQueue queue) - { - - //Set values from configuration - Configurator.configure(this); - - if (compressBufferOnQueue) - { - _log.warn("Compressing Buffers on queue."); - } - - _subscriptions = subscriptions; - _queue = queue; - } - - - 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) - { - _lock.lock(); - try - { - if (hasContent) - { - _log.debug("Queue has adding subscriber content"); - _hasContent.add(subscription); - _totalMessageSize.addAndGet(entry.getSize()); - _extraMessages.addAndGet(1); - } - else - { - _log.debug("Queue has removing subscriber content"); - if (entry == null) - { - _hasContent.remove(subscription); - } - 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(); - try - { - // New Context to for dealing with the MessageStore. - StoreContext context = new StoreContext(); - - 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()); - - // Remove the message from the queue in the MessageStore - _queue.dequeue(context,entry); - - // This queue nolonger needs a reference to this message - entry.getMessage().decrementReference(context); - iter.remove(); - } - } - } - finally - { - _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() - { - List<QueueEntry> list = new ArrayList<QueueEntry>(); - - _lock.lock(); - try - { - for (QueueEntry entry : _messages) - { - list.add(entry); - } - } - finally - { - _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; - - List<QueueEntry> foundMessagesList = new ArrayList<QueueEntry>(); - _lock.lock(); - try - { - 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; - } - } - } - finally - { - _lock.unlock(); - } - - return foundMessagesList; - } - - public void populatePreDeliveryQueue(Subscription subscription) - { - if (_log.isDebugEnabled()) - { - _log.debug("Populating PreDeliveryQueue for Subscription(" + System.identityHashCode(subscription) + ")"); - } - - Iterator<QueueEntry> currentQueue = _messages.iterator(); - - while (currentQueue.hasNext()) - { - QueueEntry entry = currentQueue.next(); - - if (subscription.hasInterest(entry)) - { - subscription.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()); - } - _queue.dequeue(channel.getStoreContext(), entry); - } - 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.getMessage().decrementReference(channel.getStoreContext()); - } - } - finally - { - entry.setDeliveredToConsumer(); - } - 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()) - { - if (!sub.isSuspended() && sub.filtersMessages()) - { - Queue<QueueEntry> preDeliveryQueue = sub.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(); - - try - { - QueueEntry entry = _messages.poll(); - - if (entry != null) - { - queue.dequeue(storeContext, entry); - - _totalMessageSize.addAndGet(-entry.getSize()); - - //If this causes ref count to hit zero then data will be purged so message.getSize() will NPE. - entry.getMessage().decrementReference(storeContext); - - } - } - finally - { - _lock.unlock(); - } - } - - public long clearAllMessages(StoreContext storeContext) throws AMQException - { - long count = 0; - - _lock.lock(); - try - { - synchronized (_queueHeadLock) - { - QueueEntry entry = getNextMessage(); - - // todo: note: why do we need this? Why not reuse the passed 'storeContext' - //Create a new StoreContext for decrementing the References - StoreContext context = new StoreContext(); - - while (entry != null) - { - //and remove it - _messages.poll(); - - // todo: NOTE: Why is this a different context to the new local 'context'? - _queue.dequeue(storeContext, entry); - - entry.getMessage().decrementReference(context); - - entry = getNextMessage(); - count++; - } - _totalMessageSize.set(0L); - } - } - finally - { - _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)) - { - AMQMessage message = entry.getMessage(); - - //remove the already taken message or expired - QueueEntry removed = messages.poll(); - - assert removed == entry; - - // if the message expired then the _totalMessageSize needs adjusting - if (message.expired(_queue) && !entry.taken(sub)) - { - _totalMessageSize.addAndGet(-entry.getSize()); - - // New Store Context for removing expired messages - StoreContext storeContext = new StoreContext(); - - // Use the reapingStoreContext as any sub(if we have one) may be in a tx. - _queue.dequeue(storeContext, entry); - - message.decrementReference(storeContext); - - 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:" + message.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 message will be taken by the Subscription(sub) for the current Queue(_queue) - * - * @param message - * @param sub - * - * @return - * - * @throws AMQException - */ - private boolean purgeMessage(QueueEntry message, Subscription sub) throws AMQException - { - return purgeMessage(message, sub, false); - } - - /** - * 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.isTaken())); - } - else - { - // if there is no subscription we are doing - // a get or purging so mark message as taken. - message.isTaken(); - // 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.isTaken(); - } - 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.taken(sub); - } - } - - public void sendNextMessage(Subscription sub, AMQQueue queue) - { - - 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 = null; - try - { - synchronized (_queueHeadLock) - { - entry = getNextMessage(messageQueue, sub, 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, _queue); - - //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()) - { - 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(Subscription sub) - { - try - { - getNextMessage(_messages, sub, 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(); - try - { - 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)) - { - sub.enqueueForPreDelivery(entry, true); - } - } - } - } - finally - { - _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()) - { - synchronized (sub.getSendLock()) - { - if (!sub.isSuspended()) - { - sendNextMessage(sub, _queue); - - 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); - - 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()) - { - - // Only give the message to those that want them. - if (sub.hasInterest(entry)) - { - if (debugEnabled) - { - _log.debug(debugIdentity() + "Queuing message(" + System.identityHashCode(entry) + - ") for PreDelivery for subscriber(" + System.identityHashCode(sub) + ")"); - } - sub.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 (s.getPreDeliveryQueue().size() > 0) - { - _log.error("Direct delivery from PDQ with queued msgs:" + s.getPreDeliveryQueue().size()); - } - } - else if (_messages.size() > 0) - { - _log.error("Direct delivery from MainQueue queued msgs:" + _messages.size()); - } - - //release lock now - _lock.unlock(); - synchronized (s.getSendLock()) - { - if (!s.isSuspended()) - { - if (debugEnabled) - { - _log.debug(debugIdentity() + "Delivering Message:" + entry.getMessage().debugIdentity() + " to(" + - System.identityHashCode(s) + ") :" + s); - } - - if (entry.taken(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 - s.send(entry, _queue); - } - 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.isTaken()) - { - 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(); - - 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); - } - } - } - } - - 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/DeliveryManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java deleted file mode 100644 index 1568f58e2e..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java +++ /dev/null @@ -1,102 +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.concurrent.Executor; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.store.StoreContext; - -interface DeliveryManager -{ - /** - * Determines whether there are queued messages. Sets _queueing to false if there are no queued messages. This needs - * to be atomic. - * - * @return true if there are queued messages - */ - boolean hasQueuedMessages(); - - /** - * This method should not be used to determin if there are messages in the queue. - * - * @return int The number of messages in the queue - * - * @use hasQueuedMessages() for all controls relating to having messages on the queue. - */ - int getQueueMessageCount(); - - /** - * Requests that the delivery manager start processing the queue asynchronously if there is work that can be done - * (i.e. there are messages queued up and subscribers that can receive them. <p/> This should be called when - * subscribers are added, but only after the consume-ok message has been returned as message delivery may start - * immediately. It should also be called after unsuspending a client. <p/> - * - * @param executor the executor on which the delivery should take place - */ - void processAsync(Executor executor); - - /** - * Handles message delivery. The delivery manager is always in one of two modes; it is either queueing messages for - * asynchronous delivery or delivering directly. - * - * @param storeContext - * @param name the name of the entity on whose behalf we are delivering the message - * @param entry the message to deliver - * @param deliverFirst - * - * @throws org.apache.qpid.server.queue.FailedDequeueException - * if the message could not be dequeued - */ - void deliver(StoreContext storeContext, AMQShortString name, QueueEntry entry, boolean deliverFirst) throws FailedDequeueException, AMQException; - - void removeAMessageFromTop(StoreContext storeContext, AMQQueue queue) throws AMQException; - - long clearAllMessages(StoreContext storeContext) throws AMQException; - - void startMovingMessages(); - - void enqueueMovedMessages(StoreContext context, List<QueueEntry> messageList); - - void stopMovingMessages(); - - void removeMovedMessages(List<QueueEntry> messageListToRemove); - - List<QueueEntry> getMessages(); - - List<QueueEntry> getMessages(long fromMessageId, long toMessageId); - - void populatePreDeliveryQueue(Subscription subscription); - - boolean performGet(AMQProtocolSession session, AMQChannel channel, boolean acks) throws AMQException; - - long getTotalMessageSize(); - - long getOldestMessageArrival(); - - void subscriberHasPendingResend(boolean hasContent, Subscription subscription, QueueEntry msg); - - void removeExpired() throws AMQException; -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java b/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java index e6377b33da..d2e5a02508 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java @@ -109,9 +109,9 @@ class ExchangeBindings } - public void remove(AMQShortString routingKey, FieldTable arguments, Exchange exchange) + public boolean remove(AMQShortString routingKey, FieldTable arguments, Exchange exchange) { - _bindings.remove(new ExchangeBinding(routingKey, exchange, arguments)); + return _bindings.remove(new ExchangeBinding(routingKey, exchange, arguments)); } 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/InMemoryMessageHandle.java b/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java index 0b40f01f1a..35ad5be4e0 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java @@ -22,11 +22,10 @@ package org.apache.qpid.server.queue; import java.util.LinkedList; import java.util.List; +import java.util.Collections; import java.util.ArrayList; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.framing.ContentBody; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.framing.abstraction.ContentChunk; @@ -41,32 +40,40 @@ public class InMemoryMessageHandle implements AMQMessageHandle private MessagePublishInfo _messagePublishInfo; - private List<ContentChunk> _contentBodies = new ArrayList<ContentChunk>(); + private List<ContentChunk> _contentBodies; private boolean _redelivered; private long _arrivalTime; - public InMemoryMessageHandle() + private final Long _messageId; + + public InMemoryMessageHandle(final Long messageId) { + _messageId = messageId; } - public ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException + public ContentHeaderBody getContentHeaderBody(StoreContext context) throws AMQException { return _contentHeaderBody; } - public int getBodyCount(StoreContext context, Long messageId) + public Long getMessageId() + { + return _messageId; + } + + public int getBodyCount(StoreContext context) { return _contentBodies.size(); } - public long getBodySize(StoreContext context, Long messageId) throws AMQException + public long getBodySize(StoreContext context) throws AMQException { - return getContentHeaderBody(context, messageId).bodySize; + return getContentHeaderBody(context).bodySize; } - public ContentChunk getContentChunk(StoreContext context, Long messageId, int index) throws AMQException, IllegalArgumentException + public ContentChunk getContentChunk(StoreContext context, int index) throws AMQException, IllegalArgumentException { if (index > _contentBodies.size() - 1) { @@ -76,13 +83,28 @@ public class InMemoryMessageHandle implements AMQMessageHandle return _contentBodies.get(index); } - public void addContentBodyFrame(StoreContext storeContext, Long messageId, ContentChunk contentBody, boolean isLastContentBody) + public void addContentBodyFrame(StoreContext storeContext, ContentChunk contentBody, boolean isLastContentBody) throws AMQException { - _contentBodies.add(contentBody); + if(_contentBodies == null) + { + if(isLastContentBody) + { + _contentBodies = Collections.singletonList(contentBody); + } + else + { + _contentBodies = new ArrayList<ContentChunk>(); + _contentBodies.add(contentBody); + } + } + else + { + _contentBodies.add(contentBody); + } } - public MessagePublishInfo getMessagePublishInfo(StoreContext context, Long messageId) throws AMQException + public MessagePublishInfo getMessagePublishInfo(StoreContext context) throws AMQException { return _messagePublishInfo; } @@ -98,12 +120,9 @@ public class InMemoryMessageHandle implements AMQMessageHandle _redelivered = redelivered; } - public boolean isPersistent(StoreContext context, Long messageId) throws AMQException + public boolean isPersistent() { - //todo remove literal values to a constant file such as AMQConstants in common - ContentHeaderBody chb = getContentHeaderBody(context, messageId); - return chb.properties instanceof BasicContentHeaderProperties && - ((BasicContentHeaderProperties) chb.properties).getDeliveryMode() == 2; + return false; } /** @@ -112,26 +131,20 @@ public class InMemoryMessageHandle implements AMQMessageHandle * @param contentHeaderBody * @throws AMQException */ - public void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, MessagePublishInfo messagePublishInfo, + public void setPublishAndContentHeaderBody(StoreContext storeContext, MessagePublishInfo messagePublishInfo, ContentHeaderBody contentHeaderBody) throws AMQException { _messagePublishInfo = messagePublishInfo; _contentHeaderBody = contentHeaderBody; + if(contentHeaderBody.bodySize == 0) + { + _contentBodies = Collections.EMPTY_LIST; + } _arrivalTime = System.currentTimeMillis(); } - public void removeMessage(StoreContext storeContext, Long messageId) throws AMQException - { - // NO OP - } - - public void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException - { - // NO OP - } - - public void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException + public void removeMessage(StoreContext storeContext) throws AMQException { // NO OP } 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 new file mode 100644 index 0000000000..9d769d7582 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java @@ -0,0 +1,336 @@ +/* + * + * 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.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.exchange.NoRouteException; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.AMQException; +import org.apache.log4j.Logger; + +import java.util.Collection; + +public class IncomingMessage implements Filterable<RuntimeException> +{ + + /** Used for debugging purposes. */ + private static final Logger _logger = Logger.getLogger(IncomingMessage.class); + + private static final boolean SYNCHED_CLOCKS = + ApplicationRegistry.getInstance().getConfiguration().getBoolean("advanced.synced-clocks", false); + + private final MessagePublishInfo _messagePublishInfo; + private ContentHeaderBody _contentHeaderBody; + private AMQMessageHandle _messageHandle; + private final Long _messageId; + private final TransactionalContext _txnContext; + + + + /** + * Keeps a track of how many bytes we have received in body frames + */ + private long _bodyLengthReceived = 0; + + /** + * This is stored during routing, to know the queues to which this message should immediately be + * delivered. It is <b>cleared after delivery has been attempted</b>. Any persistent record of destinations is done + * by the message handle. + */ + private Collection<AMQQueue> _destinationQueues; + + private AMQProtocolSession _publisher; + private MessageStore _messageStore; + private long _expiration; + + private Exchange _exchange; + + + public IncomingMessage(final Long messageId, + final MessagePublishInfo info, + final TransactionalContext txnContext, + final AMQProtocolSession publisher) + { + _messageId = messageId; + _messagePublishInfo = info; + _txnContext = txnContext; + _publisher = publisher; + + } + + public void setContentHeaderBody(final ContentHeaderBody contentHeaderBody) throws AMQException + { + _contentHeaderBody = contentHeaderBody; + } + + public void setExpiration() + { + long expiration = + ((BasicContentHeaderProperties) _contentHeaderBody.properties).getExpiration(); + long timestamp = + ((BasicContentHeaderProperties) _contentHeaderBody.properties).getTimestamp(); + + if (SYNCHED_CLOCKS) + { + _expiration = expiration; + } + else + { + // Update TTL to be in broker time. + if (expiration != 0L) + { + if (timestamp != 0L) + { + // todo perhaps use arrival time + long diff = (System.currentTimeMillis() - timestamp); + + if ((diff > 1000L) || (diff < 1000L)) + { + _expiration = expiration + diff; + } + } + } + } + + } + + public void routingComplete(final MessageStore store, + final MessageHandleFactory factory) throws AMQException + { + + final boolean persistent = isPersistent(); + _messageHandle = factory.createMessageHandle(_messageId, store, persistent); + if (persistent) + { + _txnContext.beginTranIfNecessary(); + // enqueuing the messages ensure that if required the destinations are recorded to a + // persistent store + + if(_destinationQueues != null) + { + for (AMQQueue q : _destinationQueues) + { + if(q.isDurable()) + { + + _messageStore.enqueueMessage(_txnContext.getStoreContext(), q, _messageId); + } + } + } + + } + + + + + } + + public AMQMessage deliverToQueues() + throws AMQException + { + + // we get a reference to the destination queues now so that we can clear the + // transient message data as quickly as possible + Collection<AMQQueue> destinationQueues = _destinationQueues; + if (_logger.isDebugEnabled()) + { + _logger.debug("Delivering message " + _messageId + " to " + destinationQueues); + } + + AMQMessage message = null; + + try + { + // first we allow the handle to know that the message has been fully received. This is useful if it is + // maintaining any calculated values based on content chunks + _messageHandle.setPublishAndContentHeaderBody(_txnContext.getStoreContext(), + _messagePublishInfo, getContentHeaderBody()); + + + message = new AMQMessage(_messageHandle,_txnContext.getStoreContext(), _messagePublishInfo); + + message.setExpiration(_expiration); + message.setClientIdentifier(_publisher.getSessionIdentifier()); + + + + + if ((destinationQueues == null) || destinationQueues.isEmpty()) + { + + if (isMandatory() || isImmediate()) + { + throw new NoRouteException("No Route for message", message); + + } + else + { + _logger.warn("MESSAGE DISCARDED: No routes for message - " + message); + } + } + else + { + // TODO + + int offset; + final int queueCount = destinationQueues.size(); + if(queueCount == 1) + { + offset = 0; + } + else + { + offset = ((int)(message.getMessageId().longValue())) % queueCount; + if(offset < 0) + { + offset = -offset; + } + } + + int i = 0; + for (AMQQueue q : destinationQueues) + { + if(++i > offset) + { + // Increment the references to this message for each queue delivery. + message.incrementReference(); + // normal deliver so add this message at the end. + _txnContext.deliver(q, message); + } + } + i = 0; + if(offset != 0) + { + for (AMQQueue q : destinationQueues) + { + if(i++ < offset) + { + // Increment the references to this message for each queue delivery. + message.incrementReference(); + // normal deliver so add this message at the end. + _txnContext.deliver(q, message); + } + } + } + + } + + // 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.clearStoreContext(); + return message; + } + finally + { + // Remove refence for routing process . Reference count should now == delivered queue count + if(message != null) message.decrementReference(_txnContext.getStoreContext()); + } + + } + + public void addContentBodyFrame(final ContentChunk contentChunk) + throws AMQException + { + + _bodyLengthReceived += contentChunk.getSize(); + + _messageHandle.addContentBodyFrame(_txnContext.getStoreContext(), contentChunk, allContentReceived()); + + } + + public boolean allContentReceived() + { + return (_bodyLengthReceived == getContentHeaderBody().bodySize); + } + + public AMQShortString getExchange() throws AMQException + { + return _messagePublishInfo.getExchange(); + } + + public AMQShortString getRoutingKey() throws AMQException + { + return _messagePublishInfo.getRoutingKey(); + } + + public boolean isMandatory() throws AMQException + { + return _messagePublishInfo.isMandatory(); + } + + + public boolean isImmediate() throws AMQException + { + return _messagePublishInfo.isImmediate(); + } + + public ContentHeaderBody getContentHeaderBody() + { + return _contentHeaderBody; + } + + + public boolean isPersistent() + { + //todo remove literal values to a constant file such as AMQConstants in common + return getContentHeaderBody().properties instanceof BasicContentHeaderProperties && + ((BasicContentHeaderProperties) getContentHeaderBody().properties).getDeliveryMode() == 2; + } + + public boolean isRedelivered() + { + return false; + } + + public void setMessageStore(final MessageStore messageStore) + { + _messageStore = messageStore; + } + + public Long getMessageId() + { + return _messageId; + } + + public void setExchange(final Exchange e) + { + _exchange = e; + } + + public void route() throws AMQException + { + _exchange.route(this); + } + + 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/MessageHandleFactory.java b/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java index 94ab935115..0b214ca336 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java @@ -36,11 +36,11 @@ public class MessageHandleFactory // just hardcoded for now if (persistent) { - return new WeakReferenceMessageHandle(store); + return new WeakReferenceMessageHandle(messageId, store); } else { - return new InMemoryMessageHandle(); + return new InMemoryMessageHandle(messageId); } } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java b/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java index 12d6c5998a..6f9efd3200 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java @@ -1,129 +1,138 @@ -/* - * - * 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; - -public enum NotificationCheck -{ - - MESSAGE_COUNT_ALERT - { - boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) - { - int msgCount; - final long maximumMessageCount = queue.getMaximumMessageCount(); - if (maximumMessageCount!= 0 && (msgCount = queue.getMessageCount()) >= maximumMessageCount) - { - listener.notifyClients(this, queue, msgCount + ": Maximum count on queue threshold ("+ maximumMessageCount +") breached."); - return true; - } - return false; - } - }, - MESSAGE_SIZE_ALERT(true) - { - boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) - { - final long maximumMessageSize = queue.getMaximumMessageSize(); - if(maximumMessageSize != 0) - { - // Check for threshold message size - long messageSize = (msg == null) ? 0 : msg.getSize(); - - if (messageSize >= maximumMessageSize) - { - listener.notifyClients(this, queue, messageSize + "b : Maximum message size threshold ("+ maximumMessageSize +") breached. [Message ID=" + msg.getMessageId() + "]"); - return true; - } - } - return false; - } - - }, - QUEUE_DEPTH_ALERT - { - boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) - { - // Check for threshold queue depth in bytes - final long maximumQueueDepth = queue.getMaximumQueueDepth(); - - if(maximumQueueDepth != 0) - { - final long queueDepth = queue.getQueueDepth(); - - if (queueDepth >= maximumQueueDepth) - { - listener.notifyClients(this, queue, (queueDepth>>10) + "Kb : Maximum queue depth threshold ("+(maximumQueueDepth>>10)+"Kb) breached."); - return true; - } - } - return false; - } - - }, - MESSAGE_AGE_ALERT - { - boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) - { - - final long maxMessageAge = queue.getMaximumMessageAge(); - if(maxMessageAge != 0) - { - final long currentTime = System.currentTimeMillis(); - final long thresholdTime = currentTime - maxMessageAge; - final long firstArrivalTime = queue.getOldestMessageArrivalTime(); - - if(firstArrivalTime < thresholdTime) - { - long oldestAge = currentTime - firstArrivalTime; - listener.notifyClients(this, queue, (oldestAge/1000) + "s : Maximum age on queue threshold ("+(maxMessageAge /1000)+"s) breached."); - - return true; - } - } - return false; - - } - - } - ; - - private final boolean _messageSpecific; - - NotificationCheck() - { - this(false); - } - - NotificationCheck(boolean messageSpecific) - { - _messageSpecific = messageSpecific; - } - - public boolean isMessageSpecific() - { - return _messageSpecific; - } - - abstract boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener); - -} +/*
+ *
+ * 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;
+
+public enum NotificationCheck
+{
+
+ MESSAGE_COUNT_ALERT
+ {
+ boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
+ {
+ int msgCount;
+ final long maximumMessageCount = queue.getMaximumMessageCount();
+ if (maximumMessageCount!= 0 && (msgCount = queue.getMessageCount()) >= maximumMessageCount)
+ {
+ listener.notifyClients(this, queue, msgCount + ": Maximum count on queue threshold ("+ maximumMessageCount +") breached.");
+ return true;
+ }
+ return false;
+ }
+ },
+ MESSAGE_SIZE_ALERT(true)
+ {
+ boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
+ {
+ final long maximumMessageSize = queue.getMaximumMessageSize();
+ if(maximumMessageSize != 0)
+ {
+ // Check for threshold message size
+ long messageSize;
+ try
+ {
+ messageSize = (msg == null) ? 0 : msg.getContentHeaderBody().bodySize;
+ }
+ catch (AMQException e)
+ {
+ messageSize = 0;
+ }
+
+
+ if (messageSize >= maximumMessageSize)
+ {
+ listener.notifyClients(this, queue, messageSize + "b : Maximum message size threshold ("+ maximumMessageSize +") breached. [Message ID=" + msg.getMessageId() + "]");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ },
+ QUEUE_DEPTH_ALERT
+ {
+ boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
+ {
+ // Check for threshold queue depth in bytes
+ final long maximumQueueDepth = queue.getMaximumQueueDepth();
+
+ if(maximumQueueDepth != 0)
+ {
+ final long queueDepth = queue.getQueueDepth();
+
+ if (queueDepth >= maximumQueueDepth)
+ {
+ listener.notifyClients(this, queue, (queueDepth>>10) + "Kb : Maximum queue depth threshold ("+(maximumQueueDepth>>10)+"Kb) breached.");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ },
+ MESSAGE_AGE_ALERT
+ {
+ boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener)
+ {
+
+ final long maxMessageAge = queue.getMaximumMessageAge();
+ if(maxMessageAge != 0)
+ {
+ final long currentTime = System.currentTimeMillis();
+ final long thresholdTime = currentTime - maxMessageAge;
+ final long firstArrivalTime = queue.getOldestMessageArrivalTime();
+
+ if(firstArrivalTime < thresholdTime)
+ {
+ long oldestAge = currentTime - firstArrivalTime;
+ listener.notifyClients(this, queue, (oldestAge/1000) + "s : Maximum age on queue threshold ("+(maxMessageAge /1000)+"s) breached.");
+
+ return true;
+ }
+ }
+ return false;
+
+ }
+
+ }
+ ;
+
+ private final boolean _messageSpecific;
+
+ NotificationCheck()
+ {
+ this(false);
+ }
+
+ NotificationCheck(boolean messageSpecific)
+ {
+ _messageSpecific = messageSpecific;
+ }
+
+ public boolean isMessageSpecific()
+ {
+ return _messageSpecific;
+ }
+
+ abstract boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener);
+
+}
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 8553db3e09..dd967a7cb1 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 @@ -1,173 +1,186 @@ -/* - * - * 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.server.store.StoreContext; -import org.apache.log4j.Logger; - -import java.util.Set; -import java.util.HashSet; -import java.util.concurrent.atomic.AtomicReference; - +import org.apache.qpid.server.subscription.Subscription; -public class QueueEntry +/* +* +* 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 interface QueueEntry extends Comparable<QueueEntry> { - /** - * Used for debugging purposes. - */ - private static final Logger _log = Logger.getLogger(QueueEntry.class); - - private final AMQQueue _queue; - private final AMQMessage _message; - private Set<Subscription> _rejectedBy = null; - private AtomicReference<Object> _owner = new AtomicReference<Object>(); - - - public QueueEntry(AMQQueue queue, AMQMessage message) + public static enum State { - _queue = queue; - _message = message; + AVAILABLE, + ACQUIRED, + EXPIRED, + DEQUEUED, + DELETED } - - public AMQQueue getQueue() + public static interface StateChangeListener { - return _queue; + public void stateChanged(QueueEntry entry, State oldSate, State newState); } - public AMQMessage getMessage() + public abstract class EntryState { - return _message; - } + private EntryState() + { + } - public long getSize() - { - return getMessage().getSize(); + public abstract State getState(); } - public boolean getDeliveredToConsumer() - { - return getMessage().getDeliveredToConsumer(); - } - public boolean expired() throws AMQException + public final class AvailableState extends EntryState { - return getMessage().expired(_queue); - } - public boolean isTaken() - { - return _owner.get() != null; + public State getState() + { + return State.AVAILABLE; + } } - public boolean taken(Subscription sub) - { - return !(_owner.compareAndSet(null, sub == null ? this : sub)); - } - public void setDeliveredToConsumer() + public final class DequeuedState extends EntryState { - getMessage().setDeliveredToConsumer(); - } - public void release() - { - _owner.set(null); + public State getState() + { + return State.DEQUEUED; + } } - public String debugIdentity() - { - return getMessage().debugIdentity(); - } - public void process(StoreContext storeContext, boolean deliverFirst) throws AMQException + public final class DeletedState extends EntryState { - _queue.process(storeContext, this, deliverFirst); - } - public void checkDeliveredToConsumer() throws NoConsumersException - { - _message.checkDeliveredToConsumer(); + public State getState() + { + return State.DELETED; + } } - public void setRedelivered(boolean b) + public final class ExpiredState extends EntryState { - getMessage().setRedelivered(b); - } - public Subscription getDeliveredSubscription() - { - synchronized (this) + public State getState() { - Object owner = _owner.get(); - if (owner instanceof Subscription) - { - return (Subscription) owner; - } - else - { - return null; - } + return State.EXPIRED; } } - public void reject() + + public final class NonSubscriptionAcquiredState extends EntryState { - reject(getDeliveredSubscription()); + public State getState() + { + return State.ACQUIRED; + } } - public void reject(Subscription subscription) + public final class SubscriptionAcquiredState extends EntryState { - if (subscription != null) - { - if (_rejectedBy == null) - { - _rejectedBy = new HashSet<Subscription>(); - } + private final Subscription _subscription; - _rejectedBy.add(subscription); - } - else + public SubscriptionAcquiredState(Subscription subscription) { - _log.warn("Requesting rejection by null subscriber:" + debugIdentity()); + _subscription = subscription; } - } - public boolean isRejectedBy(Subscription subscription) - { - boolean rejected = _rejectedBy != null; - if (rejected) // We have subscriptions that rejected this message + public State getState() { - return _rejectedBy.contains(subscription); + return State.ACQUIRED; } - else // This messasge hasn't been rejected yet. + + public Subscription getSubscription() { - return rejected; + return _subscription; } } + final static EntryState AVAILABLE_STATE = new AvailableState(); + final static EntryState DELETED_STATE = new DeletedState(); + final static EntryState DEQUEUED_STATE = new DequeuedState(); + final static EntryState EXPIRED_STATE = new ExpiredState(); + final static EntryState NON_SUBSCRIPTION_ACQUIRED_STATE = new NonSubscriptionAcquiredState(); + + + + + AMQQueue getQueue(); + + AMQMessage getMessage(); + + long getSize(); + + boolean getDeliveredToConsumer(); + + boolean expired() throws AMQException; + + boolean isAcquired(); + + boolean acquire(); + boolean acquire(Subscription sub); + + boolean delete(); + boolean isDeleted(); + + boolean acquiredBySubscription(); + + void setDeliveredToSubscription(); + + void release(); + + String debugIdentity(); + + boolean immediateAndNotDelivered(); + + void setRedelivered(boolean b); + + Subscription getDeliveredSubscription(); + + void reject(); + + void reject(Subscription subscription); + + boolean isRejectedBy(Subscription subscription); + + void requeue(StoreContext storeContext) throws AMQException; + + void dequeue(final StoreContext storeContext) throws FailedDequeueException; + + void dispose(final StoreContext storeContext) throws MessageCleanupException; + + void restoreCredit(); + + void discard(StoreContext storeContext) throws FailedDequeueException, MessageCleanupException; + + 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 new file mode 100644 index 0000000000..d26d6af7b2 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -0,0 +1,392 @@ +/* + * + * 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.server.store.StoreContext; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.log4j.Logger; + +import java.util.Set; +import java.util.HashSet; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import java.util.concurrent.CopyOnWriteArraySet; + + +public class QueueEntryImpl implements QueueEntry +{ + + /** + * Used for debugging purposes. + */ + private static final Logger _log = Logger.getLogger(QueueEntryImpl.class); + + private final SimpleQueueEntryList _queueEntryList; + + private AMQMessage _message; + + + private Set<Subscription> _rejectedBy = null; + + 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"); + + + private volatile long _entryId; + + volatile QueueEntryImpl _next; + + + QueueEntryImpl(SimpleQueueEntryList queueEntryList) + { + this(queueEntryList,null,Long.MIN_VALUE); + _state = DELETED_STATE; + } + + + public QueueEntryImpl(SimpleQueueEntryList queueEntryList, AMQMessage message, final long entryId) + { + _queueEntryList = queueEntryList; + _message = message; + + _entryIdUpdater.set(this, entryId); + } + + public QueueEntryImpl(SimpleQueueEntryList queueEntryList, AMQMessage message) + { + _queueEntryList = queueEntryList; + _message = message; + } + + protected void setEntryId(long entryId) + { + _entryIdUpdater.set(this, entryId); + } + + protected long getEntryId() + { + return _entryId; + } + + public AMQQueue getQueue() + { + return _queueEntryList.getQueue(); + } + + public AMQMessage getMessage() + { + return _message; + } + + public long getSize() + { + return getMessage().getSize(); + } + + public boolean getDeliveredToConsumer() + { + return getMessage().getDeliveredToConsumer(); + } + + public boolean expired() throws AMQException + { + return getMessage().expired(getQueue()); + } + + public boolean isAcquired() + { + 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 acquire(sub.getOwningState()); + } + + public boolean acquiredBySubscription() + { + + return (_state instanceof SubscriptionAcquiredState); + } + + public void setDeliveredToSubscription() + { + getMessage().setDeliveredToConsumer(); + } + + public void release() + { + _stateUpdater.set(this,AVAILABLE_STATE); + } + + public String debugIdentity() + { + return getMessage().debugIdentity(); + } + + + public boolean immediateAndNotDelivered() + { + return _message.immediateAndNotDelivered(); + } + + public void setRedelivered(boolean b) + { + getMessage().setRedelivered(b); + } + + public Subscription getDeliveredSubscription() + { + EntryState state = _state; + if (state instanceof SubscriptionAcquiredState) + { + return ((SubscriptionAcquiredState) state).getSubscription(); + } + else + { + return null; + } + + } + + public void reject() + { + reject(getDeliveredSubscription()); + } + + public void reject(Subscription subscription) + { + if (subscription != null) + { + if (_rejectedBy == null) + { + _rejectedBy = new HashSet<Subscription>(); + } + + _rejectedBy.add(subscription); + } + else + { + _log.warn("Requesting rejection by null subscriber:" + debugIdentity()); + } + } + + public boolean isRejectedBy(Subscription subscription) + { + + if (_rejectedBy != null) // We have subscriptions that rejected this message + { + return _rejectedBy.contains(subscription); + } + else // This messasge hasn't been rejected yet. + { + return false; + } + } + + + public void requeue(final StoreContext storeContext) throws AMQException + { + getQueue().requeue(storeContext, this); + if(_stateChangeListeners != null) + { + notifyStateChange(QueueEntry.State.ACQUIRED, QueueEntry.State.AVAILABLE); + } + } + + public void dequeue(final StoreContext storeContext) throws FailedDequeueException + { + EntryState state = _state; + + if((state.getState() == State.ACQUIRED) &&_stateUpdater.compareAndSet(this, state, DEQUEUED_STATE)) + { + 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 + { + if(delete()) + { + getMessage().decrementReference(storeContext); + } + } + + public void restoreCredit() + { + EntryState state = _state; + if(state instanceof SubscriptionAcquiredState) + { + Subscription s = ((SubscriptionAcquiredState) _state).getSubscription(); + s.restoreCredit(this); + } + } + + public void discard(StoreContext storeContext) throws FailedDequeueException, MessageCleanupException + { + //if the queue is null then the message is waiting to be acked, but has been removed. + if (getQueue() != null) + { + dequeue(storeContext); + } + + dispose(storeContext); + } + + public boolean isQueueDeleted() + { + 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 new file mode 100644 index 0000000000..313e076f61 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java @@ -0,0 +1,34 @@ +/* +* +* 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 QueueEntryList +{ + AMQQueue getQueue(); + + 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/QueueNotificationListener.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java index f1e7c98387..959ca03c80 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java @@ -1,27 +1,27 @@ -/* - * - * 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 QueueNotificationListener -{ - void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg); -} +/*
+ *
+ * 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 QueueNotificationListener
+{
+ void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg);
+}
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 new file mode 100644 index 0000000000..247402e442 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -0,0 +1,1671 @@ +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.server.exchange.Exchange; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionList; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.AMQException; +import org.apache.qpid.pool.ReadWriteRunnable; +import org.apache.qpid.pool.ReferenceCountingExecutorService; +import org.apache.qpid.configuration.Configured; +import org.apache.log4j.Logger; + +import javax.management.JMException; +import java.util.List; +import java.util.Set; +import java.util.ArrayList; +import java.util.EnumSet; +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; +import java.util.concurrent.atomic.AtomicReference; + +/* +* +* 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 SimpleAMQQueue implements AMQQueue, Subscription.StateListener +{ + private static final Logger _logger = Logger.getLogger(SimpleAMQQueue.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 VirtualHost _virtualHost; + + /** Used to track bindings to exchanges so that on deletion they can easily be cancelled. */ + private final ExchangeBindings _bindings = new ExchangeBindings(this); + + private final AtomicBoolean _deleted = new AtomicBoolean(false); + + private final List<Task> _deleteTaskList = new CopyOnWriteArrayList<Task>(); + + private final AtomicInteger _atomicQueueCount = new AtomicInteger(0); + + private final AtomicLong _atomicQueueSize = new AtomicLong(0L); + + private final AtomicInteger _activeSubscriberCount = new AtomicInteger(); + + protected final SubscriptionList _subscriptionList = new SubscriptionList(this); + private final AtomicReference<SubscriptionList.SubscriptionNode> _lastSubscriptionNode = new AtomicReference<SubscriptionList.SubscriptionNode>(_subscriptionList.getHead()); + + private volatile Subscription _exclusiveSubscriber; + + + private final QueueEntryList _entries; + + + private final AMQQueueMBean _managedObject; + private final Executor _asyncDelivery; + private final AtomicLong _totalMessagesReceived = new AtomicLong(); + + + + /** 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; + + + + private static final int MAX_ASYNC_DELIVERIES = 10; + + + private final Set<NotificationCheck> _notificationChecks = EnumSet.noneOf(NotificationCheck.class); + + + private final AtomicLong _stateChangeCount = new AtomicLong(Long.MIN_VALUE); + private AtomicReference _asynchronousRunner = new AtomicReference(null); + private AtomicInteger _deliveredMessages = new AtomicInteger(); + + 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) + { + 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; + _entries = entryListFactory.createQueueEntryList(this); + + _asyncDelivery = ReferenceCountingExecutorService.getInstance().acquireExecutorService(); + + try + { + _managedObject = new AMQQueueMBean(this); + _managedObject.register(); + } + catch (JMException e) + { + throw new AMQException("AMQQueue MBean creation has failed ", e); + } + + + // This ensure that the notification checks for the configured alerts are created. + setMaximumMessageAge(_maximumMessageAge); + setMaximumMessageCount(_maximumMessageCount); + setMaximumMessageSize(_maximumMessageSize); + setMaximumQueueDepth(_maximumQueueDepth); + + } + + // ------ Getters and Setters + + public AMQShortString getName() + { + return _name; + } + + public boolean isDurable() + { + return _durable; + } + + public boolean isAutoDelete() + { + return _autoDelete; + } + + public AMQShortString getOwner() + { + return _owner; + } + + public VirtualHost getVirtualHost() + { + return _virtualHost; + } + + + // ------ bind and unbind + + 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"); + } + } + + + // ------ Manage Subscriptions + + public synchronized void registerSubscription(final Subscription subscription, final boolean exclusive) throws AMQException + { + + + if(isExclusiveSubscriber()) + { + throw new ExistingExclusiveSubscription(); + } + + if(exclusive) + { + if(getConsumerCount() != 0) + { + throw new ExistingSubscriptionPreventsExclusive(); + } + else + { + _exclusiveSubscriber = subscription; + + } + } + + + _activeSubscriberCount.incrementAndGet(); + subscription.setStateListener(this); + subscription.setLastSeenEntry(null,_entries.getHead()); + + if(!isDeleted()) + { + subscription.setQueue(this); + _subscriptionList.add(subscription); + if(isDeleted()) + { + subscription.queueDeleted(this); + } + } + else + { + // TODO + } + + + deliverAsync(subscription); + + } + + public synchronized void unregisterSubscription(final Subscription subscription) throws AMQException + { + if(subscription == null) + { + throw new NullPointerException("subscription argument is null"); + } + + boolean removed = _subscriptionList.remove(subscription); + + + + if(removed) + { + subscription.close(); + // No longer can the queue have an exclusive consumer + setExclusiveSubscriber(null); + + + QueueEntry lastSeen; + + while((lastSeen = subscription.getLastSeenEntry()) != null) + { + subscription.setLastSeenEntry(lastSeen, null); + } + + + + + // auto-delete queues must be deleted if there are no remaining subscribers + + if (_autoDelete && getConsumerCount() == 0) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Auto-deleteing queue:" + this); + } + + delete(); + + // 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); + } + } + + + } + + + // ------ Enqueue / Dequeue + + public QueueEntry enqueue(StoreContext storeContext, AMQMessage message) throws AMQException + { + + + + incrementQueueCount(); + incrementQueueSize(message); + + _totalMessagesReceived.incrementAndGet(); + + + QueueEntry entry; + Subscription exclusiveSub = _exclusiveSubscriber; + + if(exclusiveSub != null) + { + exclusiveSub.getSendLock(); + + try + { + entry = _entries.add(message); + + deliverToSubscription(exclusiveSub, entry); + + + // where there is more than one producer there's a reasonable chance that even though there is + // no "queueing" we do not deliver because we get an interleving of _entries.add and + // deliverToSubscription between threads. Therefore have one more try. + if(!(entry.isAcquired() || entry.isDeleted())) + { + deliverToSubscription(exclusiveSub, entry); + } + } + finally + { + exclusiveSub.releaseSendLock(); + } + } + else + { + entry = _entries.add(message); + /* + + iterate over subscriptions and if any is at the end of the queue and can deliver this message, then deliver the message + + */ + SubscriptionList.SubscriptionNode node = _lastSubscriptionNode.get(); + SubscriptionList.SubscriptionNode nextNode = node.getNext(); + if(nextNode == null) + { + nextNode = _subscriptionList.getHead().getNext(); + } + while(nextNode != null) + { + if(_lastSubscriptionNode.compareAndSet(node, nextNode)) + { + break; + } + else + { + node = _lastSubscriptionNode.get(); + nextNode = node.getNext(); + if(nextNode == null) + { + nextNode = _subscriptionList.getHead().getNext(); + } + } + } + + + // 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() || entry.isDeleted()) && loops != 0) + { + if(nextNode == null) + { + loops--; + nextNode = _subscriptionList.getHead(); + } + else + { + // if subscription at end, and active, offer + Subscription sub = nextNode.getSubscription(); + deliverToSubscription(sub, entry); + } + nextNode = nextNode.getNext(); + + } + } + + + if(entry.immediateAndNotDelivered()) + { + dequeue(storeContext, entry); + entry.dispose(storeContext); + } + else if(!(entry.isAcquired() || entry.isDeleted())) + { + checkSubscriptionsNotAheadOfDelivery(entry); + + deliverAsync(); + } + + + try + { + _managedObject.checkForNotification(entry.getMessage()); + } + catch (JMException e) + { + throw new AMQException("Unable to get notification from manage queue: " + e, e); + } + + + return entry; + + } + + private void deliverToSubscription(final Subscription sub, final QueueEntry entry) + throws AMQException + { + + sub.getSendLock(); + try + { + if(subscriptionReadyAndHasInterest(sub, entry) + && !sub.isSuspended()) + { + if( !sub.wouldSuspend(entry)) + { + if(!sub.isBrowser() && !entry.acquire(sub)) + { + // restore credit here that would have been taken away by wouldSuspend since we didn't manage + // to acquire the entry for this subscription + sub.restoreCredit(entry); + } + else + { + + deliverMessage(sub, entry); + + } + } + } + } + finally + { + sub.releaseSendLock(); + } + } + + protected void checkSubscriptionsNotAheadOfDelivery(final QueueEntry entry) + { + // This method is only required for queues which mess with ordering + // Simple Queues don't :-) + } + + 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 + { + _deliveredMessages.incrementAndGet(); + sub.send(entry); + + } + + private boolean subscriptionReadyAndHasInterest(final Subscription sub, final QueueEntry entry) + { + + // We need to move this subscription on, past entries which are already acquired, or deleted or ones it has no + // interest in. + QueueEntry node = sub.getLastSeenEntry(); + while(node != null && (node.isAcquired() || node.isDeleted() || !sub.hasInterest(node)) ) + { + + QueueEntry newNode = _entries.next(node); + if(newNode != null) + { + sub.setLastSeenEntry(node, newNode); + node = sub.getLastSeenEntry(); + } + else + { + node = null; + break; + } + + } + + + if(node == entry) + { + // If the first entry that subscription can process is the one we are trying to deliver to it, then we are + // good + return true; + } + else + { + // Otherwise we should try to update the subscription's last seen entry to the entry we got to, providing + // no-one else has updated it to something furhter on in the list + //TODO - check + //updateLastSeenEntry(sub, entry); + return false; + } + + } + + private void updateLastSeenEntry(final Subscription sub, final QueueEntry entry) + { + QueueEntry node = sub.getLastSeenEntry(); + + if(node != null && entry.compareTo(node) < 0 && sub.hasInterest(entry)) + { + do + { + if(sub.setLastSeenEntry(node,entry)) + { + return; + } + else + { + node = sub.getLastSeenEntry(); + } + } while (node != null && entry.compareTo(node) < 0); + } + + } + + public void requeue(StoreContext storeContext, QueueEntry entry) throws AMQException + { + + SubscriptionList.SubscriptionNodeIterator subscriberIter = _subscriptionList.iterator(); + // iterate over all the subscribers, and if they are in advance of this queue entry then move them backwards + while(subscriberIter.advance()) + { + Subscription sub = subscriberIter.getNode().getSubscription(); + + // we don't make browsers send the same stuff twice + if(!sub.isBrowser()) + { + updateLastSeenEntry(sub, entry); + } + } + + + deliverAsync(); + + + } + + public void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException + { + decrementQueueCount(); + decrementQueueSize(entry); + if(entry.acquiredBySubscription()) + { + _deliveredMessages.decrementAndGet(); + } + + try + { + AMQMessage msg = entry.getMessage(); + if(isDurable() && msg.isPersistent()) + { + _virtualHost.getMessageStore().dequeueMessage(storeContext, this, msg.getMessageId()); + } + //entry.dispose(storeContext); + + } + 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); + } + + + } + + 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 + entry to resend and move back the subscription pointer. */ + + subscription.getSendLock(); + try + { + if(!subscription.isClosed()) + { + deliverMessage(subscription, entry); + return true; + } + else + { + return false; + } + } + finally + { + subscription.releaseSendLock(); + } + } + + + + + + public int getConsumerCount() + { + return _subscriptionList.size(); + } + + public int getActiveConsumerCount() + { + return _activeSubscriberCount.get(); + } + + public boolean isUnused() + { + return getConsumerCount() == 0; + } + + public boolean isEmpty() + { + return getMessageCount() == 0; + } + + public int getMessageCount() + { + return getAtomicQueueCount().get(); + } + + public long getQueueDepth() + { + return getAtomicQueueSize().get(); + } + + public int getUndeliveredMessageCount() + { + int count = getMessageCount() - _deliveredMessages.get(); + if(count < 0) + { + return 0; + } + else + { + return count; + } + } + + + public long getReceivedMessageCount() + { + return _totalMessagesReceived.get(); + } + + public long getOldestMessageArrivalTime() + { + QueueEntry entry = getOldestQueueEntry(); + return entry == null ? Long.MAX_VALUE : entry.getMessage().getArrivalTime(); + } + + protected QueueEntry getOldestQueueEntry() + { + return _entries.next(_entries.getHead()); + } + + public boolean isDeleted() + { + return _deleted.get(); + } + + + + public List<QueueEntry> getMessagesOnTheQueue() + { + ArrayList<QueueEntry> entryList = new ArrayList<QueueEntry>(); + QueueEntryIterator queueListIterator = _entries.iterator(); + while(queueListIterator.advance()) + { + QueueEntry node = queueListIterator.getNode(); + if(node != null && !node.isDeleted()) + { + entryList.add(node); + } + } + return entryList; + + } + + public void stateChange(Subscription sub, Subscription.State oldState, Subscription.State newState) + { + if(oldState == Subscription.State.ACTIVE && newState != Subscription.State.ACTIVE) + { + _activeSubscriberCount.decrementAndGet(); + + } + else if(newState == Subscription.State.ACTIVE) + { + if(oldState != Subscription.State.ACTIVE) + { + _activeSubscriberCount.incrementAndGet(); + + } + deliverAsync(sub); + } + } + + public int compareTo(final AMQQueue o) + { + return _name.compareTo(o.getName()); + } + + public AtomicInteger getAtomicQueueCount() + { + return _atomicQueueCount; + } + + public AtomicLong getAtomicQueueSize() + { + return _atomicQueueSize; + } + + private boolean isExclusiveSubscriber() + { + return _exclusiveSubscriber != null; + } + + private void setExclusiveSubscriber(Subscription exclusiveSubscriber) + { + _exclusiveSubscriber = exclusiveSubscriber; + } + + public static interface QueueEntryFilter + { + public boolean accept(QueueEntry entry); + + public boolean filterComplete(); + } + + + + public List<QueueEntry> getMessagesOnTheQueue(final long fromMessageId, final long toMessageId) + { + return getMessagesOnTheQueue(new QueueEntryFilter() + { + + public boolean accept(QueueEntry entry) + { + final long messageId = entry.getMessage().getMessageId(); + return messageId >= fromMessageId && messageId <= toMessageId; + } + + public boolean filterComplete() + { + return false; + } + }); + } + + public QueueEntry getMessageOnTheQueue(final long messageId) + { + List<QueueEntry> entries = getMessagesOnTheQueue(new QueueEntryFilter() + { + private boolean _complete; + + public boolean accept(QueueEntry entry) + { + _complete = entry.getMessage().getMessageId() == messageId; + return _complete; + } + + public boolean filterComplete() + { + return _complete; + } + }); + return entries.isEmpty() ? null : entries.get(0); + } + + + public List<QueueEntry> getMessagesOnTheQueue(QueueEntryFilter filter) + { + ArrayList<QueueEntry> entryList = new ArrayList<QueueEntry>(); + QueueEntryIterator queueListIterator = _entries.iterator(); + while(queueListIterator.advance() && !filter.filterComplete()) + { + QueueEntry node = queueListIterator.getNode(); + if(!node.isDeleted() && filter.accept(node)) + { + entryList.add(node); + } + } + return entryList; + + } + + + public void moveMessagesToAnotherQueue(final long fromMessageId, + final long toMessageId, + String queueName, + StoreContext storeContext) + { + + AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); + MessageStore store = getVirtualHost().getMessageStore(); + + + List<QueueEntry> entries = getMessagesOnTheQueue(new QueueEntryFilter() + { + + public boolean accept(QueueEntry entry) + { + final long messageId = entry.getMessage().getMessageId(); + return (messageId >= fromMessageId) + && (messageId <= toMessageId) + && entry.acquire(); + } + + public boolean filterComplete() + { + return false; + } + }); + + + try + { + store.beginTran(storeContext); + + // Move the messages in on the message store. + for (QueueEntry entry : entries) + { + AMQMessage message = entry.getMessage(); + + if(message.isPersistent() && toQueue.isDurable()) + { + store.enqueueMessage(storeContext, toQueue, message.getMessageId()); + } + // dequeue does not decrement the refence count + entry.dequeue(storeContext); + } + + // Commit and flush the move transcations. + try + { + store.commitTran(storeContext); + } + catch (AMQException e) + { + throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e); + } + } + catch (AMQException e) + { + try + { + store.abortTran(storeContext); + } + catch (AMQException rollbackEx) + { + _logger.error("Failed to rollback transaction when error occured moving messages", rollbackEx); + } + throw new RuntimeException(e); + } + + try + { + for (QueueEntry entry : entries) + { + toQueue.enqueue(storeContext, entry.getMessage()); + + } + } + catch (MessageCleanupException e) + { + throw new RuntimeException(e); + } + catch (AMQException e) + { + throw new RuntimeException(e); + } + + + } + + public void copyMessagesToAnotherQueue(final long fromMessageId, + final long toMessageId, + String queueName, + final StoreContext storeContext) + { + AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); + MessageStore store = getVirtualHost().getMessageStore(); + + + List<QueueEntry> entries = getMessagesOnTheQueue(new QueueEntryFilter() + { + + public boolean accept(QueueEntry entry) + { + final long messageId = entry.getMessage().getMessageId(); + if((messageId >= fromMessageId) + && (messageId <= toMessageId)) + { + if(!entry.isDeleted()) + { + return entry.getMessage().incrementReference(); + } + } + + return false; + } + + public boolean filterComplete() + { + return false; + } + }); + + try + { + store.beginTran(storeContext); + + // Move the messages in on the message store. + for (QueueEntry entry : entries) + { + AMQMessage message = entry.getMessage(); + + if(message.isReferenced() && message.isPersistent() && toQueue.isDurable()) + { + store.enqueueMessage(storeContext, toQueue, message.getMessageId()); + } + } + + // Commit and flush the move transcations. + try + { + store.commitTran(storeContext); + } + catch (AMQException e) + { + throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e); + } + } + catch (AMQException e) + { + try + { + store.abortTran(storeContext); + } + catch (AMQException rollbackEx) + { + _logger.error("Failed to rollback transaction when error occured moving messages", rollbackEx); + } + throw new RuntimeException(e); + } + + try + { + for (QueueEntry entry : entries) + { + if(entry.getMessage().isReferenced()) + { + toQueue.enqueue(storeContext, entry.getMessage()); + } + } + } + catch (MessageCleanupException e) + { + throw new RuntimeException(e); + } + catch (AMQException e) + { + throw new RuntimeException(e); + } + + + } + + public void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext) + { + + try + { + QueueEntryIterator queueListIterator = _entries.iterator(); + + + while(queueListIterator.advance()) + { + QueueEntry node = queueListIterator.getNode(); + + final long messageId = node.getMessage().getMessageId(); + + if((messageId >= fromMessageId) + && (messageId <= toMessageId) + && !node.isDeleted() + && node.acquire()) + { + node.discard(storeContext); + } + + } + } + catch (AMQException e) + { + throw new RuntimeException(e); + } + + } + + // ------ Management functions + + + public void deleteMessageFromTop(StoreContext storeContext) throws AMQException + { + QueueEntryIterator queueListIterator = _entries.iterator(); + boolean noDeletes = true; + + while(noDeletes && queueListIterator.advance() ) + { + QueueEntry node = queueListIterator.getNode(); + if(!node.isDeleted() && node.acquire()) + { + node.discard(storeContext); + noDeletes = false; + } + + } + } + + public long clearQueue(StoreContext storeContext) throws AMQException + { + + QueueEntryIterator queueListIterator = _entries.iterator(); + long count = 0; + + while(queueListIterator.advance()) + { + QueueEntry node = queueListIterator.getNode(); + if(!node.isDeleted() && node.acquire()) + { + node.discard(storeContext); + count++; + } + + } + return count; + + } + + + public void addQueueDeleteTask(final Task task) + { + _deleteTaskList.add(task); + } + + public int delete() throws AMQException + { + if (!_deleted.getAndSet(true)) + { + + SubscriptionList.SubscriptionNodeIterator subscriptionIter = _subscriptionList.iterator(); + + while (subscriptionIter.advance()) + { + Subscription s = subscriptionIter.getNode().getSubscription(); + if(s != null) + { + s.queueDeleted(this); + } + } + + _bindings.deregister(); + _virtualHost.getQueueRegistry().unregisterQueue(_name); + + + _managedObject.unregister(); + for (Task task : _deleteTaskList) + { + task.doTask(this); + } + + _deleteTaskList.clear(); + ReferenceCountingExecutorService.getInstance().releaseExecutorService(); + } + return getMessageCount(); + + } + + + public void deliverAsync() + { + _stateChangeCount.incrementAndGet(); + + Runner runner = new Runner(); + + if(_asynchronousRunner.compareAndSet(null,runner)) + { + _asyncDelivery.execute(runner); + } + } + + public void deliverAsync(Subscription sub) + { + _asyncDelivery.execute(new SubFlushRunner(sub)); + } + + private class Runner implements ReadWriteRunnable + { + public void run() + { + try + { + processQueue(this); + } + catch (AMQException e) + { + _logger.error(e); + } + + } + + public boolean isRead() + { + return false; + } + + public boolean isWrite() + { + return true; + } + } + + + private class SubFlushRunner implements ReadWriteRunnable + { + private final Subscription _sub; + + + public SubFlushRunner(Subscription sub) + { + _sub = sub; + } + + public void run() + { + boolean complete = false; + try + { + complete = flushSubscription(_sub, MAX_ASYNC_DELIVERIES); + + } + catch (AMQException e) + { + _logger.error(e); + } + if(!complete && !_sub.isSuspended()) + { + _asyncDelivery.execute(this); + } + + } + + public boolean isRead() + { + return false; + } + + public boolean isWrite() + { + return true; + } + } + + public void flushSubscription(Subscription sub) throws AMQException + { + flushSubscription(sub, Long.MAX_VALUE); + } + + public boolean flushSubscription(Subscription sub, long deliveries) throws AMQException + { + boolean atTail = false; + boolean advanced; + + while(!sub.isSuspended() && !atTail && deliveries != 0) + { + + advanced = false; + sub.getSendLock(); + try + { + 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 + { + deliveries--; + deliverMessage(sub, node); + + if(sub.isBrowser()) + { + QueueEntry newNode = _entries.next(node); + + if(newNode != null) + { + advanced = true; + 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) && !advanced; + + } + } + finally + { + sub.releaseSendLock(); + } + + } + + // if there's (potentially) more than one subscription the others will potentially not have been advanced to the + // next entry they are interested in yet. This would lead to holding on to references to expired messages, etc + // which would give us memory "leak". + + if(!isExclusiveSubscriber()) + { + advanceAllSubscriptions(); + } + + if(atTail && sub.isAutoClose()) + { + unregisterSubscription(sub); + + ProtocolOutputConverter converter = sub.getChannel().getProtocolSession().getProtocolOutputConverter(); + converter.confirmConsumerAutoClose(sub.getChannel().getChannelId(), sub.getConsumerTag()); + } + + return atTail; + } + + protected void advanceAllSubscriptions() throws AMQException + { + SubscriptionList.SubscriptionNodeIterator subscriberIter = _subscriptionList.iterator(); + while(subscriberIter.advance()) + { + SubscriptionList.SubscriptionNode subNode = subscriberIter.getNode(); + Subscription sub = subNode.getSubscription(); + moveSubscriptionToNextNode(sub); + } + } + + private QueueEntry moveSubscriptionToNextNode(final Subscription sub) + throws AMQException + { + QueueEntry node = sub.getLastSeenEntry(); + + while(node != null && (node.isAcquired() || node.isDeleted() || node.expired())) + { + if(!node.isAcquired() && !node.isDeleted() && node.expired()) + { + if(node.acquire()) + { + final StoreContext reapingStoreContext = new StoreContext(); + node.discard(reapingStoreContext); + } + } + QueueEntry newNode = _entries.next(node); + if(newNode != null) + { + sub.setLastSeenEntry(node, newNode); + node = sub.getLastSeenEntry(); + } + else + { + break; + } + + } + return node; + } + + + private void processQueue(Runnable runner) throws AMQException + { + long stateChangeCount; + long previousStateChangeCount = Long.MIN_VALUE; + boolean deliveryIncomplete = true; + + int extraLoops = 1; + int deliveries = MAX_ASYNC_DELIVERIES; + + _asynchronousRunner.compareAndSet(runner,null); + + while(deliveries != 0 && ((previousStateChangeCount != (stateChangeCount = _stateChangeCount.get())) || deliveryIncomplete ) && _asynchronousRunner.compareAndSet(null,runner)) + { + // we want to have one extra loop after every subscription has reached the point where it cannot move + // further, just in case the advance of one subscription in the last loop allows a different subscription to + // move forward in the next iteration + + if(previousStateChangeCount != stateChangeCount) + { + extraLoops = 1; + } + + previousStateChangeCount = stateChangeCount; + deliveryIncomplete = _subscriptionList.size() != 0; + boolean done = true; + + + SubscriptionList.SubscriptionNodeIterator subscriptionIter = _subscriptionList.iterator(); + //iterate over the subscribers and try to advance their pointer + while(subscriptionIter.advance()) + { + Subscription sub = subscriptionIter.getNode().getSubscription(); + if(sub != null) + { + sub.getSendLock(); + try + { + QueueEntry node = moveSubscriptionToNextNode(sub); + + if(node != null && sub.isActive()) + { + boolean advanced = false; + boolean subActive = false; + + if(!(node.isAcquired() || node.isDeleted())) + { + if(!sub.isSuspended()) + { + subActive = true; + if(sub.hasInterest(node)) + { + if(!sub.wouldSuspend(node)) + { + if(!sub.isBrowser() && !node.acquire(sub)) + { + sub.restoreCredit(node); + + } + else + { + deliverMessage(sub, node); + deliveries--; + + if(sub.isBrowser()) + { + QueueEntry newNode = _entries.next(node); + + if(newNode != null) + { + sub.setLastSeenEntry(node, newNode); + node = sub.getLastSeenEntry(); + advanced = true; + } + + + } + } + done = false; + } + else + { + node.addStateChangeListener(new QueueEntryListener(sub,node)); + } + } + 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); + } + } + } + } + final boolean atTail = (_entries.next(node) == null); + + done = done && (!subActive || atTail); + + if(atTail && !advanced && sub.isAutoClose()) + { + unregisterSubscription(sub); + + ProtocolOutputConverter converter = sub.getChannel().getProtocolSession().getProtocolOutputConverter(); + converter.confirmConsumerAutoClose(sub.getChannel().getChannelId(), sub.getConsumerTag()); + + } + + } + } + finally + { + sub.releaseSendLock(); + } + } + if(done) + { + if(extraLoops == 0) + { + deliveryIncomplete = false; + } + else + { + extraLoops--; + } + } + else + { + extraLoops = 1; + } + } + + + + _asynchronousRunner.set(null); + } + + // If deliveries == 0 then the limitting factor was the time-slicing rather than available messages or credit + // therefore we should schedule this runner again (unless someone beats us to it :-) ). + if(deliveries == 0 && _asynchronousRunner.compareAndSet(null,runner)) + { + _asyncDelivery.execute(runner); + } + } + + + public void removeExpiredIfNoSubscribers() throws AMQException + { + + final StoreContext storeContext = new StoreContext(); + + QueueEntryIterator queueListIterator = _entries.iterator(); + + while(queueListIterator.advance()) + { + QueueEntry node = queueListIterator.getNode(); + if(!node.isDeleted() && node.expired() && node.acquire()) + { + + node.discard(storeContext); + } + + } + + } + + + 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 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 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 Set<NotificationCheck> getNotificationChecks() + { + return _notificationChecks; + } + + public ManagedObject getManagedObject() + { + return _managedObject; + } + + private final class QueueEntryListener implements QueueEntry.StateChangeListener + { + private final QueueEntry _entry; + private final Subscription _sub; + + public QueueEntryListener(final Subscription sub, final QueueEntry entry) + { + _entry = entry; + _sub = sub; + } + + public boolean equals(Object o) + { + return _entry == ((QueueEntryListener)o)._entry && _sub == ((QueueEntryListener)o)._sub; + } + + public int hashCode() + { + return System.identityHashCode(_entry) ^ System.identityHashCode(_sub); + } + + public void stateChanged(QueueEntry entry, QueueEntry.State oldSate, QueueEntry.State newState) + { + entry.removeStateChangeListener(this); + deliverAsync(_sub); + } + } +}
\ 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/SubscriptionImpl.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java deleted file mode 100644 index 05cd461582..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java +++ /dev/null @@ -1,680 +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.Queue; -import java.util.concurrent.atomic.AtomicBoolean; - -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; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.output.ProtocolOutputConverter; -import org.apache.qpid.server.filter.FilterManager; -import org.apache.qpid.server.filter.FilterManagerFactory; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.store.StoreContext; -import org.apache.qpid.util.ConcurrentLinkedQueueAtomicSize; -import org.apache.qpid.util.MessageQueue; -import org.apache.qpid.util.ConcurrentLinkedMessageQueueAtomicSize; - -/** - * Encapsulation of a supscription to a queue. <p/> Ties together the protocol session of a subscriber, the consumer tag - * that was given out by the broker and the channel id. <p/> - */ -public class SubscriptionImpl implements Subscription -{ - - private static final Logger _suspensionlogger = Logger.getLogger("Suspension"); - private static final Logger _logger = Logger.getLogger(SubscriptionImpl.class); - - public final AMQChannel channel; - - public final AMQProtocolSession protocolSession; - - public final AMQShortString consumerTag; - - private final Object _sessionKey; - - private MessageQueue<QueueEntry> _messages; - - private Queue<QueueEntry> _resendQueue; - - private final boolean _noLocal; - - /** True if messages need to be acknowledged */ - private final boolean _acks; - private FilterManager _filters; - private final boolean _isBrowser; - private final Boolean _autoClose; - private boolean _sentClose = false; - - private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString(); - - private AMQQueue _queue; - private final AtomicBoolean _sendLock = new AtomicBoolean(false); - - - public static class Factory implements SubscriptionFactory - { - public Subscription createSubscription(int channel, AMQProtocolSession protocolSession, - AMQShortString consumerTag, boolean acks, FieldTable filters, - boolean noLocal, AMQQueue queue) throws AMQException - { - return new SubscriptionImpl(channel, protocolSession, consumerTag, acks, filters, noLocal, queue); - } - - public SubscriptionImpl createSubscription(int channel, AMQProtocolSession protocolSession, AMQShortString consumerTag) - throws AMQException - { - return new SubscriptionImpl(channel, protocolSession, consumerTag, false, null, false, null); - } - } - - public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession, - AMQShortString consumerTag, boolean acks) - throws AMQException - { - this(channelId, protocolSession, consumerTag, acks, null, false, null); - } - - public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession, - AMQShortString consumerTag, boolean acks, FieldTable filters, - boolean noLocal, AMQQueue queue) - throws AMQException - { - AMQChannel channel = protocolSession.getChannel(channelId); - if (channel == null) - { - throw new AMQException(AMQConstant.NOT_FOUND, "channel :" + channelId + " not found in protocol session"); - } - - this.channel = channel; - this.protocolSession = protocolSession; - this.consumerTag = consumerTag; - _sessionKey = protocolSession.getKey(); - _acks = acks; - _noLocal = noLocal; - _queue = queue; - - _filters = FilterManagerFactory.createManager(filters); - - - if (_filters != null) - { - Object isBrowser = filters.get(AMQPFilterTypes.NO_CONSUME.getValue()); - if (isBrowser != null) - { - _isBrowser = (Boolean) isBrowser; - } - else - { - _isBrowser = false; - } - } - else - { - _isBrowser = false; - } - - - if (_filters != null) - { - Object autoClose = filters.get(AMQPFilterTypes.AUTO_CLOSE.getValue()); - if (autoClose != null) - { - _autoClose = (Boolean) autoClose; - } - else - { - _autoClose = false; - } - } - else - { - _autoClose = false; - } - - - if (filtersMessages()) - { - _messages = new ConcurrentLinkedMessageQueueAtomicSize<QueueEntry>(); - } - else - { - // Reference the DeliveryManager - _messages = null; - } - } - - - public SubscriptionImpl(int channel, AMQProtocolSession protocolSession, - AMQShortString consumerTag) - throws AMQException - { - this(channel, protocolSession, consumerTag, false); - } - - public boolean equals(Object o) - { - return (o instanceof SubscriptionImpl) && equals((SubscriptionImpl) o); - } - - /** - * Equality holds if the session matches and the channel and consumer tag are the same. - * - * @param psc The subscriptionImpl to compare - * - * @return equality - */ - private boolean equals(SubscriptionImpl psc) - { - return _sessionKey.equals(psc._sessionKey) - && psc.channel == channel - && psc.consumerTag.equals(consumerTag); - } - - public int hashCode() - { - return _sessionKey.hashCode(); - } - - public String toString() - { - String subscriber = "[channel=" + channel + - ", consumerTag=" + consumerTag + - ", session=" + protocolSession.getKey() + - ", resendQueue=" + (_resendQueue != null); - - if (_resendQueue != null) - { - subscriber += ", resendSize=" + _resendQueue.size(); - } - - - return subscriber + "]"; - } - - /** - * This method can be called by each of the publisher threads. As a result all changes to the channel object must be - * thread safe. - * - * @param msg The message to send - * @param queue the Queue it has been sent from - * - * @throws AMQException - */ - public void send(QueueEntry msg, AMQQueue queue) throws AMQException - { - if (msg != null) - { - if (_isBrowser) - { - sendToBrowser(msg, queue); - } - else - { - sendToConsumer(channel.getStoreContext(), msg, queue); - } - } - else - { - _logger.error("Attempt to send Null message", new NullPointerException()); - } - } - - private void sendToBrowser(QueueEntry msg, AMQQueue queue) throws AMQException - { - // We don't decrement the reference here as we don't want to consume the message - // but we do want to send it to the client. - - synchronized (channel) - { - long deliveryTag = channel.getNextDeliveryTag(); - - if (_sendLock.get()) - { - _logger.error("Sending " + msg + " when subscriber(" + this + ") is closed!"); - } - - protocolSession.getProtocolOutputConverter().writeDeliver(msg.getMessage(), channel.getChannelId(), deliveryTag, consumerTag); - } - } - - private void sendToConsumer(StoreContext storeContext, QueueEntry entry, AMQQueue queue) - throws AMQException - { - 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. - final AMQMessage message = entry.getMessage(); - - if (!_acks) - { - if (_logger.isDebugEnabled()) - { - _logger.debug("No ack mode so dequeuing message immediately: " + message.getMessageId()); - } - queue.dequeue(storeContext, entry); - } - - final ProtocolOutputConverter outputConverter = protocolSession.getProtocolOutputConverter(); - final int channelId = channel.getChannelId(); - - synchronized (channel) - { - final long deliveryTag = channel.getNextDeliveryTag(); - - - if (_acks) - { - channel.addUnacknowledgedMessage(entry, deliveryTag, consumerTag); - } - - outputConverter.writeDeliver(message, channelId, deliveryTag, consumerTag); - - - } - if (!_acks) - { - message.decrementReference(storeContext); - } - } - finally - { - //Only set delivered if it actually was writen successfully.. - // using a try->finally would set it even if an error occured. - // Is this what we want? - - entry.setDeliveredToConsumer(); - } - } - - public boolean isSuspended() - { -// if (_suspensionlogger.isInfoEnabled()) -// { -// if (channel.isSuspended()) -// { -// _suspensionlogger.debug("Subscription(" + debugIdentity() + ") channel's is susupended"); -// } -// if (_sendLock.get()) -// { -// _suspensionlogger.debug("Subscription(" + debugIdentity() + ") has sendLock set so closing."); -// } -// } - return channel.isSuspended() || _sendLock.get(); - } - - /** - * Callback indicating that a queue has been deleted. - * - * @param queue The queue to delete - */ - public void queueDeleted(AMQQueue queue) throws AMQException - { - channel.queueDeleted(queue); - } - - public boolean filtersMessages() - { - return _filters != null || _noLocal; - } - - public boolean hasInterest(QueueEntry entry) - { - //check that the message hasn't been rejected - if (entry.isRejectedBy(this)) - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Subscription:" + debugIdentity() + " rejected message:" + entry.debugIdentity()); - } -// return false; - } - - - - //todo - client id should be recoreded and this test removed but handled below - if (_noLocal) - { - - final AMQProtocolSession publisher = entry.getMessage().getPublisher(); - if(publisher != null) - - { - // We don't want local messages so check to see if message is one we sent - Object localInstance; - Object msgInstance; - - if ((protocolSession.getClientProperties() != null) && - (localInstance = protocolSession.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null) - { - - if ((publisher.getClientProperties() != null) && - (msgInstance = publisher.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null) - { - if (localInstance == msgInstance || localInstance.equals(msgInstance)) - { - // if (_logger.isTraceEnabled()) - // { - // _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" + - // msg.debugIdentity() + ")"); - // } - return false; - } - } - } - else - { - - localInstance = protocolSession.getClientIdentifier(); - //todo - client id should be recoreded and this test removed but handled here - - msgInstance = publisher.getClientIdentifier(); - if (localInstance == msgInstance || ((localInstance != null) && localInstance.equals(msgInstance))) - { - // if (_logger.isTraceEnabled()) - // { - // _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" + - // msg.debugIdentity() + ")"); - // } - return false; - } - } - - } - } - - - return checkFilters(entry); - - } - - private String id = String.valueOf(System.identityHashCode(this)); - - private String debugIdentity() - { - return id; - } - - private boolean checkFilters(QueueEntry msg) - { - return (_filters == null) || _filters.allAllow(msg.getMessage()); - } - - public Queue<QueueEntry> getPreDeliveryQueue() - { - return _messages; - } - - public void enqueueForPreDelivery(QueueEntry msg, boolean deliverFirst) - { - if (_messages != null) - { - if (deliverFirst) - { - _messages.pushHead(msg); - } - else - { - _messages.offer(msg); - } - } - } - - private boolean isAutoClose() - { - return _autoClose; - } - - public void close() - { - boolean closed = false; - synchronized (_sendLock) - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Setting SendLock true:" + debugIdentity()); - } - - closed = _sendLock.getAndSet(true); - } - - if (closed) - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Called close() on a closed subscription"); - } - - return; - } - - if (_logger.isInfoEnabled()) - { - _logger.info("Closing subscription (" + debugIdentity() + "):" + this); - } - - if (_resendQueue != null && !_resendQueue.isEmpty()) - { - if (_logger.isInfoEnabled()) - { - _logger.info("Requeuing closing subscription (" + debugIdentity() + "):" + this); - } - requeue(); - } - - //remove references in PDQ - if (_messages != null) - { - if (_logger.isInfoEnabled()) - { - _logger.info("Clearing PDQ (" + debugIdentity() + "):" + this); - } - - _messages.clear(); - } - } - - private void autoclose() - { - close(); - - if (_autoClose && !_sentClose) - { - _logger.info("Closing autoclose subscription (" + debugIdentity() + "):" + this); - - boolean unregisteredOK = false; - try - { - unregisteredOK = channel.unsubscribeConsumer(protocolSession, consumerTag); - } - catch (AMQException e) - { - // Occurs if we cannot find the subscriber in the channel with protocolSession and consumerTag. - _logger.info("Unable to UnsubscribeConsumer :" + consumerTag +" so not going to send CancelOK."); - } - - if (unregisteredOK) - { - ProtocolOutputConverter converter = protocolSession.getProtocolOutputConverter(); - converter.confirmConsumerAutoClose(channel.getChannelId(), consumerTag); - _sentClose = true; - } - - } - } - - private void requeue() - { - if (_queue != null) - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Requeuing :" + _resendQueue.size() + " messages"); - } - - while (!_resendQueue.isEmpty()) - { - QueueEntry resent = _resendQueue.poll(); - - if (_logger.isDebugEnabled()) - { - _logger.debug("Removed for resending:" + resent.debugIdentity()); - } - - resent.release(); - _queue.subscriberHasPendingResend(false, this, resent); - - try - { - channel.getTransactionalContext().deliver(resent, true); - } - 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."); - } - - _queue.subscriberHasPendingResend(false, this, null); - } - else - { - if (!_resendQueue.isEmpty()) - { - _logger.error("Unable to re-deliver messages as queue is null."); - } - } - - // Clear the messages - _resendQueue = null; - } - - - public boolean isClosed() - { - return _sendLock.get(); // This rather than _close is used to signify the subscriber is now closed. - } - - public boolean isBrowser() - { - return _isBrowser; - } - - public boolean wouldSuspend(QueueEntry msg) - { - return _acks && channel.wouldSuspend(msg.getMessage()); - } - - public Queue<QueueEntry> getResendQueue() - { - if (_resendQueue == null) - { - _resendQueue = new ConcurrentLinkedQueueAtomicSize<QueueEntry>(); - } - return _resendQueue; - } - - - public Queue<QueueEntry> getNextQueue(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; - } - } - - public void addToResendQueue(QueueEntry msg) - { - // add to our resend queue - getResendQueue().add(msg); - - // Mark Queue has having content. - if (_queue == null) - { - _logger.error("Queue is null won't be able to resend messages"); - } - else - { - _queue.subscriberHasPendingResend(true, this, msg); - } - } - - public Object getSendLock() - { - return _sendLock; - } - - public AMQChannel getChannel() - { - return channel; - } - - 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/SubscriptionManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java deleted file mode 100644 index bc17bcca9c..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java +++ /dev/null @@ -1,34 +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; - -/** - * 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 Subscription nextSubscriber(QueueEntry entry); -} 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 882efd380d..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java +++ /dev/null @@ -1,274 +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.concurrent.CopyOnWriteArrayList; - -import org.apache.log4j.Logger; -import org.apache.qpid.AMQException; - -/** Holds a set of subscriptions for a queue and manages the round robin-ing of deliver etc. */ -class SubscriptionSet implements WeightedSubscriptionManager -{ - private static final Logger _log = Logger.getLogger(SubscriptionSet.class); - - /** List of registered subscribers */ - private List<Subscription> _subscriptions = new CopyOnWriteArrayList<Subscription>(); - - /** 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); - } - } - - /** - * 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. - - Subscription sub = null; - synchronized (_changeLock) - { - int subIndex = _subscriptions.indexOf(subscription); - - if (subIndex != -1) - { - //we can't just return the passed in subscription as it is a new object - // and doesn't contain the stored state we need. - //NOTE while this may be removed now anyone with an iterator will still have it in the list!! - sub = _subscriptions.remove(subIndex); - } - else - { - _log.error("Unable to remove from index(" + subIndex + ")subscription:" + subscription); - } - } - if (sub != null) - { - return sub; - } - else - { - 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() || subscription.wouldSuspend(msg))) - { - if (subscription.hasInterest(msg)) - { - // 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. - if (!subscription.filtersMessages() || subscription.getPreDeliveryQueue().isEmpty()) - { - 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(); - ++_currentSubscriber; - subscriberScanned(); - - if (!(subscription.isSuspended() || subscription.wouldSuspend(msg))) - { - if (subscription.hasInterest(msg)) - { - // 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. - if (!subscription.filtersMessages() || subscription.getPreDeliveryQueue().isEmpty()) - { - 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 getWeight() - { - int count = 0; - for (Subscription s : _subscriptions) - { - if (!s.isSuspended()) - { - count++; - } - } - return count; - } - - /** - * 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) throws AMQException - { - 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; - } - -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java b/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java index 373a64e2eb..3ed8b0e55c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java @@ -26,7 +26,6 @@ import java.util.LinkedList; import java.util.List; import org.apache.qpid.AMQException; -import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.framing.abstraction.ContentChunk; @@ -48,29 +47,35 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle private final MessageStore _messageStore; + private final Long _messageId; private long _arrivalTime; - - public WeakReferenceMessageHandle(MessageStore messageStore) + public WeakReferenceMessageHandle(final Long messageId, MessageStore messageStore) { + _messageId = messageId; _messageStore = messageStore; } - public ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException + public ContentHeaderBody getContentHeaderBody(StoreContext context) throws AMQException { ContentHeaderBody chb = (_contentHeaderBody != null ? _contentHeaderBody.get() : null); if (chb == null) { - MessageMetaData mmd = loadMessageMetaData(context, messageId); + MessageMetaData mmd = loadMessageMetaData(context); chb = mmd.getContentHeaderBody(); } return chb; } - private MessageMetaData loadMessageMetaData(StoreContext context, Long messageId) + public Long getMessageId() + { + return _messageId; + } + + private MessageMetaData loadMessageMetaData(StoreContext context) throws AMQException { - MessageMetaData mmd = _messageStore.getMessageMetaData(context, messageId); + MessageMetaData mmd = _messageStore.getMessageMetaData(context, _messageId); populateFromMessageMetaData(mmd); return mmd; } @@ -82,11 +87,11 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle _messagePublishInfo = new WeakReference<MessagePublishInfo>(mmd.getMessagePublishInfo()); } - public int getBodyCount(StoreContext context, Long messageId) throws AMQException + public int getBodyCount(StoreContext context) throws AMQException { if (_contentBodies == null) { - MessageMetaData mmd = _messageStore.getMessageMetaData(context, messageId); + MessageMetaData mmd = _messageStore.getMessageMetaData(context, _messageId); int chunkCount = mmd.getContentChunkCount(); _contentBodies = new ArrayList<WeakReference<ContentChunk>>(chunkCount); for (int i = 0; i < chunkCount; i++) @@ -97,12 +102,12 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle return _contentBodies.size(); } - public long getBodySize(StoreContext context, Long messageId) throws AMQException + public long getBodySize(StoreContext context) throws AMQException { - return getContentHeaderBody(context, messageId).bodySize; + return getContentHeaderBody(context).bodySize; } - public ContentChunk getContentChunk(StoreContext context, Long messageId, int index) throws AMQException, IllegalArgumentException + public ContentChunk getContentChunk(StoreContext context, int index) throws AMQException, IllegalArgumentException { if (index > _contentBodies.size() - 1) { @@ -113,7 +118,7 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle ContentChunk cb = wr.get(); if (cb == null) { - cb = _messageStore.getContentBodyChunk(context, messageId, index); + cb = _messageStore.getContentBodyChunk(context, _messageId, index); _contentBodies.set(index, new WeakReference<ContentChunk>(cb)); } return cb; @@ -123,12 +128,11 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle * Content bodies are set <i>before</i> the publish and header frames * * @param storeContext - * @param messageId * @param contentChunk * @param isLastContentBody * @throws AMQException */ - public void addContentBodyFrame(StoreContext storeContext, Long messageId, ContentChunk contentChunk, boolean isLastContentBody) throws AMQException + public void addContentBodyFrame(StoreContext storeContext, ContentChunk contentChunk, boolean isLastContentBody) throws AMQException { if (_contentBodies == null && isLastContentBody) { @@ -142,16 +146,16 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle } } _contentBodies.add(new WeakReference<ContentChunk>(contentChunk)); - _messageStore.storeContentBodyChunk(storeContext, messageId, _contentBodies.size() - 1, + _messageStore.storeContentBodyChunk(storeContext, _messageId, _contentBodies.size() - 1, contentChunk, isLastContentBody); } - public MessagePublishInfo getMessagePublishInfo(StoreContext context, Long messageId) throws AMQException + public MessagePublishInfo getMessagePublishInfo(StoreContext context) throws AMQException { MessagePublishInfo bpb = (_messagePublishInfo != null ? _messagePublishInfo.get() : null); if (bpb == null) { - MessageMetaData mmd = loadMessageMetaData(context, messageId); + MessageMetaData mmd = loadMessageMetaData(context); bpb = mmd.getMessagePublishInfo(); } @@ -168,12 +172,9 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle _redelivered = redelivered; } - public boolean isPersistent(StoreContext context, Long messageId) throws AMQException + public boolean isPersistent() { - //todo remove literal values to a constant file such as AMQConstants in common - ContentHeaderBody chb = getContentHeaderBody(context, messageId); - return chb.properties instanceof BasicContentHeaderProperties && - ((BasicContentHeaderProperties) chb.properties).getDeliveryMode() == 2; + return true; } /** @@ -183,7 +184,7 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle * @param contentHeaderBody * @throws AMQException */ - public void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, MessagePublishInfo publishBody, + public void setPublishAndContentHeaderBody(StoreContext storeContext, MessagePublishInfo publishBody, ContentHeaderBody contentHeaderBody) throws AMQException { @@ -199,24 +200,15 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle MessageMetaData mmd = new MessageMetaData(publishBody, contentHeaderBody, _contentBodies.size(), arrivalTime); - _messageStore.storeMessageMetaData(storeContext, messageId, mmd); + _messageStore.storeMessageMetaData(storeContext, _messageId, mmd); - populateFromMessageMetaData(mmd); - } - public void removeMessage(StoreContext storeContext, Long messageId) throws AMQException - { - _messageStore.removeMessage(storeContext, messageId); - } - - public void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException - { - _messageStore.enqueueMessage(storeContext, queue.getName(), messageId); + populateFromMessageMetaData(mmd); } - public void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException + public void removeMessage(StoreContext storeContext) throws AMQException { - _messageStore.dequeueMessage(storeContext, queue.getName(), messageId); + _messageStore.removeMessage(storeContext, _messageId); } public long getArrivalTime() diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java deleted file mode 100644 index 6c71571807..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java +++ /dev/null @@ -1,26 +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; - -public interface WeightedSubscriptionManager extends SubscriptionManager -{ - public int getWeight(); -} 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/registry/ConfigurationFileApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java index 748e33ba7a..fef958000a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java @@ -61,11 +61,6 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry private PluginManager _pluginManager; - public ConfigurationFileApplicationRegistry(Configuration configuration) - { - super(configuration); - } - public ConfigurationFileApplicationRegistry(File configurationURL) throws ConfigurationException { super(config(configurationURL)); @@ -81,7 +76,7 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry } } - public static final Configuration config(File url) throws ConfigurationException + private static final Configuration config(File url) throws ConfigurationException { // We have to override the interpolate methods so that // interpolation takes place accross the entirety of the diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java index 5d439a99eb..00757a4f8c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java @@ -1,37 +1,37 @@ -/*
- * 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.security.access;
-
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.server.exchange.Exchange;
-import org.apache.qpid.server.queue.AMQQueue;
-
-public enum Permission
-{
- CONSUME,
- PUBLISH,
- CREATE,
- ACCESS,
- BIND,
- UNBIND,
- DELETE,
- PURGE
-}
+/* + * 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.security.access; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.AMQQueue; + +public enum Permission +{ + CONSUME, + PUBLISH, + CREATE, + ACCESS, + BIND, + UNBIND, + DELETE, + PURGE +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java index 126ff22d69..9b784069dd 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java @@ -28,13 +28,9 @@ import org.apache.qpid.server.security.access.AccessResult; import org.apache.qpid.server.security.access.Accessable; import org.apache.qpid.server.security.access.Permission; import org.apache.commons.configuration.Configuration; -import org.apache.log4j.Logger; public class AllowAll implements ACLPlugin { - - private static final Logger _logger = ACLManager.getLogger(); - public AccessResult authorise(AMQProtocolSession session, Permission permission, AMQMethodBody body, Object... parameters) { if (ACLManager.getLogger().isDebugEnabled()) 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 bd980c696c..cc22569d77 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 @@ -1,23 +1,3 @@ -/* -* -* 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.store; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -25,7 +5,7 @@ import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.MessageMetaData; import org.apache.qpid.server.queue.QueueRegistry; - +import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.AMQMessage; import org.apache.qpid.server.queue.MessageHandleFactory; import org.apache.qpid.server.txn.TransactionalContext; @@ -41,7 +21,6 @@ import org.apache.log4j.Logger; import org.apache.mina.common.ByteBuffer; import java.io.File; -import java.io.ByteArrayInputStream; import java.sql.DriverManager; import java.sql.Driver; import java.sql.Connection; @@ -60,6 +39,26 @@ import java.util.HashMap; import java.util.TreeMap; +/* +* +* 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 DerbyMessageStore implements MessageStore { @@ -68,7 +67,7 @@ public class DerbyMessageStore implements MessageStore private static final String ENVIRONMENT_PATH_PROPERTY = "environment-path"; - private static final String SQL_DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver"; + private static final String DERBY_DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver"; private static final String DB_VERSION_TABLE_NAME = "QPID_DB_VERSION"; @@ -92,39 +91,6 @@ public class DerbyMessageStore implements MessageStore private String _connectionURL; - - private static final String CREATE_DB_VERSION_TABLE = "CREATE TABLE "+DB_VERSION_TABLE_NAME+" ( version int not null )"; - private static final String INSERT_INTO_DB_VERSION = "INSERT INTO "+DB_VERSION_TABLE_NAME+" ( version ) VALUES ( ? )"; - private static final String CREATE_EXCHANGE_TABLE = "CREATE TABLE "+EXCHANGE_TABLE_NAME+" ( name varchar(255) not null, type varchar(255) not null, autodelete SMALLINT not null, PRIMARY KEY ( name ) )"; - private static final String CREATE_QUEUE_TABLE = "CREATE TABLE "+QUEUE_TABLE_NAME+" ( name varchar(255) not null, owner varchar(255), PRIMARY KEY ( name ) )"; - private static final String CREATE_BINDINGS_TABLE = "CREATE TABLE "+BINDINGS_TABLE_NAME+" ( exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255) not null, arguments blob , PRIMARY KEY ( exchange_name, queue_name, binding_key ) )"; - private static final String CREATE_QUEUE_ENTRY_TABLE = "CREATE TABLE "+QUEUE_ENTRY_TABLE_NAME+" ( queue_name varchar(255) not null, message_id bigint not null, PRIMARY KEY (queue_name, message_id) )"; - private static final String CREATE_MESSAGE_META_DATA_TABLE = "CREATE TABLE "+MESSAGE_META_DATA_TABLE_NAME+" ( message_id bigint not null, exchange_name varchar(255) not null, routing_key varchar(255), flag_mandatory smallint not null, flag_immediate smallint not null, content_header blob, chunk_count int not null, PRIMARY KEY ( message_id ) )"; - private static final String CREATE_MESSAGE_CONTENT_TABLE = "CREATE TABLE "+MESSAGE_CONTENT_TABLE_NAME+" ( message_id bigint not null, chunk_id int not null, content_chunk blob , PRIMARY KEY (message_id, chunk_id) )"; - private static final String SELECT_FROM_QUEUE = "SELECT name, owner FROM " + QUEUE_TABLE_NAME; - private static final String SELECT_FROM_EXCHANGE = "SELECT name, type, autodelete FROM " + EXCHANGE_TABLE_NAME; - private static final String SELECT_FROM_BINDINGS = - "SELECT queue_name, binding_key, arguments FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ?"; - private static final String DELETE_FROM_MESSAGE_META_DATA = "DELETE FROM " + MESSAGE_META_DATA_TABLE_NAME + " WHERE message_id = ?"; - private static final String DELETE_FROM_MESSAGE_CONTENT = "DELETE FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ?"; - private static final String INSERT_INTO_EXCHANGE = "INSERT INTO " + EXCHANGE_TABLE_NAME + " ( name, type, autodelete ) VALUES ( ?, ?, ? )"; - private static final String DELETE_FROM_EXCHANGE = "DELETE FROM " + EXCHANGE_TABLE_NAME + " WHERE name = ?"; - private static final String INSERT_INTO_BINDINGS = "INSERT INTO " + BINDINGS_TABLE_NAME + " ( exchange_name, queue_name, binding_key, arguments ) values ( ?, ?, ?, ? )"; - private static final String DELETE_FROM_BINDINGS = "DELETE FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ? AND queue_name = ? AND binding_key = ?"; - private static final String INSERT_INTO_QUEUE = "INSERT INTO " + QUEUE_TABLE_NAME + " (name, owner) VALUES (?, ?)"; - private static final String DELETE_FROM_QUEUE = "DELETE FROM " + QUEUE_TABLE_NAME + " WHERE name = ?"; - private static final String INSERT_INTO_QUEUE_ENTRY = "INSERT INTO " + QUEUE_ENTRY_TABLE_NAME + " (queue_name, message_id) values (?,?)"; - private static final String DELETE_FROM_QUEUE_ENTRY = "DELETE FROM " + QUEUE_ENTRY_TABLE_NAME + " WHERE queue_name = ? AND message_id =?"; - private static final String INSERT_INTO_MESSAGE_CONTENT = "INSERT INTO " + MESSAGE_CONTENT_TABLE_NAME + "( message_id, chunk_id, content_chunk ) values (?, ?, ?)"; - private static final String INSERT_INTO_MESSAGE_META_DATA = "INSERT INTO " + MESSAGE_META_DATA_TABLE_NAME + "( message_id , exchange_name , routing_key , flag_mandatory , flag_immediate , content_header , chunk_count ) values (?, ?, ?, ?, ?, ?, ?)"; - private static final String SELECT_FROM_MESSAGE_META_DATA = - "SELECT exchange_name , routing_key , flag_mandatory , flag_immediate , content_header , chunk_count FROM " + MESSAGE_META_DATA_TABLE_NAME + " WHERE message_id = ?"; - private static final String SELECT_FROM_MESSAGE_CONTENT = - "SELECT content_chunk FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ? and chunk_id = ?"; - private static final String SELECT_FROM_QUEUE_ENTRY = "SELECT queue_name, message_id FROM " + QUEUE_ENTRY_TABLE_NAME; - private static final String TABLE_EXISTANCE_QUERY = "SELECT 1 FROM SYS.SYSTABLES WHERE TABLENAME = ?"; - - private enum State { INITIAL, @@ -163,6 +129,10 @@ public class DerbyMessageStore implements MessageStore createOrOpenDatabase(databasePath); + + + + // this recovers durable queues and persistent messages recover(); @@ -175,7 +145,7 @@ public class DerbyMessageStore implements MessageStore { if(DRIVER_CLASS == null) { - DRIVER_CLASS = (Class<Driver>) Class.forName(SQL_DRIVER_NAME); + DRIVER_CLASS = (Class<Driver>) Class.forName(DERBY_DRIVER_NAME); } } @@ -193,7 +163,7 @@ public class DerbyMessageStore implements MessageStore createMessageMetaDataTable(conn); createMessageContentTable(conn); - conn.close(); + } @@ -204,10 +174,10 @@ public class DerbyMessageStore implements MessageStore { Statement stmt = conn.createStatement(); - stmt.execute(CREATE_DB_VERSION_TABLE); + stmt.execute("CREATE TABLE "+DB_VERSION_TABLE_NAME+" ( version int not null )"); stmt.close(); - PreparedStatement pstmt = conn.prepareStatement(INSERT_INTO_DB_VERSION); + PreparedStatement pstmt = conn.prepareStatement("INSERT INTO "+DB_VERSION_TABLE_NAME+" ( version ) VALUES ( ? )"); pstmt.setInt(1, DB_VERSION); pstmt.execute(); pstmt.close(); @@ -221,8 +191,8 @@ public class DerbyMessageStore implements MessageStore if(!tableExists(EXCHANGE_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); - - stmt.execute(CREATE_EXCHANGE_TABLE); + + stmt.execute("CREATE TABLE "+EXCHANGE_TABLE_NAME+" ( name varchar(255) not null, type varchar(255) not null, autodelete SMALLINT not null, PRIMARY KEY ( name ) )"); stmt.close(); } } @@ -232,7 +202,7 @@ public class DerbyMessageStore implements MessageStore if(!tableExists(QUEUE_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); - stmt.execute(CREATE_QUEUE_TABLE); + stmt.execute("CREATE TABLE "+QUEUE_TABLE_NAME+" ( name varchar(255) not null, owner varchar(255), PRIMARY KEY ( name ) )"); stmt.close(); } } @@ -242,7 +212,8 @@ public class DerbyMessageStore implements MessageStore if(!tableExists(BINDINGS_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); - stmt.execute(CREATE_BINDINGS_TABLE); + stmt.execute("CREATE TABLE "+BINDINGS_TABLE_NAME+" ( exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255) not null, arguments blob , PRIMARY KEY ( exchange_name, queue_name, binding_key ) )"); + stmt.close(); } @@ -254,7 +225,7 @@ public class DerbyMessageStore implements MessageStore if(!tableExists(QUEUE_ENTRY_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); - stmt.execute(CREATE_QUEUE_ENTRY_TABLE); + stmt.execute("CREATE TABLE "+QUEUE_ENTRY_TABLE_NAME+" ( queue_name varchar(255) not null, message_id bigint not null, PRIMARY KEY (queue_name, message_id) )"); stmt.close(); } @@ -266,7 +237,7 @@ public class DerbyMessageStore implements MessageStore if(!tableExists(MESSAGE_META_DATA_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); - stmt.execute(CREATE_MESSAGE_META_DATA_TABLE); + stmt.execute("CREATE TABLE "+MESSAGE_META_DATA_TABLE_NAME+" ( message_id bigint not null, exchange_name varchar(255) not null, routing_key varchar(255), flag_mandatory smallint not null, flag_immediate smallint not null, content_header blob, chunk_count int not null, PRIMARY KEY ( message_id ) )"); stmt.close(); } @@ -279,7 +250,7 @@ public class DerbyMessageStore implements MessageStore if(!tableExists(MESSAGE_CONTENT_TABLE_NAME, conn)) { Statement stmt = conn.createStatement(); - stmt.execute(CREATE_MESSAGE_CONTENT_TABLE); + stmt.execute("CREATE TABLE "+MESSAGE_CONTENT_TABLE_NAME+" ( message_id bigint not null, chunk_id int not null, content_chunk blob , PRIMARY KEY (message_id, chunk_id) )"); stmt.close(); } @@ -290,7 +261,7 @@ public class DerbyMessageStore implements MessageStore private boolean tableExists(final String tableName, final Connection conn) throws SQLException { - PreparedStatement stmt = conn.prepareStatement(TABLE_EXISTANCE_QUERY); + PreparedStatement stmt = conn.prepareStatement("SELECT 1 FROM SYS.SYSTABLES WHERE TABLENAME = ?"); stmt.setString(1, tableName); ResultSet rs = stmt.executeQuery(); boolean exists = rs.next(); @@ -312,6 +283,8 @@ public class DerbyMessageStore implements MessageStore recoverExchanges(); +// + try { @@ -344,14 +317,15 @@ public class DerbyMessageStore implements MessageStore Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(SELECT_FROM_QUEUE); + ResultSet rs = stmt.executeQuery("SELECT name, owner FROM " + QUEUE_TABLE_NAME); Map<AMQShortString, AMQQueue> queueMap = new HashMap<AMQShortString, AMQQueue>(); while(rs.next()) { String queueName = rs.getString(1); String owner = rs.getString(2); AMQShortString queueNameShortString = new AMQShortString(queueName); - AMQQueue q = new AMQQueue(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); @@ -379,7 +353,7 @@ public class DerbyMessageStore implements MessageStore Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(SELECT_FROM_EXCHANGE); + ResultSet rs = stmt.executeQuery("SELECT name, type, autodelete FROM " + EXCHANGE_TABLE_NAME); Exchange exchange; while(rs.next()) @@ -417,7 +391,7 @@ public class DerbyMessageStore implements MessageStore { conn = newConnection(); - PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_BINDINGS); + PreparedStatement stmt = conn.prepareStatement("SELECT queue_name, binding_key, arguments FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ?"); stmt.setString(1, exchange.getName().toString()); ResultSet rs = stmt.executeQuery(); @@ -450,7 +424,7 @@ public class DerbyMessageStore implements MessageStore argumentsFT = new FieldTable(buf,arguments.length()); } - queue.bind(bindingKey == null ? null : new AMQShortString(bindingKey), argumentsFT, exchange); + queue.bind(exchange, bindingKey == null ? null : new AMQShortString(bindingKey), argumentsFT); } } } @@ -465,7 +439,9 @@ public class DerbyMessageStore implements MessageStore public void close() throws Exception { + _closed.getAndSet(true); + } public void removeMessage(StoreContext storeContext, Long messageId) throws AMQException @@ -486,11 +462,12 @@ public class DerbyMessageStore implements MessageStore MessageMetaData mmd = getMessageMetaData(storeContext, messageId); try { - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_MESSAGE_META_DATA); + PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + MESSAGE_META_DATA_TABLE_NAME + " WHERE message_id = ?"); stmt.setLong(1,messageId); wrapper.setRequiresCommit(); int results = stmt.executeUpdate(); + if (results == 0) { if (localTx) @@ -507,7 +484,8 @@ public class DerbyMessageStore implements MessageStore _logger.debug("Deleted metadata for message " + messageId); } - stmt = conn.prepareStatement(DELETE_FROM_MESSAGE_CONTENT); + + stmt = conn.prepareStatement("DELETE FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ?"); stmt.setLong(1,messageId); results = stmt.executeUpdate(); @@ -550,7 +528,7 @@ public class DerbyMessageStore implements MessageStore { conn = newConnection(); - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_EXCHANGE); + PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + EXCHANGE_TABLE_NAME + " ( name, type, autodelete ) VALUES ( ?, ?, ? )"); stmt.setString(1, exchange.getName().toString()); stmt.setString(2, exchange.getType().toString()); stmt.setShort(3, exchange.isAutoDelete() ? (short) 1 : (short) 0); @@ -564,6 +542,7 @@ public class DerbyMessageStore implements MessageStore if(conn != null) { conn.close(); + } } } @@ -582,7 +561,7 @@ public class DerbyMessageStore implements MessageStore try { conn = newConnection(); - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_EXCHANGE); + PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + EXCHANGE_TABLE_NAME + " WHERE name = ?"); stmt.setString(1, exchange.getName().toString()); int results = stmt.executeUpdate(); if(results == 0) @@ -627,20 +606,16 @@ public class DerbyMessageStore implements MessageStore try { conn = newConnection(); - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_BINDINGS); + // exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255), arguments blob + PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + BINDINGS_TABLE_NAME + " ( exchange_name, queue_name, binding_key, arguments ) values ( ?, ?, ?, ? )"); stmt.setString(1, exchange.getName().toString() ); stmt.setString(2, queue.getName().toString()); stmt.setString(3, routingKey == null ? null : routingKey.toString()); if(args != null) { - /* This would be the Java 6 way of setting a Blob Blob blobArgs = conn.createBlob(); blobArgs.setBytes(0, args.getDataAsBytes()); stmt.setBlob(4, blobArgs); - */ - byte[] bytes = args.getDataAsBytes(); - ByteArrayInputStream bis = new ByteArrayInputStream(bytes); - stmt.setBinaryStream(4, bis, bytes.length); } else { @@ -687,7 +662,7 @@ public class DerbyMessageStore implements MessageStore { conn = newConnection(); // exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255), arguments blob - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_BINDINGS); + PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ? AND queue_name = ? AND binding_key = ?"); stmt.setString(1, exchange.getName().toString() ); stmt.setString(2, queue.getName().toString()); stmt.setString(3, routingKey == null ? null : routingKey.toString()); @@ -736,7 +711,7 @@ public class DerbyMessageStore implements MessageStore Connection conn = newConnection(); PreparedStatement stmt = - conn.prepareStatement(INSERT_INTO_QUEUE); + conn.prepareStatement("INSERT INTO " + QUEUE_TABLE_NAME + " (name, owner) VALUES (?, ?)"); stmt.setString(1, queue.getName().toString()); stmt.setString(2, queue.getOwner() == null ? null : queue.getOwner().toString()); @@ -758,13 +733,13 @@ public class DerbyMessageStore implements MessageStore private Connection newConnection() throws SQLException { - final Connection connection = DriverManager.getConnection(_connectionURL); - return connection; + return DriverManager.getConnection(_connectionURL); } - public void removeQueue(AMQShortString name) throws AMQException + public void removeQueue(final AMQQueue queue) throws AMQException { + AMQShortString name = queue.getName(); _logger.debug("public void removeQueue(AMQShortString name = " + name + "): called"); Connection conn = null; @@ -772,7 +747,7 @@ public class DerbyMessageStore implements MessageStore try { conn = newConnection(); - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_QUEUE); + PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + QUEUE_TABLE_NAME + " WHERE name = ?"); stmt.setString(1, name.toString()); int results = stmt.executeUpdate(); @@ -808,16 +783,17 @@ public class DerbyMessageStore implements MessageStore } - public void enqueueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException + public void enqueueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException { boolean localTx = getOrCreateTransaction(context); Connection conn = getConnection(context); ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload(); + AMQShortString name = queue.getName(); try { - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_QUEUE_ENTRY); + PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + QUEUE_ENTRY_TABLE_NAME + " (queue_name, message_id) values (?,?)"); stmt.setString(1,name.toString()); stmt.setLong(2,messageId); stmt.executeUpdate(); @@ -848,16 +824,17 @@ public class DerbyMessageStore implements MessageStore } - public void dequeueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException + public void dequeueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException { + AMQShortString name = queue.getName(); boolean localTx = getOrCreateTransaction(context); Connection conn = getConnection(context); ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload(); try { - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_QUEUE_ENTRY); + PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + QUEUE_ENTRY_TABLE_NAME + " WHERE queue_name = ? AND message_id =?"); stmt.setString(1,name.toString()); stmt.setLong(2,messageId); int results = stmt.executeUpdate(); @@ -954,18 +931,17 @@ public class DerbyMessageStore implements MessageStore try { - Connection conn = connWrapper.getConnection(); if(connWrapper.requiresCommit()) { + Connection conn = connWrapper.getConnection(); conn.commit(); if (_logger.isDebugEnabled()) { _logger.debug("commit tran completed"); } - + conn.close(); } - conn.close(); } catch (SQLException e) { @@ -1026,25 +1002,21 @@ public class DerbyMessageStore implements MessageStore int index, ContentChunk contentBody, boolean lastContentBody) throws AMQException - { + { boolean localTx = getOrCreateTransaction(context); Connection conn = getConnection(context); ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload(); try { - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_CONTENT); + PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + MESSAGE_CONTENT_TABLE_NAME + "( message_id, chunk_id, content_chunk ) values (?, ?, ?)"); stmt.setLong(1,messageId); stmt.setInt(2, index); byte[] chunkData = new byte[contentBody.getSize()]; contentBody.getData().duplicate().get(chunkData); - /* this would be the Java 6 way of doing things Blob dataAsBlob = conn.createBlob(); dataAsBlob.setBytes(1L, chunkData); stmt.setBlob(3, dataAsBlob); - */ - ByteArrayInputStream bis = new ByteArrayInputStream(chunkData); - stmt.setBinaryStream(3, bis, chunkData.length); stmt.executeUpdate(); connWrapper.requiresCommit(); @@ -1076,7 +1048,7 @@ public class DerbyMessageStore implements MessageStore try { - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_META_DATA); + PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + MESSAGE_META_DATA_TABLE_NAME + "( message_id , exchange_name , routing_key , flag_mandatory , flag_immediate , content_header , chunk_count ) values (?, ?, ?, ?, ?, ?, ?)"); stmt.setLong(1,messageId); stmt.setString(2, mmd.getMessagePublishInfo().getExchange().toString()); stmt.setString(3, mmd.getMessagePublishInfo().getRoutingKey().toString()); @@ -1088,13 +1060,9 @@ public class DerbyMessageStore implements MessageStore byte[] underlying = new byte[bodySize]; ByteBuffer buf = ByteBuffer.wrap(underlying); headerBody.writePayload(buf); -/* Blob dataAsBlob = conn.createBlob(); dataAsBlob.setBytes(1L, underlying); stmt.setBlob(6, dataAsBlob); -*/ - ByteArrayInputStream bis = new ByteArrayInputStream(underlying); - stmt.setBinaryStream(6,bis,underlying.length); stmt.setInt(7, mmd.getContentChunkCount()); @@ -1128,7 +1096,7 @@ public class DerbyMessageStore implements MessageStore try { - PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_META_DATA); + PreparedStatement stmt = conn.prepareStatement("SELECT exchange_name , routing_key , flag_mandatory , flag_immediate , content_header , chunk_count FROM " + MESSAGE_META_DATA_TABLE_NAME + " WHERE message_id = ?"); stmt.setLong(1,messageId); ResultSet rs = stmt.executeQuery(); @@ -1213,7 +1181,7 @@ public class DerbyMessageStore implements MessageStore try { - PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_CONTENT); + PreparedStatement stmt = conn.prepareStatement("SELECT content_chunk FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ? and chunk_id = ?"); stmt.setLong(1,messageId); stmt.setInt(2, index); ResultSet rs = stmt.executeQuery(); @@ -1300,7 +1268,7 @@ public class DerbyMessageStore implements MessageStore public void process() throws AMQException { - _queue.process(_context, _queue.createEntry(_message), false); + _queue.enqueue(_context, _message); } } @@ -1335,7 +1303,7 @@ public class DerbyMessageStore implements MessageStore TransactionalContext txnContext = new NonTransactionalContext(this, new StoreContext(), null, null); Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(SELECT_FROM_QUEUE_ENTRY); + ResultSet rs = stmt.executeQuery("SELECT queue_name, message_id FROM " + QUEUE_ENTRY_TABLE_NAME); while (rs.next()) @@ -1349,7 +1317,7 @@ public class DerbyMessageStore implements MessageStore AMQQueue queue = queues.get(queueName); if (queue == null) { - queue = new AMQQueue(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 7a6e0b011f..b02eff957e 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,9 +26,9 @@ 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.MessageMetaData; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.MessageMetaData; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.ArrayList; @@ -126,17 +126,17 @@ public class MemoryMessageStore implements MessageStore // Not required to do anything } - public void removeQueue(AMQShortString name) throws AMQException + public void removeQueue(final AMQQueue queue) throws AMQException { // Not required to do anything } - public void enqueueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException + public void enqueueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException { // Not required to do anything } - public void dequeueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException + public void dequeueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException { // Not required to do anything } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java index 2a83d9b649..e15e69a414 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java @@ -27,8 +27,8 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.abstraction.ContentChunk; import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.MessageMetaData; +import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.virtualhost.VirtualHost; /** @@ -138,33 +138,30 @@ public interface MessageStore /** * Removes the specified queue from the persistent store. * - * @param name The queue to remove. - * + * @param queue The queue to remove. * @throws AMQException If the operation fails for any reason. */ - void removeQueue(AMQShortString name) throws AMQException; + void removeQueue(final AMQQueue queue) throws AMQException; /** * Places a message onto a specified queue, in a given transactional context. * * @param context The transactional context for the operation. - * @param name The name of the queue to place the message on. + * @param queue The queue to place the message on. * @param messageId The message to enqueue. - * * @throws AMQException If the operation fails for any reason. */ - void enqueueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException; + void enqueueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException; /** * Extracts a message from a specified queue, in a given transactional context. * * @param context The transactional context for the operation. - * @param name The name of the queue to take the message from. + * @param queue The queue to place the message on. * @param messageId The message to dequeue. - * * @throws AMQException If the operation fails for any reason, or if the specified message does not exist. */ - void dequeueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException; + void dequeueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException; /** * Begins a transactional context. 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/queue/Subscription.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java index 96ce6743ec..408defe453 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java @@ -18,46 +18,77 @@ * under the License. * */ -package org.apache.qpid.server.queue; - -import java.util.Queue; +package org.apache.qpid.server.subscription; import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueEntry; public interface Subscription { - void send(QueueEntry msg, AMQQueue queue) throws AMQException; - boolean isSuspended(); - void queueDeleted(AMQQueue queue) throws AMQException; + public static enum State + { + ACTIVE, + SUSPENDED, + CLOSED + } - boolean filtersMessages(); + public static interface StateListener + { + public void stateChange(Subscription sub, State oldState, State newState); + } - boolean hasInterest(QueueEntry msg); + AMQQueue getQueue(); - Queue<QueueEntry> getPreDeliveryQueue(); + QueueEntry.SubscriptionAcquiredState getOwningState(); - Queue<QueueEntry> getResendQueue(); + void setQueue(AMQQueue queue); - Queue<QueueEntry> getNextQueue(Queue<QueueEntry> messages); + AMQChannel getChannel(); - void enqueueForPreDelivery(QueueEntry msg, boolean deliverFirst); + AMQShortString getConsumerTag(); - void close(); + boolean isSuspended(); + + boolean hasInterest(QueueEntry msg); + + boolean isAutoClose(); boolean isClosed(); boolean isBrowser(); + void close(); + + boolean filtersMessages(); + + void send(QueueEntry msg) throws AMQException; + + void queueDeleted(AMQQueue queue); + + boolean wouldSuspend(QueueEntry msg); - void addToResendQueue(QueueEntry msg); + void getSendLock(); + void releaseSendLock(); + + void resend(final QueueEntry entry) throws AMQException; + + void restoreCredit(final QueueEntry queueEntry); + + void setStateListener(final StateListener listener); + + QueueEntry getLastSeenEntry(); + + boolean setLastSeenEntry(QueueEntry expected, QueueEntry newValue); + + + boolean isActive(); - Object getSendLock(); - AMQChannel getChannel(); - void start(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java index 917f7c4e97..ce0362d73f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java @@ -18,12 +18,15 @@ * under the License. * */ -package org.apache.qpid.server.queue; +package org.apache.qpid.server.subscription; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; 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 @@ -34,10 +37,23 @@ import org.apache.qpid.server.protocol.AMQProtocolSession; */ public interface SubscriptionFactory { - Subscription createSubscription(int channel, AMQProtocolSession protocolSession, AMQShortString consumerTag, boolean acks, - FieldTable filters, boolean noLocal, AMQQueue queue) throws AMQException; + Subscription createSubscription(int channel, + AMQProtocolSession protocolSession, + AMQShortString consumerTag, + boolean acks, + FieldTable filters, + boolean noLocal, FlowCreditManager creditManager) throws AMQException; - Subscription createSubscription(int channel, AMQProtocolSession protocolSession, AMQShortString consumerTag) + 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 new file mode 100644 index 0000000000..5badbad642 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java @@ -0,0 +1,103 @@ +/* + * + * 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.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 +{ + + /* private SubscriptionFactoryImpl() + { + + }*/ + + 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) + { + Boolean isBrowserObj = (Boolean) filters.get(AMQPFilterTypes.NO_CONSUME.getValue()); + isBrowser = (isBrowserObj != null) && isBrowserObj.booleanValue(); + } + else + { + isBrowser = false; + } + + if(isBrowser) + { + 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, clientMethod, recordMethod); + } + else + { + 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 new file mode 100644 index 0000000000..556b87590c --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -0,0 +1,605 @@ +/* + * + * 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 java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.common.ClientProperties; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.filter.FilterManager; +import org.apache.qpid.server.filter.FilterManagerFactory; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.store.StoreContext; + +/** + * Encapsulation of a supscription to a queue. <p/> Ties together the protocol session of a subscriber, the consumer tag + * that was given out by the broker and the channel id. <p/> + */ +public abstract class SubscriptionImpl implements Subscription, FlowCreditManager.FlowCreditManagerListener +{ + + private StateListener _stateListener = new StateListener() + { + + public void stateChange(Subscription sub, State oldState, State newState) + { + + } + }; + + + private final AtomicReference<State> _state = new AtomicReference<State>(State.ACTIVE); + 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); + private final Lock _stateChangeLock; + + static final class BrowserSubscription extends SubscriptionImpl + { + public BrowserSubscription(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 isBrowser() + { + return true; + } + + /** + * This method can be called by each of the publisher threads. As a result all changes to the channel object must be + * thread safe. + * + * @param msg The message to send + * @throws AMQException + */ + public void send(QueueEntry msg) throws AMQException + { + // We don't decrement the reference here as we don't want to consume the message + // but we do want to send it to the client. + + synchronized (getChannel()) + { + long deliveryTag = getChannel().getNextDeliveryTag(); + sendToClient(msg, deliveryTag); + } + + } + } + + public static class NoAckSubscription extends SubscriptionImpl + { + public NoAckSubscription(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 isBrowser() + { + return false; + } + + /** + * This method can be called by each of the publisher threads. As a result all changes to the channel object must be + * thread safe. + * + * @param entry The message to send + * @throws AMQException + */ + 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. + + // 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. + entry.dequeue(storeContext); + + + synchronized (getChannel()) + { + long deliveryTag = getChannel().getNextDeliveryTag(); + + sendToClient(entry, deliveryTag); + + } + entry.dispose(storeContext); + } + finally + { + //Only set delivered if it actually was writen successfully.. + // using a try->finally would set it even if an error occured. + // Is this what we want? + + entry.setDeliveredToSubscription(); + } + } + + public boolean wouldSuspend(QueueEntry msg) + { + return false; + } + + } + + static final class AckSubscription extends SubscriptionImpl + { + public AckSubscription(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 isBrowser() + { + return false; + } + + + /** + * This method can be called by each of the publisher threads. As a result all changes to the channel object must be + * thread safe. + * + * @param entry The message to send + * @throws AMQException + */ + public void send(QueueEntry entry) throws AMQException + { + + 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. + + synchronized (getChannel()) + { + long deliveryTag = getChannel().getNextDeliveryTag(); + + + recordMessageDelivery(entry, deliveryTag); + sendToClient(entry, deliveryTag); + + + } + } + finally + { + //Only set delivered if it actually was writen successfully.. + // using a try->finally would set it even if an error occured. + // Is this what we want? + + entry.setDeliveredToSubscription(); + } + } + + + } + + + private static final Logger _logger = Logger.getLogger(SubscriptionImpl.class); + + private final AMQChannel _channel; + + private final AMQShortString _consumerTag; + + + private final boolean _noLocal; + + private final FlowCreditManager _creditManager; + + private FilterManager _filters; + + private final Boolean _autoClose; + + + private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString(); + + private AMQQueue _queue; + private final AtomicBoolean _deleted = new AtomicBoolean(false); + + + + + public SubscriptionImpl(AMQChannel channel , AMQProtocolSession protocolSession, + AMQShortString consumerTag, FieldTable arguments, + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + + _channel = channel; + _consumerTag = consumerTag; + + _creditManager = creditManager; + creditManager.addStateListener(this); + + _noLocal = noLocal; + + + _filters = FilterManagerFactory.createManager(arguments); + + _deliveryMethod = deliveryMethod; + _recordMethod = recordMethod; + + + _stateChangeLock = new ReentrantLock(); + + + if (arguments != null) + { + Object autoClose = arguments.get(AMQPFilterTypes.AUTO_CLOSE.getValue()); + if (autoClose != null) + { + _autoClose = (Boolean) autoClose; + } + else + { + _autoClose = false; + } + } + else + { + _autoClose = false; + } + + + } + + + + public synchronized void setQueue(AMQQueue queue) + { + if(getQueue() != null) + { + throw new IllegalStateException("Attempt to set queue for subscription " + this + " to " + queue + "when already set to " + getQueue()); + } + _queue = queue; + } + + public String toString() + { + String subscriber = "[channel=" + _channel + + ", consumerTag=" + _consumerTag + + ", session=" + getProtocolSession().getKey() ; + + return subscriber + "]"; + } + + /** + * This method can be called by each of the publisher threads. As a result all changes to the channel object must be + * thread safe. + * + * @param msg The message to send + * @throws AMQException + */ + abstract public void send(QueueEntry msg) throws AMQException; + + + public boolean isSuspended() + { + return !isActive() || _channel.isSuspended() || _deleted.get(); + } + + /** + * Callback indicating that a queue has been deleted. + * + * @param queue The queue to delete + */ + public void queueDeleted(AMQQueue queue) + { + _deleted.set(true); +// _channel.queueDeleted(queue); + } + + public boolean filtersMessages() + { + return _filters != null || _noLocal; + } + + public boolean hasInterest(QueueEntry entry) + { + //check that the message hasn't been rejected + if (entry.isRejectedBy(this)) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Subscription:" + debugIdentity() + " rejected message:" + entry.debugIdentity()); + } +// return false; + } + + + + //todo - client id should be recoreded and this test removed but handled below + if (_noLocal) + { + final Object publisherId = entry.getMessage().getPublisherClientInstance(); + + // We don't want local messages so check to see if message is one we sent + Object localInstance; + + if (publisherId != null && (getProtocolSession().getClientProperties() != null) && + (localInstance = getProtocolSession().getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null) + { + if(publisherId.equals(localInstance)) + { + return false; + } + } + else + { + + localInstance = getProtocolSession().getClientIdentifier(); + //todo - client id should be recoreded and this test removed but handled here + + + if (localInstance != null && localInstance.equals(entry.getMessage().getPublisherIdentifier())) + { + return false; + } + } + + + } + + + if (_logger.isDebugEnabled()) + { + _logger.debug("(" + debugIdentity() + ") checking filters for message (" + entry.debugIdentity()); + } + return checkFilters(entry); + + } + + private String id = String.valueOf(System.identityHashCode(this)); + + private String debugIdentity() + { + return id; + } + + private boolean checkFilters(QueueEntry msg) + { + return (_filters == null) || _filters.allAllow(msg.getMessage()); + } + + public boolean isAutoClose() + { + return _autoClose; + } + + public FlowCreditManager getCreditManager() + { + return _creditManager; + } + + + public void close() + { + boolean closed = false; + State state = getState(); + + _stateChangeLock.lock(); + try + { + while(!closed && state != State.CLOSED) + { + closed = _state.compareAndSet(state, State.CLOSED); + if(!closed) + { + state = getState(); + } + else + { + _stateListener.stateChange(this,state, State.CLOSED); + } + } + _creditManager.removeListener(this); + } + finally + { + _stateChangeLock.unlock(); + } + + + if (closed) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Called close() on a closed subscription"); + } + + return; + } + + if (_logger.isInfoEnabled()) + { + _logger.info("Closing subscription (" + debugIdentity() + "):" + this); + } + } + + public boolean isClosed() + { + return getState() == State.CLOSED; + } + + + public boolean wouldSuspend(QueueEntry msg) + { + return !_creditManager.useCreditForMessage(msg.getMessage());//_channel.wouldSuspend(msg.getMessage()); + } + + public void getSendLock() + { + _stateChangeLock.lock(); + } + + public void releaseSendLock() + { + _stateChangeLock.unlock(); + } + + public void resend(final QueueEntry entry) throws AMQException + { + _queue.resend(entry, this); + } + + public AMQChannel getChannel() + { + return _channel; + } + + public AMQShortString getConsumerTag() + { + return _consumerTag; + } + + public AMQProtocolSession getProtocolSession() + { + return _channel.getProtocolSession(); + } + + public AMQQueue getQueue() + { + return _queue; + } + + public void restoreCredit(final QueueEntry queueEntry) + { + _creditManager.addCredit(1, queueEntry.getSize()); + } + + + public void creditStateChanged(boolean hasCredit) + { + + if(hasCredit) + { + if(_state.compareAndSet(State.SUSPENDED, State.ACTIVE)) + { + _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 + { + if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) + { + _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); + } + } + } + + public State getState() + { + return _state.get(); + } + + + public void setStateListener(final StateListener listener) + { + _stateListener = listener; + } + + + public QueueEntry getLastSeenEntry() + { + return _queueContext.get(); + } + + 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/subscription/SubscriptionList.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java new file mode 100644 index 0000000000..3fbb6bfa4a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java @@ -0,0 +1,247 @@ +/* +* +* 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.AMQQueue; +import org.apache.qpid.server.subscription.Subscription; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.nio.ByteBuffer; + +public class SubscriptionList +{ + + private final SubscriptionNode _head = new SubscriptionNode(); + + private AtomicReference<SubscriptionNode> _tail = new AtomicReference<SubscriptionNode>(_head); + private final AMQQueue _queue; + private AtomicInteger _size = new AtomicInteger(); + + + public final class SubscriptionNode + { + private final AtomicBoolean _deleted = new AtomicBoolean(); + private final AtomicReference<SubscriptionNode> _next = new AtomicReference<SubscriptionNode>(); + private final Subscription _sub; + + + public SubscriptionNode() + { + + _sub = null; + _deleted.set(true); + } + + public SubscriptionNode(final Subscription sub) + { + _sub = sub; + } + + + public SubscriptionNode getNext() + { + + SubscriptionNode next = nextNode(); + while(next != null && next.isDeleted()) + { + + final SubscriptionNode newNext = next.nextNode(); + if(newNext != null) + { + _next.compareAndSet(next, newNext); + next = nextNode(); + } + else + { + next = null; + } + + } + return next; + } + + private SubscriptionNode nextNode() + { + return _next.get(); + } + + public boolean isDeleted() + { + return _deleted.get(); + } + + + public boolean delete() + { + if(_deleted.compareAndSet(false,true)) + { + _size.decrementAndGet(); + advanceHead(); + return true; + } + else + { + return false; + } + } + + + public Subscription getSubscription() + { + return _sub; + } + } + + + public SubscriptionList(AMQQueue queue) + { + _queue = queue; + } + + private void advanceHead() + { + SubscriptionNode head = _head.nextNode(); + while(head._next.get() != null && head.isDeleted()) + { + + final SubscriptionNode newhead = head.nextNode(); + if(newhead != null) + { + _head._next.compareAndSet(head, newhead); + } + head = _head.nextNode(); + } + } + + + public SubscriptionNode add(Subscription sub) + { + SubscriptionNode node = new SubscriptionNode(sub); + for (;;) + { + SubscriptionNode tail = _tail.get(); + SubscriptionNode next = tail.nextNode(); + if (tail == _tail.get()) + { + if (next == null) + { + if (tail._next.compareAndSet(null, node)) + { + _tail.compareAndSet(tail, node); + _size.incrementAndGet(); + return node; + } + } + else + { + _tail.compareAndSet(tail, next); + } + } + } + + } + + public boolean remove(Subscription sub) + { + SubscriptionNode node = _head.getNext(); + while(node != null) + { + if(sub.equals(node._sub) && node.delete()) + { + return true; + } + node = node.getNext(); + } + return false; + } + + + public class SubscriptionNodeIterator + { + + private SubscriptionNode _lastNode; + + SubscriptionNodeIterator(SubscriptionNode startNode) + { + _lastNode = startNode; + } + + + public boolean atTail() + { + return _lastNode.nextNode() == null; + } + + public SubscriptionNode getNode() + { + + return _lastNode; + + } + + public boolean advance() + { + + if(!atTail()) + { + SubscriptionNode nextNode = _lastNode.nextNode(); + while(nextNode.isDeleted() && nextNode.nextNode() != null) + { + nextNode = nextNode.nextNode(); + } + _lastNode = nextNode; + return true; + + } + else + { + return false; + } + + } + + } + + + public SubscriptionNodeIterator iterator() + { + return new SubscriptionNodeIterator(_head); + } + + + public SubscriptionNode getHead() + { + return _head; + } + + public int size() + { + return _size.get(); + } + + + +} + + + diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java index 23aaf56876..b67bb98e28 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java @@ -97,6 +97,10 @@ public class ConnectorConfiguration defaultValue = "false") public boolean _multiThreadNIO; + @Configured(path = "advanced.useWriteBiasedPool", + defaultValue = "false") + public boolean useBiasedWrites; + public IoAcceptor createAcceptor() { diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java b/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java deleted file mode 100644 index 988f589339..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java +++ /dev/null @@ -1,77 +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.txn; - -import org.apache.log4j.Logger; - -import org.apache.qpid.AMQException; -import org.apache.qpid.server.RequiredDeliveryException; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.NoConsumersException; -import org.apache.qpid.server.store.StoreContext; - -import java.util.List; - -/** - * @author Apache Software Foundation - */ -public class CleanupMessageOperation implements TxnOp -{ - private static final Logger _log = Logger.getLogger(CleanupMessageOperation.class); - - private final AMQMessage _msg; - - private final List<RequiredDeliveryException> _returns; - - public CleanupMessageOperation(AMQMessage msg, List<RequiredDeliveryException> returns) - { - _msg = msg; - _returns = returns; - } - - public void prepare(StoreContext context) throws AMQException - { } - - public void undoPrepare() - { - // don't need to do anything here, if the store's txn failed - // when processing prepare then the message was not stored - // or enqueued on any queues and can be discarded - } - - public void commit(StoreContext context) - { - // No-op can't be done here has this is before the message has been attempted to be delivered. - /*try - { - _msg.checkDeliveredToConsumer(); - } - catch (NoConsumersException e) - { - _returns.add(e); - }*/ - } - - public void rollback(StoreContext context) - { - // NO OP - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java b/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java index 2307b94566..ad8303ec5d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java @@ -24,18 +24,16 @@ import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.ack.TxAck; import org.apache.qpid.server.ack.UnacknowledgedMessageMap; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.NoConsumersException; -import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.*; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.StoreContext; -import java.util.LinkedList; import java.util.List; +import java.util.ArrayList; /** A transactional context that only supports local transactions. */ public class LocalTransactionalContext implements TransactionalContext @@ -44,7 +42,7 @@ public class LocalTransactionalContext implements TransactionalContext private final TxnBuffer _txnBuffer = new TxnBuffer(); - private final List<DeliveryDetails> _postCommitDeliveryList = new LinkedList<DeliveryDetails>(); + private final List<DeliveryAction> _postCommitDeliveryList = new ArrayList<DeliveryAction>(); /** * We keep hold of the ack operation so that we can consolidate acks, i.e. multiple acks within a txn are @@ -52,81 +50,112 @@ public class LocalTransactionalContext implements TransactionalContext */ private TxAck _ackOp; - private List<RequiredDeliveryException> _returnMessages; - - private final MessageStore _messageStore; - - private final StoreContext _storeContext; - private boolean _inTran = false; /** Are there messages to deliver. NOT Has the message been delivered */ private boolean _messageDelivered = false; + private final AMQChannel _channel; - private static class DeliveryDetails + + private abstract class DeliveryAction { - public QueueEntry entry; - private boolean deliverFirst; + abstract public void process() throws AMQException; - public DeliveryDetails(QueueEntry entry, boolean deliverFirst) + } + + private class RequeueAction extends DeliveryAction + { + public QueueEntry entry; + + public RequeueAction(QueueEntry entry) { this.entry = entry; - this.deliverFirst = deliverFirst; + } + + public void process() throws AMQException + { + entry.requeue(getStoreContext()); + } + } + + private class PublishAction extends DeliveryAction + { + private final AMQQueue _queue; + private final AMQMessage _message; + + public PublishAction(final AMQQueue queue, final AMQMessage message) + { + _queue = queue; + _message = message; + } + + public void process() throws AMQException + { + + QueueEntry entry = _queue.enqueue(getStoreContext(),_message); + + if(entry.immediateAndNotDelivered()) + { + getReturnMessages().add(new NoConsumersException(_message)); + } } } - public LocalTransactionalContext(MessageStore messageStore, StoreContext storeContext, - List<RequiredDeliveryException> returnMessages) + public LocalTransactionalContext(final AMQChannel channel) { - _messageStore = messageStore; - _storeContext = storeContext; - _returnMessages = returnMessages; - // _txnBuffer.enlist(new StoreMessageOperation(messageStore)); + _channel = channel; } public StoreContext getStoreContext() { - return _storeContext; + return _channel.getStoreContext(); } + public List<RequiredDeliveryException> getReturnMessages() + { + return _channel.getReturnMessages(); + } + + public MessageStore getMessageStore() + { + return _channel.getMessageStore(); + } + + public void rollback() throws AMQException { - _txnBuffer.rollback(_storeContext); + _txnBuffer.rollback(getStoreContext()); // Hack to deal with uncommitted non-transactional writes - if (_messageStore.inTran(_storeContext)) + if (getMessageStore().inTran(getStoreContext())) { - _messageStore.abortTran(_storeContext); + getMessageStore().abortTran(getStoreContext()); _inTran = false; } _postCommitDeliveryList.clear(); } - public void deliver(QueueEntry entry, boolean deliverFirst) throws AMQException + public void deliver(final AMQQueue queue, AMQMessage message) throws AMQException { // A publication will result in the enlisting of several // TxnOps. The first is an op that will store the message. // Following that (and ordering is important), an op will // be added for every queue onto which the message is - // enqueued. Finally a cleanup op will be added to decrement - // the reference associated with the routing. - // message.incrementReference(); - _postCommitDeliveryList.add(new DeliveryDetails(entry, deliverFirst)); + // enqueued. + _postCommitDeliveryList.add(new PublishAction(queue, message)); _messageDelivered = true; - _txnBuffer.enlist(new CleanupMessageOperation(entry.getMessage(), _returnMessages)); - /*_txnBuffer.enlist(new DeliverMessageOperation(message, queue)); - if (_log.isDebugEnabled()) - { - _log.debug("Incrementing ref count on message and enlisting cleanup operation - id " + - message.getMessageId()); - } - message.incrementReference(); + + } + + public void requeue(QueueEntry entry) throws AMQException + { + _postCommitDeliveryList.add(new RequeueAction(entry)); _messageDelivered = true; - */ } + private void checkAck(long deliveryTag, UnacknowledgedMessageMap unacknowledgedMessageMap) throws AMQException { if (!unacknowledgedMessageMap.contains(deliveryTag)) @@ -147,10 +176,8 @@ public class LocalTransactionalContext implements TransactionalContext // as new acks come in. If this is the first ack in the txn // we will need to create and enlist the op. if (_ackOp == null) - { - + { _ackOp = new TxAck(unacknowledgedMessageMap); - _txnBuffer.enlist(_ackOp); } // update the op to include this ack request @@ -189,7 +216,7 @@ public class LocalTransactionalContext implements TransactionalContext _log.debug("Starting transaction on message store: " + this); } - _messageStore.beginTran(_storeContext); + getMessageStore().beginTran(getStoreContext()); _inTran = true; } } @@ -212,22 +239,22 @@ public class LocalTransactionalContext implements TransactionalContext if (_messageDelivered && _inTran) { - _txnBuffer.enlist(new StoreMessageOperation(_messageStore)); + _txnBuffer.enlist(new StoreMessageOperation(getMessageStore())); } // fixme fail commit here ... QPID-440 try { - _txnBuffer.commit(_storeContext); + _txnBuffer.commit(getStoreContext()); } finally { _messageDelivered = false; - _inTran = _messageStore.inTran(_storeContext); + _inTran = getMessageStore().inTran(getStoreContext()); } try { - postCommitDelivery(_returnMessages); + postCommitDelivery(); } catch (AMQException e) { @@ -236,7 +263,7 @@ public class LocalTransactionalContext implements TransactionalContext } } - private void postCommitDelivery(List<RequiredDeliveryException> returnMessages) throws AMQException + private void postCommitDelivery() throws AMQException { if (_log.isDebugEnabled()) { @@ -245,18 +272,9 @@ public class LocalTransactionalContext implements TransactionalContext try { - for (DeliveryDetails dd : _postCommitDeliveryList) + for (DeliveryAction dd : _postCommitDeliveryList) { - dd.entry.process(_storeContext, dd.deliverFirst); - - try - { - dd.entry.checkDeliveredToConsumer(); - } - catch (NoConsumersException nce) - { - returnMessages.add(nce); - } + dd.process(); } } finally diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java b/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java index cac3489f4c..18f1836185 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java @@ -22,19 +22,14 @@ package org.apache.qpid.server.txn; import java.util.LinkedList; import java.util.List; -import java.util.Set; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.RequiredDeliveryException; -import org.apache.qpid.server.ack.UnacknowledgedMessage; import org.apache.qpid.server.ack.UnacknowledgedMessageMap; import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.server.queue.AMQMessage; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.NoConsumersException; -import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.*; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.StoreContext; @@ -49,6 +44,8 @@ public class NonTransactionalContext implements TransactionalContext /** Where to put undeliverable messages */ private final List<RequiredDeliveryException> _returnMessages; + + private final MessageStore _messageStore; private final StoreContext _storeContext; @@ -57,12 +54,6 @@ public class NonTransactionalContext implements TransactionalContext private boolean _inTran; public NonTransactionalContext(MessageStore messageStore, StoreContext storeContext, AMQChannel channel, - List<RequiredDeliveryException> returnMessages, Set<Long> browsedAcks) - { - this(messageStore,storeContext,channel,returnMessages); - } - - public NonTransactionalContext(MessageStore messageStore, StoreContext storeContext, AMQChannel channel, List<RequiredDeliveryException> returnMessages) { _channel = channel; @@ -97,19 +88,22 @@ public class NonTransactionalContext implements TransactionalContext // Does not apply to this context } - public void deliver(QueueEntry entry, boolean deliverFirst) throws AMQException + public void deliver(final AMQQueue queue, AMQMessage message) throws AMQException { - try + QueueEntry entry = queue.enqueue(_storeContext, message); + + //following check implements the functionality + //required by the 'immediate' flag: + if(entry.immediateAndNotDelivered()) { - entry.process(_storeContext, deliverFirst); - //following check implements the functionality - //required by the 'immediate' flag: - entry.checkDeliveredToConsumer(); - } - catch (NoConsumersException e) - { - _returnMessages.add(e); + _returnMessages.add(new NoConsumersException(entry.getMessage())); } + + } + + public void requeue(QueueEntry entry) throws AMQException + { + entry.requeue(_storeContext); } public void acknowledgeMessage(final long deliveryTag, long lastDeliveryTag, @@ -118,7 +112,7 @@ public class NonTransactionalContext implements TransactionalContext { final boolean debug = _log.isDebugEnabled(); - + ; if (multiple) { if (deliveryTag == 0) @@ -130,7 +124,7 @@ public class NonTransactionalContext implements TransactionalContext unacknowledgedMessageMap.size()); unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor() { - public boolean callback(UnacknowledgedMessage message) throws AMQException + public boolean callback(final long deliveryTag, QueueEntry message) throws AMQException { if (debug) { @@ -140,6 +134,7 @@ public class NonTransactionalContext implements TransactionalContext { beginTranIfNecessary(); } + message.restoreCredit(); //Message has been ack so discard it. This will dequeue and decrement the reference. message.discard(_storeContext); @@ -159,27 +154,27 @@ public class NonTransactionalContext implements TransactionalContext throw new AMQException("Multiple ack on delivery tag " + deliveryTag + " not known for channel"); } - LinkedList<UnacknowledgedMessage> acked = new LinkedList<UnacknowledgedMessage>(); + LinkedList<QueueEntry> acked = new LinkedList<QueueEntry>(); unacknowledgedMessageMap.drainTo(acked, deliveryTag); - for (UnacknowledgedMessage msg : acked) + for (QueueEntry msg : acked) { - if (debug) - { - _log.debug("Discarding message: " + msg.getMessage().getMessageId()); - } - if(msg.getMessage().isPersistent()) - { - beginTranIfNecessary(); - } + if (debug) + { + _log.debug("Discarding message: " + msg.getMessage().getMessageId()); + } + if(msg.getMessage().isPersistent()) + { + beginTranIfNecessary(); + } - //Message has been ack so discard it. This will dequeue and decrement the reference. - msg.discard(_storeContext); + //Message has been ack so discard it. This will dequeue and decrement the reference. + msg.discard(_storeContext); } } } else { - UnacknowledgedMessage msg; + QueueEntry msg; msg = unacknowledgedMessageMap.remove(deliveryTag); if (msg == null) @@ -208,13 +203,11 @@ public class NonTransactionalContext implements TransactionalContext msg.getMessage().getMessageId()); } } - if(_inTran) { _messageStore.commitTran(_storeContext); _inTran = false; } - } public void messageFullyReceived(boolean persistent) throws AMQException @@ -228,6 +221,6 @@ public class NonTransactionalContext implements TransactionalContext public void messageProcessed(AMQProtocolSession protocolSession) throws AMQException { - _channel.processReturns(protocolSession); + _channel.processReturns(); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java b/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java index 6016ecc1a5..647ba66fb4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java @@ -25,6 +25,7 @@ import org.apache.qpid.server.ack.UnacknowledgedMessageMap; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.queue.AMQMessage; import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.store.StoreContext; /** @@ -106,18 +107,26 @@ public interface TransactionalContext void rollback() throws AMQException; /** - * Delivers the specified message to the specified queue. A 'deliverFirst' flag may be set if the message is a - * redelivery, and should be placed on the front of the queue. + * Delivers the specified message to the specified queue. * * <p/>This is an 'enqueue' operation. * - * @param entry The message to deliver, and the queue to deliver to. - * @param deliverFirst <tt>true</tt> to place the message on the front of the queue for redelivery, <tt>false</tt> - * for normal FIFO message ordering. - * + * @param queue + * @param message The message to deliver * @throws AMQException If the message cannot be delivered for any reason. */ - void deliver(QueueEntry entry, boolean deliverFirst) throws AMQException; + void deliver(final AMQQueue queue, AMQMessage message) throws AMQException; + + /** + * Requeues the specified message entry (message queue pair) + * + * + * @param queueEntry The message,queue pair + * + * @throws AMQException If the message cannot be delivered for any reason. + */ + void requeue(QueueEntry queueEntry) throws AMQException; + /** * Acknowledges a message or many messages as delivered. All messages up to a specified one, may be acknowledged by diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java index 70a76dd8c2..85d804457e 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java @@ -1,44 +1,44 @@ -/* - * - * 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.virtualhost; - -import java.io.IOException; - -import org.apache.qpid.server.management.MBeanAttribute; - -/** - * The management interface exposed to allow management of an Exchange. - * @version 0.1 - */ -public interface ManagedVirtualHost -{ - static final String TYPE = "VirtualHost"; - - /** - * Returns the name of the managed virtualHost. - * @return the name of the exchange. - * @throws java.io.IOException - */ - @MBeanAttribute(name="Name", description= TYPE + " Name") - String getName() throws IOException; - - -} +/*
+ *
+ * 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.virtualhost;
+
+import java.io.IOException;
+
+import org.apache.qpid.server.management.MBeanAttribute;
+
+/**
+ * The management interface exposed to allow management of an Exchange.
+ * @version 0.1
+ */
+public interface ManagedVirtualHost
+{
+ static final String TYPE = "VirtualHost";
+
+ /**
+ * Returns the name of the managed virtualHost.
+ * @return the name of the exchange.
+ * @throws java.io.IOException
+ */
+ @MBeanAttribute(name="Name", description= TYPE + " Name")
+ String getName() throws IOException;
+
+
+}
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 90dc7432b2..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 @@ -71,8 +71,8 @@ public class VirtualHost implements Accessable private ACLPlugin _accessManager; - private Timer _houseKeepingTimer; - + private final Timer _houseKeepingTimer = new Timer("Queue-housekeeping", true); + private static final long DEFAULT_HOUSEKEEPING_PERIOD = 30000L; public void setAccessableName(String name) @@ -172,25 +172,22 @@ public class VirtualHost implements Accessable _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); _brokerMBean.register(); - - _houseKeepingTimer = new Timer("Queue-housekeeping-" + _name, true); - initialiseHouseKeeping(hostConfig); } private void initialiseHouseKeeping(final Configuration hostConfig) { - + 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) + if(period != 0L) { class RemoveExpiredMessagesTask extends TimerTask { public void run() { - for (AMQQueue q : _queueRegistry.getQueues()) + for(AMQQueue q : _queueRegistry.getQueues()) { try @@ -199,7 +196,7 @@ public class VirtualHost implements Accessable } catch (AMQException e) { - _logger.error("Exception in housekeeping for queue: " + q.getName().toString(), e); + _logger.error("Exception in housekeeping for queue: " + q.getName().toString(),e); throw new RuntimeException(e); } } @@ -207,11 +204,11 @@ public class VirtualHost implements Accessable } _houseKeepingTimer.scheduleAtFixedRate(new RemoveExpiredMessagesTask(), - period / 2, - period); + period/2, + period); } } - + private void initialiseMessageStore(Configuration config) throws Exception { String messageStoreClass = config.getString("store.class"); diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java index 9b1619c609..27917fac8a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java @@ -1,70 +1,70 @@ -/* - * - * 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.virtualhost; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - - -public class VirtualHostRegistry -{ - private final Map<String, VirtualHost> _registry = new ConcurrentHashMap<String,VirtualHost>(); - - - private String _defaultVirtualHostName; - - public synchronized void registerVirtualHost(VirtualHost host) throws Exception - { - if(_registry.containsKey(host.getName())) - { - throw new Exception("Virtual Host with name " + host.getName() + " already registered."); - } - _registry.put(host.getName(),host); - } - - public VirtualHost getVirtualHost(String name) - { - if(name == null || name.trim().length() == 0 ) - { - name = getDefaultVirtualHostName(); - } - - return _registry.get(name); - } - - private String getDefaultVirtualHostName() - { - return _defaultVirtualHostName; - } - - public void setDefaultVirtualHostName(String defaultVirtualHostName) - { - _defaultVirtualHostName = defaultVirtualHostName; - } - - - public Collection<VirtualHost> getVirtualHosts() - { - return new ArrayList<VirtualHost>(_registry.values()); - } -} +/*
+ *
+ * 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.virtualhost;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+public class VirtualHostRegistry
+{
+ private final Map<String, VirtualHost> _registry = new ConcurrentHashMap<String,VirtualHost>();
+
+
+ private String _defaultVirtualHostName;
+
+ public synchronized void registerVirtualHost(VirtualHost host) throws Exception
+ {
+ if(_registry.containsKey(host.getName()))
+ {
+ throw new Exception("Virtual Host with name " + host.getName() + " already registered.");
+ }
+ _registry.put(host.getName(),host);
+ }
+
+ public VirtualHost getVirtualHost(String name)
+ {
+ if(name == null || name.trim().length() == 0 )
+ {
+ name = getDefaultVirtualHostName();
+ }
+
+ return _registry.get(name);
+ }
+
+ private String getDefaultVirtualHostName()
+ {
+ return _defaultVirtualHostName;
+ }
+
+ public void setDefaultVirtualHostName(String defaultVirtualHostName)
+ {
+ _defaultVirtualHostName = defaultVirtualHostName;
+ }
+
+
+ public Collection<VirtualHost> getVirtualHosts()
+ {
+ return new ArrayList<VirtualHost>(_registry.values());
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java index edc900f401..faa7b85d58 100644 --- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java +++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java @@ -25,11 +25,11 @@ import org.apache.commons.cli.OptionBuilder; import org.apache.commons.configuration.ConfigurationException; import org.apache.qpid.configuration.Configuration; import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; import org.apache.qpid.server.store.MemoryMessageStore; import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.tools.messagestore.commands.Clear; import org.apache.qpid.tools.messagestore.commands.Command; import org.apache.qpid.tools.messagestore.commands.Copy; diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java index a5b3a87616..0869d9a497 100644 --- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java +++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java @@ -20,8 +20,8 @@ */ package org.apache.qpid.tools.messagestore.commands; -import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.tools.messagestore.MessageStoreTool; +import org.apache.qpid.server.queue.AMQQueue; public class Copy extends Move { diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java index 218d5f04ed..731f6140f9 100644 --- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java +++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java @@ -24,6 +24,7 @@ import org.apache.commons.codec.binary.Hex; import org.apache.mina.common.ByteBuffer; import org.apache.qpid.framing.abstraction.ContentChunk; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.QueueEntryImpl; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.tools.messagestore.MessageStoreTool; import org.apache.qpid.tools.utils.Console; @@ -255,7 +256,7 @@ public class Dump extends Show String title, boolean routing, boolean headers, boolean messageHeaders) { List<QueueEntry> single = new LinkedList<QueueEntry>(); - single.add(new QueueEntry(null,msg)); + single.add(new QueueEntryImpl(null,msg, Long.MIN_VALUE)); List<List> routingData = super.createMessageData(null, single, headers, routing, messageHeaders); diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java index 7e21253fab..a8dd58ca83 100644 --- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java +++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java @@ -21,7 +21,7 @@ package org.apache.qpid.tools.messagestore.commands; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.QueueEntryImpl; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.StoreContext; diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java index f187e26593..5e99997863 100644 --- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java +++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java @@ -20,9 +20,8 @@ */ package org.apache.qpid.tools.messagestore.commands; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.store.StoreContext; import org.apache.qpid.tools.messagestore.MessageStoreTool; +import org.apache.qpid.server.queue.AMQQueue; public class Purge extends Move { diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java index fd7d4c3f13..ff59568374 100644 --- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java +++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java @@ -22,9 +22,9 @@ 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.AMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.tools.messagestore.MessageStoreTool; import java.util.LinkedList; diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java index a6dccf0f36..2fa017fc64 100644 --- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java +++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java @@ -26,6 +26,7 @@ import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.QueueEntryImpl; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.tools.messagestore.MessageStoreTool; @@ -33,7 +34,6 @@ import org.apache.qpid.tools.utils.Console; import java.util.LinkedList; import java.util.List; -import java.util.StringTokenizer; public class Show extends AbstractCommand { |