diff options
Diffstat (limited to 'qpid/java/broker/src/main')
98 files changed, 3574 insertions, 4409 deletions
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java index d1ea5dba69..01a0d9900d 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java @@ -20,8 +20,8 @@ package org.apache.qpid.server; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Map; import javax.management.JMException; import javax.management.MBeanException; @@ -30,6 +30,7 @@ import javax.management.ObjectName; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; import org.apache.qpid.management.common.mbeans.ManagedBroker; import org.apache.qpid.management.common.mbeans.ManagedQueue; import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; @@ -60,6 +61,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr private final QueueRegistry _queueRegistry; private final ExchangeRegistry _exchangeRegistry; private final ExchangeFactory _exchangeFactory; + private final Exchange _defaultExchange; private final DurableConfigurationStore _durableConfig; private final VirtualHostImpl.VirtualHostMBean _virtualHostMBean; @@ -74,6 +76,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr _queueRegistry = virtualHost.getQueueRegistry(); _exchangeRegistry = virtualHost.getExchangeRegistry(); + _defaultExchange = _exchangeRegistry.getDefaultExchange(); _durableConfig = virtualHost.getDurableConfigurationStore(); _exchangeFactory = virtualHost.getExchangeFactory(); } @@ -241,7 +244,13 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr */ public void createNewQueue(String queueName, String owner, boolean durable) throws JMException, MBeanException { - AMQQueue queue = _queueRegistry.getQueue(new AMQShortString(queueName)); + createNewQueue(queueName, owner, durable, null); + } + + public void createNewQueue(String queueName, String owner, boolean durable, Map<String,Object> arguments) throws JMException + { + final AMQShortString queueNameAsAMQShortString = new AMQShortString(queueName); + AMQQueue queue = _queueRegistry.getQueue(queueNameAsAMQShortString); if (queue != null) { throw new JMException("The queue \"" + queueName + "\" already exists."); @@ -256,13 +265,21 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr ownerShortString = new AMQShortString(owner); } - queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), durable, ownerShortString, false, false, getVirtualHost(), null); + FieldTable args = null; + if(arguments != null) + { + args = FieldTable.convertToFieldTable(arguments); + } + final VirtualHost virtualHost = getVirtualHost(); + + queue = AMQQueueFactory.createAMQQueueImpl(queueNameAsAMQShortString, durable, ownerShortString, + false, false, getVirtualHost(), args); if (queue.isDurable() && !queue.isAutoDelete()) { - _durableConfig.createQueue(queue); + _durableConfig.createQueue(queue, args); } - _queueRegistry.registerQueue(queue); + virtualHost.getBindingFactory().addBinding(queueName, queue, _defaultExchange, null); } catch (AMQException ex) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java index 2a96426f02..30d620401f 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java @@ -22,7 +22,6 @@ package org.apache.qpid.server; import org.apache.log4j.Logger; -import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQMethodBody; @@ -53,6 +52,7 @@ import org.apache.qpid.server.logging.messages.ChannelMessages; import org.apache.qpid.server.logging.messages.ExchangeMessages; import org.apache.qpid.server.logging.subjects.ChannelLogSubject; import org.apache.qpid.server.message.AMQMessage; +import org.apache.qpid.server.message.InboundMessage; import org.apache.qpid.server.message.MessageMetaData; import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; @@ -63,6 +63,7 @@ import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.queue.InboundMessageAdapter; import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.registry.ApplicationRegistry; @@ -693,6 +694,31 @@ public class AMQChannel implements SessionConfig, AMQSessionModel } + public boolean isMaxDeliveryCountEnabled(final long deliveryTag) + { + final QueueEntry queueEntry = _unacknowledgedMessageMap.get(deliveryTag); + if (queueEntry != null) + { + final int maximumDeliveryCount = queueEntry.getQueue().getMaximumDeliveryCount(); + return maximumDeliveryCount > 0; + } + + return false; + } + + public boolean isDeliveredTooManyTimes(final long deliveryTag) + { + final QueueEntry queueEntry = _unacknowledgedMessageMap.get(deliveryTag); + if (queueEntry != null) + { + final int maximumDeliveryCount = queueEntry.getQueue().getMaximumDeliveryCount(); + final int numDeliveries = queueEntry.getDeliveryCount(); + return maximumDeliveryCount != 0 && numDeliveries >= maximumDeliveryCount; + } + + return false; + } + /** * Called to resend all outstanding unacknowledged messages to this same channel. * @@ -740,9 +766,9 @@ public class AMQChannel implements SessionConfig, AMQSessionModel QueueEntry message = entry.getValue(); long deliveryTag = entry.getKey(); + //Amend the delivery counter as the client hasn't seen these messages yet. + message.decrementDeliveryCount(); - - ServerMessage msg = message.getMessage(); AMQQueue queue = message.getQueue(); // Our Java Client will always suspend the channel when resending! @@ -800,6 +826,10 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { QueueEntry message = entry.getValue(); long deliveryTag = entry.getKey(); + + //Amend the delivery counter as the client hasn't seen these messages yet. + message.decrementDeliveryCount(); + _unacknowledgedMessageMap.remove(deliveryTag); message.setRedelivered(); @@ -1060,6 +1090,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel getProtocolSession().getProtocolOutputConverter().writeDeliver(entry, getChannelId(), deliveryTag, ((SubscriptionImpl)sub).getConsumerTag()); + entry.incrementDeliveryCount(); } }; @@ -1248,7 +1279,6 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { private final Collection<QueueEntry> _ackedMessages; - public MessageAcknowledgeAction(Collection<QueueEntry> ackedMessages) { _ackedMessages = ackedMessages; @@ -1481,4 +1511,54 @@ public class AMQChannel implements SessionConfig, AMQSessionModel } } } + + public void deadLetter(long deliveryTag) throws AMQException + { + final UnacknowledgedMessageMap unackedMap = getUnacknowledgedMessageMap(); + final QueueEntry rejectedQueueEntry = unackedMap.get(deliveryTag); + + if (rejectedQueueEntry == null) + { + _logger.warn("No message found, unable to DLQ delivery tag: " + deliveryTag); + return; + } + else + { + final ServerMessage msg = rejectedQueueEntry.getMessage(); + + final AMQQueue queue = rejectedQueueEntry.getQueue(); + + final Exchange altExchange = queue.getAlternateExchange(); + unackedMap.remove(deliveryTag); + + if (altExchange == null) + { + _logger.debug("No alternate exchange configured for queue, must discard the message as unable to DLQ: delivery tag: " + deliveryTag); + _actor.message(_logSubject, ChannelMessages.DISCARDMSG_NOALTEXCH(msg.getMessageNumber(), queue.getName(), msg.getRoutingKey())); + rejectedQueueEntry.discard(); + return; + } + + final InboundMessage m = new InboundMessageAdapter(rejectedQueueEntry); + + final ArrayList<? extends BaseQueue> destinationQueues = altExchange.route(m); + + if (destinationQueues == null || destinationQueues.isEmpty()) + { + _logger.debug("Routing process provided no queues to enqueue the message on, must discard message as unable to DLQ: delivery tag: " + deliveryTag); + _actor.message(_logSubject, ChannelMessages.DISCARDMSG_NOROUTE(msg.getMessageNumber(), altExchange.getName())); + rejectedQueueEntry.discard(); + return; + } + + rejectedQueueEntry.routeToAlternate(); + + //output operational logging for each delivery post commit + for (final BaseQueue destinationQueue : destinationQueues) + { + _actor.message(_logSubject, ChannelMessages.DEADLETTERMSG(msg.getMessageNumber(), destinationQueue.getNameShortString().asString())); + } + + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java index 400ce50bc4..94ab43c851 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java @@ -170,11 +170,17 @@ public class BindingFactory { arguments = Collections.emptyMap(); } - - //Perform ACLs - if (!getVirtualHost().getSecurityManager().authoriseBind(exchange, queue, new AMQShortString(bindingKey))) + + // The default exchange bindings must reflect the existence of queues, allow + // all operations on it to succeed. It is up to the broker to prevent illegal + // attempts at binding to this exchange, not the ACLs. + if(exchange != _defaultExchange) { - throw new AMQSecurityException("Permission denied: binding " + bindingKey); + //Perform ACLs + if (!getVirtualHost().getSecurityManager().authoriseBind(exchange, queue, new AMQShortString(bindingKey))) + { + throw new AMQSecurityException("Permission denied: binding " + bindingKey); + } } BindingImpl b = new BindingImpl(bindingKey,queue,exchange,arguments); @@ -238,10 +244,16 @@ public class BindingFactory arguments = Collections.emptyMap(); } - // Check access - if (!getVirtualHost().getSecurityManager().authoriseUnbind(exchange, new AMQShortString(bindingKey), queue)) + // The default exchange bindings must reflect the existence of queues, allow + // all operations on it to succeed. It is up to the broker to prevent illegal + // attempts at binding to this exchange, not the ACLs. + if(exchange != _defaultExchange) { - throw new AMQSecurityException("Permission denied: binding " + bindingKey); + // Check access + if (!getVirtualHost().getSecurityManager().authoriseUnbind(exchange, new AMQShortString(bindingKey), queue)) + { + throw new AMQSecurityException("Permission denied: unbinding " + bindingKey); + } } BindingImpl b = _bindings.remove(new BindingImpl(bindingKey,queue,exchange,arguments)); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java index 3a28b65a08..759907d4bd 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/QueueConfiguration.java @@ -62,7 +62,10 @@ public class QueueConfiguration extends ConfigurationPlugin "capacity", "flowResumeCapacity", "lvq", - "lvqKey" + "lvqKey", + "sortKey", + "maximumDeliveryCount", + "deadLetterQueues" }; } @@ -167,11 +170,30 @@ public class QueueConfiguration extends ConfigurationPlugin return getStringValue("lvqKey", null); } + public boolean isTopic() { return getBooleanValue("topic"); } + public String getQueueSortKey() + { + return getStringValue("sortKey", null); + } + + public int getMaxDeliveryCount() + { + return getIntValue("maximumDeliveryCount", _vHostConfig.getMaxDeliveryCount()); + } + + /** + * Check if dead letter queue delivery is enabled, deferring to the virtualhost configuration if not set. + */ + public boolean isDeadLetterQueueEnabled() + { + return getBooleanValue("deadLetterQueues", _vHostConfig.isDeadLetterQueueEnabled()); + } + public static class QueueConfig extends ConfigurationPlugin { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java index 0d347873c2..4b42e39aa1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java @@ -40,6 +40,8 @@ import org.apache.commons.configuration.SystemConfiguration; import org.apache.commons.configuration.XMLConfiguration; import org.apache.log4j.Logger; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.exchange.DefaultExchangeFactory; +import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.signal.SignalHandlerTask; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -805,4 +807,39 @@ public class ServerConfiguration extends ConfigurationPlugin final List<String> disabledFeatures = getListValue("disabledFeatures", Collections.emptyList()); return disabledFeatures; } + + public boolean getManagementRightsInferAllAccess() + { + return getBooleanValue("management.managementRightsInferAllAccess", true); + } + + public int getMaxDeliveryCount() + { + return getConfig().getInt("maximumDeliveryCount", 0); + } + + /** + * Check if dead letter queue delivery is enabled, defaults to disabled if not set. + */ + public boolean isDeadLetterQueueEnabled() + { + return getConfig().getBoolean("deadLetterQueues", false); + } + + /** + * String to affix to end of queue name when generating an alternate exchange for DLQ purposes. + */ + public String getDeadLetterExchangeSuffix() + { + return getConfig().getString("deadLetterExchangeSuffix", DefaultExchangeFactory.DEFAULT_DLE_NAME_SUFFIX); + } + + /** + * String to affix to end of queue name when generating a queue for DLQ purposes. + */ + public String getDeadLetterQueueSuffix() + { + return getConfig().getString("deadLetterQueueSuffix", AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java index 6729a5ce0f..c4e4f701a8 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java @@ -347,4 +347,18 @@ public class VirtualHostConfiguration extends ConfigurationPlugin { return getLongValue("transactionTimeout.idleClose", 0L); } + + public int getMaxDeliveryCount() + { + return getIntValue("queues.maximumDeliveryCount", ApplicationRegistry.getInstance().getConfiguration().getMaxDeliveryCount()); + } + + /** + * Check if dead letter queue delivery is enabled, deferring to the broker configuration if not set. + */ + public boolean isDeadLetterQueueEnabled() + { + return getBooleanValue("queues.deadLetterQueues", ApplicationRegistry.getInstance().getConfiguration().isDeadLetterQueueEnabled()); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/SlowConsumerDetectionQueueConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/SlowConsumerDetectionQueueConfiguration.java index 0638ea362f..6f8020fc54 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/SlowConsumerDetectionQueueConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/SlowConsumerDetectionQueueConfiguration.java @@ -87,6 +87,19 @@ public class SlowConsumerDetectionQueueConfiguration extends ConfigurationPlugin @Override public void validateConfiguration() throws ConfigurationException { + PluginManager pluginManager; + try + { + pluginManager = ApplicationRegistry.getInstance().getPluginManager(); + } + catch (IllegalStateException ise) + { + // We see this happen during shutdown due to asynchronous reconfig performed IO threads + // running at the same time as the shutdown handler. + _policyPlugin = null; + return; + } + if (!containsPositiveLong("messageAge") && !containsPositiveLong("depth") && !containsPositiveLong("messageCount")) @@ -96,8 +109,6 @@ public class SlowConsumerDetectionQueueConfiguration extends ConfigurationPlugin } SlowConsumerDetectionPolicyConfiguration policyConfig = getConfiguration(SlowConsumerDetectionPolicyConfiguration.class.getName()); - - PluginManager pluginManager = ApplicationRegistry.getInstance().getPluginManager(); Map<String, SlowConsumerPolicyPluginFactory> factories = pluginManager.getSlowConsumerPlugins(); if (policyConfig == null) diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java index 7837a9bc38..090c0426cf 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java @@ -40,6 +40,7 @@ import org.apache.qpid.server.virtualhost.VirtualHost; public class DefaultExchangeFactory implements ExchangeFactory { private static final Logger _logger = Logger.getLogger(DefaultExchangeFactory.class); + public static final String DEFAULT_DLE_NAME_SUFFIX = "_DLE"; private Map<AMQShortString, ExchangeType<? extends Exchange>> _exchangeClassMap = new HashMap<AMQShortString, ExchangeType<? extends Exchange>>(); private final VirtualHost _host; @@ -122,7 +123,7 @@ public class DefaultExchangeFactory implements ExchangeFactory if (exchangeType == null) { _logger.error("No such custom exchange class found: \""+String.valueOf(className)+"\""); - return; + continue; } Class<? extends ExchangeType> exchangeTypeClass = exchangeType.getClass(); ExchangeType<? extends ExchangeType> type = exchangeTypeClass.newInstance(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java index 0e7459498a..8d2dee5aaa 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java @@ -27,7 +27,9 @@ import java.util.concurrent.ConcurrentMap; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -87,12 +89,22 @@ public class DefaultExchangeRegistry implements ExchangeRegistry public void unregisterExchange(AMQShortString name, boolean inUse) throws AMQException { - // Check access - if (!_host.getSecurityManager().authoriseDelete(_exchangeMap.get(name))) + final Exchange exchange = _exchangeMap.get(name); + if (exchange == null) + { + throw new AMQException(AMQConstant.NOT_FOUND, "Unknown exchange " + name, null); + } + + if (ExchangeDefaults.DEFAULT_EXCHANGE_NAME.equals(name)) + { + throw new AMQException(AMQConstant.NOT_ALLOWED, "Cannot unregister the default exchange", null); + } + + if (!_host.getSecurityManager().authoriseDelete(exchange)) { throw new AMQSecurityException(); } - + // TODO: check inUse argument Exchange e = _exchangeMap.remove(name); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java index bd75f7bc51..76f86ea1b4 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java @@ -117,7 +117,7 @@ public class FanoutExchange extends AbstractExchange public boolean isBound(AMQShortString routingKey, AMQQueue queue) { - return _queues.contains(queue); + return _queues.containsKey(queue); } public boolean isBound(AMQShortString routingKey) @@ -129,7 +129,7 @@ public class FanoutExchange extends AbstractExchange public boolean isBound(AMQQueue queue) { - return _queues.contains(queue); + return _queues.containsKey(queue); } public boolean hasBindings() diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java index 66c9b5b552..caed8f4b94 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeMBean.java @@ -20,12 +20,21 @@ */ package org.apache.qpid.server.exchange; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.ManagementActor; +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.*; + +import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.Map; @@ -94,5 +103,48 @@ final class HeadersExchangeMBean extends AbstractExchangeMBean<HeadersExchange> return bindingList; } + @Override + public void createNewBinding(String queueName, String binding) throws JMException + { + VirtualHost vhost = getExchange().getVirtualHost(); + AMQQueue queue = vhost.getQueueRegistry().getQueue(new AMQShortString(queueName)); + if (queue == null) + { + throw new JMException("Queue \"" + queueName + "\" is not registered with the virtualhost."); + } + + CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger())); + + final Map<String,Object> arguments = new HashMap<String, Object>(); + final String[] bindings = binding.split(","); + for (int i = 0; i < bindings.length; i++) + { + final String[] keyAndValue = bindings[i].split("="); + if (keyAndValue == null || keyAndValue.length == 0 || keyAndValue.length > 2 || keyAndValue[0].length() == 0) + { + throw new JMException("Format for headers binding should be \"<attribute1>=<value1>,<attribute2>=<value2>\" "); + } + + if(keyAndValue.length == 1) + { + //no value was given, only a key. Use an empty value to signal match on key presence alone + arguments.put(keyAndValue[0], ""); + } + else + { + arguments.put(keyAndValue[0], keyAndValue[1]); + } + } + try + { + vhost.getBindingFactory().addBinding(binding,queue,getExchange(),arguments); + } + catch (AMQException ex) + { + JMException jme = new JMException(ex.toString()); + throw new MBeanException(jme, "Error creating new binding " + binding); + } + CurrentActor.remove(); + } } // End of MBean class diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java index c5f2d1e808..5a5ef5e6c5 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java @@ -1,6 +1,5 @@ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; import java.util.concurrent.atomic.AtomicLong; @@ -59,9 +58,8 @@ public class BytesOnlyCreditManager extends AbstractFlowCreditManager return _bytesCredit.get() > 0L; } - public boolean useCreditForMessage(ServerMessage msg) + public boolean useCreditForMessage(long msgSize) { - final long msgSize = msg.getSize(); if(hasCredit()) { if(_bytesCredit.addAndGet(-msgSize) >= 0) diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/CreditCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/CreditCreditManager.java index b47f986155..c6771177ac 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/CreditCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/CreditCreditManager.java @@ -20,7 +20,6 @@ */ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; public class CreditCreditManager extends AbstractFlowCreditManager implements FlowCreditManager_0_10 { @@ -118,7 +117,7 @@ public class CreditCreditManager extends AbstractFlowCreditManager implements Fl return (_bytesCredit != 0L && _messageCredit != 0L); } - public synchronized boolean useCreditForMessage(final ServerMessage msg) + public synchronized boolean useCreditForMessage(long msgSize) { if(_messageCredit >= 0L) { @@ -130,10 +129,10 @@ public class CreditCreditManager extends AbstractFlowCreditManager implements Fl return true; } - else if(msg.getSize() <= _bytesCredit) + else if(msgSize <= _bytesCredit) { _messageCredit--; - _bytesCredit -= msg.getSize(); + _bytesCredit -= msgSize; return true; } @@ -151,9 +150,9 @@ public class CreditCreditManager extends AbstractFlowCreditManager implements Fl } else if(_bytesCredit >= 0L) { - if(msg.getSize() <= _bytesCredit) + if(msgSize <= _bytesCredit) { - _bytesCredit -= msg.getSize(); + _bytesCredit -= msgSize; return true; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java index bec51d361d..8a80262983 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java @@ -1,6 +1,5 @@ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; /* * @@ -41,6 +40,6 @@ public interface FlowCreditManager public boolean hasCredit(); - public boolean useCreditForMessage(ServerMessage msg); + public boolean useCreditForMessage(long msgSize); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java index 901b71fd1f..6fcc687440 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java @@ -1,6 +1,5 @@ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; /* * @@ -47,7 +46,7 @@ public class LimitlessCreditManager extends AbstractFlowCreditManager implements return true; } - public boolean useCreditForMessage(ServerMessage msg) + public boolean useCreditForMessage(long msgSize) { return true; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java index 19a9ac1d23..0e6ce70a60 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java @@ -1,6 +1,5 @@ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; /* * @@ -62,7 +61,7 @@ public class MessageAndBytesCreditManager extends AbstractFlowCreditManager impl return (_messageCredit > 0L) && ( _bytesCredit > 0L ); } - public synchronized boolean useCreditForMessage(ServerMessage msg) + public synchronized boolean useCreditForMessage(final long msgSize) { if(_messageCredit == 0L) { @@ -71,7 +70,6 @@ public class MessageAndBytesCreditManager extends AbstractFlowCreditManager impl } else { - final long msgSize = msg.getSize(); if(msgSize > _bytesCredit) { setSuspended(true); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java index a386f66b11..9b7c40e923 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java @@ -1,6 +1,5 @@ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; import java.util.concurrent.atomic.AtomicLong; @@ -61,7 +60,7 @@ public class MessageOnlyCreditManager extends AbstractFlowCreditManager implemen return _messageCredit.get() > 0L; } - public boolean useCreditForMessage(ServerMessage msg) + public boolean useCreditForMessage(long msgSize) { if(hasCredit()) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java index 026804439c..a193f8fae4 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java @@ -20,7 +20,6 @@ */ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; public class Pre0_10CreditManager extends AbstractFlowCreditManager implements FlowCreditManager { @@ -133,7 +132,7 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F && (_messageCreditLimit == 0L || _messageCredit > 0); } - public synchronized boolean useCreditForMessage(final ServerMessage msg) + public synchronized boolean useCreditForMessage(final long msgSize) { if(_messageCreditLimit != 0L) { @@ -147,10 +146,10 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F } else { - if((_bytesCredit >= msg.getSize()) || (_bytesCredit == _bytesCreditLimit)) + if((_bytesCredit >= msgSize) || (_bytesCredit == _bytesCreditLimit)) { _messageCredit--; - _bytesCredit -= msg.getSize(); + _bytesCredit -= msgSize; return true; } @@ -176,9 +175,9 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F } else { - if((_bytesCredit >= msg.getSize()) || (_bytesCredit == _bytesCreditLimit)) + if((_bytesCredit >= msgSize) || (_bytesCredit == _bytesCreditLimit)) { - _bytesCredit -= msg.getSize(); + _bytesCredit -= msgSize; return true; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/WindowCreditManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/WindowCreditManager.java index 10f578551a..a0c2e9f977 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/WindowCreditManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/WindowCreditManager.java @@ -20,10 +20,12 @@ */ package org.apache.qpid.server.flow; -import org.apache.qpid.server.message.ServerMessage; +import org.apache.log4j.Logger; public class WindowCreditManager extends AbstractFlowCreditManager implements FlowCreditManager_0_10 { + private static final Logger LOGGER = Logger.getLogger(WindowCreditManager.class); + private volatile long _bytesCreditLimit; private volatile long _messageCreditLimit; @@ -43,6 +45,15 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl } + public long getBytesCreditLimit() + { + return _bytesCreditLimit; + } + + public long getMessageCreditLimit() + { + return _messageCreditLimit; + } public synchronized void setCreditLimits(final long bytesCreditLimit, final long messageCreditLimit) { @@ -70,31 +81,30 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl public synchronized void restoreCredit(final long messageCredit, final long bytesCredit) { + _messageUsed -= messageCredit; + if(_messageUsed < 0L) + { + LOGGER.error("Message credit used value was negative: "+ _messageUsed); + _messageUsed = 0; + } + boolean notifyIncrease = true; + if(_messageCreditLimit > 0L) { notifyIncrease = (_messageUsed != _messageCreditLimit); - _messageUsed -= messageCredit; - - //TODO log warning - if(_messageUsed < 0L) - { - _messageUsed = 0; - } } - + _bytesUsed -= bytesCredit; + if(_bytesUsed < 0L) + { + LOGGER.error("Bytes credit used value was negative: "+ _messageUsed); + _bytesUsed = 0; + } if(_bytesCreditLimit > 0L) { notifyIncrease = notifyIncrease && bytesCredit>0; - _bytesUsed -= bytesCredit; - - //TODO log warning - if(_bytesUsed < 0L) - { - _bytesUsed = 0; - } if(notifyIncrease) { @@ -102,10 +112,7 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl } } - - setSuspended(!hasCredit()); - } @@ -116,7 +123,7 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl && (_messageCreditLimit < 0L || _messageCreditLimit > _messageUsed); } - public synchronized boolean useCreditForMessage(final ServerMessage msg) + public synchronized boolean useCreditForMessage(final long msgSize) { if(_messageCreditLimit >= 0L) { @@ -128,10 +135,10 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl return true; } - else if(_bytesUsed + msg.getSize() <= _bytesCreditLimit) + else if(_bytesUsed + msgSize <= _bytesCreditLimit) { _messageUsed++; - _bytesUsed += msg.getSize(); + _bytesUsed += msgSize; return true; } @@ -149,9 +156,9 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl } else if(_bytesCreditLimit >= 0L) { - if(_bytesUsed + msg.getSize() <= _bytesCreditLimit) + if(_bytesUsed + msgSize <= _bytesCreditLimit) { - _bytesUsed += msg.getSize(); + _bytesUsed += msgSize; return true; } @@ -169,18 +176,6 @@ public class WindowCreditManager extends AbstractFlowCreditManager implements Fl } - public void stop() - { - if(_bytesCreditLimit > 0) - { - _bytesCreditLimit = 0; - } - if(_messageCreditLimit > 0) - { - _messageCreditLimit = 0; - } - - } public synchronized void addCredit(long count, long bytes) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java index 14ce85530e..bbb009003c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java @@ -127,16 +127,15 @@ public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetB 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()); + singleMessageCredit.useCreditForMessage(entry.getMessage().getSize()); if(entry.getMessage() instanceof AMQMessage) { session.getProtocolOutputConverter().writeGetOk(entry, channel.getChannelId(), deliveryTag, queue.getMessageCount()); + entry.incrementDeliveryCount(); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java index 62dd76f832..0ea88e4ab6 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java @@ -59,7 +59,6 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR { _logger.debug("Rejecting:" + body.getDeliveryTag() + ": Requeue:" + body.getRequeue() + - //": Resend:" + evt.getMethod().resend + " on channel:" + channel.debugIdentity()); } @@ -70,26 +69,23 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR if (message == null) { _logger.warn("Dropping reject request as message is null for tag:" + deliveryTag); -// throw evt.getMethod().getChannelException(AMQConstant.NOT_FOUND, "Delivery Tag(" + deliveryTag + ")not known"); } else { if (message.isQueueDeleted()) { - _logger.warn("Message's Queue as already been purged, unable to Reject. " + - "Dropping message should use Dead Letter Queue"); + _logger.warn("Message's Queue has already been purged, dropping message"); message = channel.getUnacknowledgedMessageMap().remove(deliveryTag); if(message != null) { message.discard(); } - //sendtoDeadLetterQueue(msg) return; } if (message.getMessage() == null) { - _logger.warn("Message as already been purged, unable to Reject."); + _logger.warn("Message has already been purged, unable to Reject."); return; } @@ -98,27 +94,44 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR { _logger.debug("Rejecting: DT:" + deliveryTag + "-" + message.getMessage() + ": Requeue:" + body.getRequeue() + - //": Resend:" + evt.getMethod().resend + " on channel:" + channel.debugIdentity()); } - // If we haven't requested message to be resent to this consumer then reject it from ever getting it. - //if (!evt.getMethod().resend) - { - message.reject(); - } + message.reject(); if (body.getRequeue()) { channel.requeue(deliveryTag); + + //this requeue represents a message rejected from the pre-dispatch queue + //therefore we need to amend the delivery counter. + message.decrementDeliveryCount(); } 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); + final boolean maxDeliveryCountEnabled = channel.isMaxDeliveryCountEnabled(deliveryTag); + _logger.debug("maxDeliveryCountEnabled: " + maxDeliveryCountEnabled + " deliveryTag " + deliveryTag); + if (maxDeliveryCountEnabled) + { + final boolean deliveredTooManyTimes = channel.isDeliveredTooManyTimes(deliveryTag); + _logger.debug("deliveredTooManyTimes: " + deliveredTooManyTimes + " deliveryTag " + deliveryTag); + if (deliveredTooManyTimes) + { + channel.deadLetter(body.getDeliveryTag()); + } + else + { + //this requeue represents a message rejected because of a recover/rollback that we + //are not ready to DLQ. We rely on the reject command to resend from the unacked map + //and therefore need to increment the delivery counter so we cancel out the effect + //of the AMQChannel#resend() decrement. + message.incrementDeliveryCount(); + } + } + else + { + channel.deadLetter(body.getDeliveryTag()); + } } } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java index 8391a4b184..f2119f7faa 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java @@ -25,8 +25,10 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQMethodBody; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.MethodRegistry; import org.apache.qpid.framing.QueueUnbindBody; import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; +import org.apache.qpid.framing.amqp_0_91.MethodRegistry_0_91; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.exchange.Exchange; @@ -62,7 +64,7 @@ public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindB final AMQQueue queue; - final AMQShortString routingKey; + final AMQShortString routingKey; if (body.getQueue() == null) { @@ -114,10 +116,21 @@ public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindB _log.info("Binding queue " + queue + " to exchange " + exch + " with routing key " + routingKey); } - MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry(); - AMQMethodBody responseBody = methodRegistry.createQueueUnbindOkBody(); + final MethodRegistry registry = session.getMethodRegistry(); + final AMQMethodBody responseBody; + if (registry instanceof MethodRegistry_0_9) + { + responseBody = ((MethodRegistry_0_9)registry).createQueueUnbindOkBody(); + } + else if (registry instanceof MethodRegistry_0_91) + { + responseBody = ((MethodRegistry_0_91)registry).createQueueUnbindOkBody(); + } + else + { + // 0-8 does not support QueueUnbind + throw new AMQException(AMQConstant.COMMAND_INVALID, "QueueUnbind not present in AMQP version: " + session.getProtocolVersion(), null); + } session.writeFrame(responseBody.generateFrame(channelId)); - - } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java index 4643dee0a3..20ba3af458 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java @@ -72,7 +72,12 @@ public class TxRollbackHandler implements StateAwareMethodListener<TxRollbackBod }; channel.rollback(task); - + + //Now resend all the unacknowledged messages back to the original subscribers. + //(Must be done after the TxnRollback-ok response). + // Why, are we not allowed to send messages back to client before the ok method? + channel.resend(false); + } catch (AMQException e) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties index ed8c0d0ce9..b5df212904 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties @@ -32,3 +32,7 @@ FLOW_REMOVED = CHN-1006 : Flow Control Removed # 0 - time in milliseconds OPEN_TXN = CHN-1007 : Open Transaction : {0,number} ms IDLE_TXN = CHN-1008 : Idle Transaction : {0,number} ms + +DISCARDMSG_NOALTEXCH = CHN-1009 : Discarded message : {0,number} as no alternate exchange configured for queue : {1} routing key : {2} +DISCARDMSG_NOROUTE = CHN-1010 : Discarded message : {0,number} as no binding on alternate exchange : {1} +DEADLETTERMSG = CHN-1011 : Message : {0,number} moved to dead letter queue : {1} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java index c4ffcd26bf..6c9d6e39de 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java @@ -52,8 +52,6 @@ public abstract class AMQManagedObject extends DefaultManagedObject */ protected long _notificationSequenceNumber = 0; - protected MBeanInfo _mbeanInfo; - protected LogActor _logActor; protected AMQManagedObject(Class<?> managementInterface, String typeName) @@ -63,27 +61,8 @@ public abstract class AMQManagedObject extends DefaultManagedObject // CurrentActor will be defined as these objects are created during // broker startup. _logActor = new ManagementActor(CurrentActor.get().getRootMessageLogger()); - buildMBeanInfo(); - } - - @Override - public MBeanInfo getMBeanInfo() - { - return _mbeanInfo; - } - - private void buildMBeanInfo() throws NotCompliantMBeanException - { - _mbeanInfo = new MBeanInfo(this.getClass().getName(), - MBeanIntrospector.getMBeanDescription(this.getClass()), - MBeanIntrospector.getMBeanAttributesInfo(getManagementInterface()), - MBeanIntrospector.getMBeanConstructorsInfo(this.getClass()), - MBeanIntrospector.getMBeanOperationsInfo(getManagementInterface()), - this.getNotificationInfo()); } - - // notification broadcaster implementation public void addNotificationListener(NotificationListener listener, @@ -99,8 +78,5 @@ public abstract class AMQManagedObject extends DefaultManagedObject _broadcaster.removeNotificationListener(listener); } - public MBeanNotificationInfo[] getNotificationInfo() - { - return null; - } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java new file mode 100644 index 0000000000..68350a1632 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AbstractAMQManagedConnectionObject.java @@ -0,0 +1,71 @@ +package org.apache.qpid.server.management; + +import javax.management.Notification; + +import javax.management.JMException; +import javax.management.MBeanNotificationInfo; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; +import javax.management.monitor.MonitorNotification; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularType; +import org.apache.qpid.management.common.mbeans.ManagedConnection; + +public abstract class AbstractAMQManagedConnectionObject extends AMQManagedObject implements ManagedConnection +{ + protected final String _name; + + protected static final OpenType[] _channelAttributeTypes = { SimpleType.INTEGER, SimpleType.BOOLEAN, SimpleType.STRING, SimpleType.INTEGER, SimpleType.BOOLEAN }; + protected static final CompositeType _channelType; + protected static final TabularType _channelsType; + + protected static final String BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION_STR = + "Broker Management Console has closed the connection."; + + static + { + try + { + _channelType = new CompositeType("Channel", "Channel Details", COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), + COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), _channelAttributeTypes); + _channelsType = new TabularType("Channels", "Channels", _channelType, (String[]) TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()])); + } + catch (JMException ex) + { + // This is not expected to ever occur. + throw new RuntimeException("Got JMException in static initializer.", ex); + } + } + + protected AbstractAMQManagedConnectionObject(final String remoteAddress) throws NotCompliantMBeanException + { + super(ManagedConnection.class, ManagedConnection.TYPE); + _name = "anonymous".equals(remoteAddress) ? (remoteAddress + hashCode()) : remoteAddress; + } + + @Override + public String getObjectInstanceName() + { + return ObjectName.quote(_name); + } + + public void notifyClients(String notificationMsg) + { + final Notification n = new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, ++_notificationSequenceNumber, + System.currentTimeMillis(), notificationMsg); + _broadcaster.sendNotification(n); + } + + @Override + public MBeanNotificationInfo[] getNotificationInfo() + { + String[] notificationTypes = new String[] { MonitorNotification.THRESHOLD_VALUE_EXCEEDED }; + String name = MonitorNotification.class.getName(); + String description = "Channel count has reached threshold value"; + MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description); + + return new MBeanNotificationInfo[] { info1 }; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java index e44b8c41cb..0c3a5fc571 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java @@ -21,6 +21,8 @@ package org.apache.qpid.server.management; import javax.management.JMException; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; @@ -28,7 +30,6 @@ import javax.management.StandardMBean; import org.apache.log4j.Logger; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; /** * Provides implementation of the boilerplate ManagedObject interface. Most managed objects should find it useful @@ -37,11 +38,13 @@ import org.apache.qpid.server.registry.IApplicationRegistry; */ public abstract class DefaultManagedObject extends StandardMBean implements ManagedObject { - private static final Logger LOGGER = Logger.getLogger(ApplicationRegistry.class); + private static final Logger LOGGER = Logger.getLogger(DefaultManagedObject.class); - private Class<?> _managementInterface; + private final Class<?> _managementInterface; - private String _typeName; + private final String _typeName; + + private final MBeanInfo _mbeanInfo; private ManagedObjectRegistry _registry; @@ -51,6 +54,13 @@ public abstract class DefaultManagedObject extends StandardMBean implements Mana super(managementInterface); _managementInterface = managementInterface; _typeName = typeName; + _mbeanInfo = buildMBeanInfo(); + } + + @Override + public MBeanInfo getMBeanInfo() + { + return _mbeanInfo; } public String getType() @@ -98,7 +108,6 @@ public abstract class DefaultManagedObject extends StandardMBean implements Mana return getObjectInstanceName() + "[" + getType() + "]"; } - /** * Created the ObjectName as per the JMX Specs * @return ObjectName @@ -161,4 +170,18 @@ public abstract class DefaultManagedObject extends StandardMBean implements Mana return ""; } + private MBeanInfo buildMBeanInfo() throws NotCompliantMBeanException + { + return new MBeanInfo(this.getClass().getName(), + MBeanIntrospector.getMBeanDescription(this.getClass()), + MBeanIntrospector.getMBeanAttributesInfo(getManagementInterface()), + MBeanIntrospector.getMBeanConstructorsInfo(this.getClass()), + MBeanIntrospector.getMBeanOperationsInfo(getManagementInterface()), + this.getNotificationInfo()); + } + + public MBeanNotificationInfo[] getNotificationInfo() + { + return null; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java index b44964f176..8583e8d57b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java @@ -38,10 +38,13 @@ import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMIServerSocketFactory; import java.rmi.server.UnicastRemoteObject; import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; +import javax.management.Notification; import javax.management.NotificationFilterSupport; import javax.management.NotificationListener; import javax.management.ObjectName; @@ -49,11 +52,13 @@ import javax.management.remote.JMXConnectionNotification; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXServiceURL; import javax.management.remote.MBeanServerForwarder; +import javax.management.remote.rmi.RMIConnection; import javax.management.remote.rmi.RMIConnectorServer; import javax.management.remote.rmi.RMIJRMPServerImpl; import javax.management.remote.rmi.RMIServerImpl; import javax.rmi.ssl.SslRMIClientSocketFactory; import javax.rmi.ssl.SslRMIServerSocketFactory; +import javax.security.auth.Subject; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; @@ -63,6 +68,7 @@ import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.auth.rmi.RMIPasswordAuthenticator; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; /** * This class starts up an MBeanserver. If out of the box agent has been enabled then there are no @@ -223,7 +229,40 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry * The registry is exported on the defined management port 'port'. We will export the RMIConnectorServer * on 'port +1'. Use of these two well-defined ports will ease any navigation through firewall's. */ - final RMIServerImpl rmiConnectorServerStub = new RMIJRMPServerImpl(_jmxPortConnectorServer, csf, ssf, env); + final Map<String, String> connectionIdUsernameMap = new ConcurrentHashMap<String, String>(); + final RMIServerImpl rmiConnectorServerStub = new RMIJRMPServerImpl(_jmxPortConnectorServer, csf, ssf, env) + { + + /** + * Override makeClient so we can cache the username of the client in a Map keyed by connectionId. + * ConnectionId is guaranteed to be unique per client connection, according to the JMS spec. + * An instance of NotificationListener (mapCleanupListener) will be responsible for removing these Map + * entries. + * + * @see javax.management.remote.rmi.RMIJRMPServerImpl#makeClient(java.lang.String, javax.security.auth.Subject) + */ + @Override + protected RMIConnection makeClient(String connectionId, Subject subject) throws IOException + { + final RMIConnection makeClient = super.makeClient(connectionId, subject); + final UsernamePrincipal usernamePrincipalFromSubject = UsernamePrincipal.getUsernamePrincipalFromSubject(subject); + connectionIdUsernameMap.put(connectionId, usernamePrincipalFromSubject.getName()); + return makeClient; + } + }; + + // Create a Listener responsible for removing the map entries add by the #makeClient entry above. + final NotificationListener mapCleanupListener = new NotificationListener() + { + + @Override + public void handleNotification(Notification notification, Object handback) + { + final String connectionId = ((JMXConnectionNotification) notification).getConnectionId(); + connectionIdUsernameMap.remove(connectionId); + } + }; + String localHost; try { @@ -295,13 +334,26 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry MBeanServerForwarder mbsf = MBeanInvocationHandlerImpl.newProxyInstance(); _cs.setMBeanServerForwarder(mbsf); - NotificationFilterSupport filter = new NotificationFilterSupport(); - filter.enableType(JMXConnectionNotification.OPENED); - filter.enableType(JMXConnectionNotification.CLOSED); - filter.enableType(JMXConnectionNotification.FAILED); + // Get the handler that is used by the above MBInvocationHandler Proxy. - // which is the MBeanInvocationHandlerImpl and so also a NotificationListener - _cs.addNotificationListener((NotificationListener) Proxy.getInvocationHandler(mbsf), filter, null); + // which is the MBeanInvocationHandlerImpl and so also a NotificationListener. + final NotificationListener invocationHandler = (NotificationListener) Proxy.getInvocationHandler(mbsf); + + // Install a notification listener on OPENED, CLOSED, and FAILED, + // passing the map of connection-ids to usernames as hand-back data. + final NotificationFilterSupport invocationHandlerFilter = new NotificationFilterSupport(); + invocationHandlerFilter.enableType(JMXConnectionNotification.OPENED); + invocationHandlerFilter.enableType(JMXConnectionNotification.CLOSED); + invocationHandlerFilter.enableType(JMXConnectionNotification.FAILED); + _cs.addNotificationListener(invocationHandler, invocationHandlerFilter, connectionIdUsernameMap); + + // Install a second notification listener on CLOSED AND FAILED only to remove the entry from the + // Map. Here we rely on the fact that JMX will call the listeners in the order in which they are + // installed. + final NotificationFilterSupport mapCleanupHandlerFilter = new NotificationFilterSupport(); + mapCleanupHandlerFilter.enableType(JMXConnectionNotification.CLOSED); + mapCleanupHandlerFilter.enableType(JMXConnectionNotification.FAILED); + _cs.addNotificationListener(mapCleanupListener, mapCleanupHandlerFilter, null); _cs.start(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java index 169195304c..40a221e0ba 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java @@ -26,6 +26,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.security.AccessControlContext; import java.security.AccessController; +import java.util.Map; import java.util.Set; import javax.management.Attribute; @@ -45,6 +46,7 @@ import org.apache.log4j.Logger; import org.apache.qpid.server.logging.actors.ManagementActor; import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.access.Operation; @@ -56,22 +58,54 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati { private static final Logger _logger = Logger.getLogger(MBeanInvocationHandlerImpl.class); + private final IApplicationRegistry _appRegistry = ApplicationRegistry.getInstance(); private final static String DELEGATE = "JMImplementation:type=MBeanServerDelegate"; private MBeanServer _mbs; - private static ManagementActor _logActor; - + private final ManagementActor _logActor = new ManagementActor(_appRegistry.getRootMessageLogger()); + private final boolean _managementRightsInferAllAccess = + _appRegistry.getConfiguration().getManagementRightsInferAllAccess(); + public static MBeanServerForwarder newProxyInstance() { final InvocationHandler handler = new MBeanInvocationHandlerImpl(); final Class<?>[] interfaces = new Class[] { MBeanServerForwarder.class }; - - _logActor = new ManagementActor(ApplicationRegistry.getInstance().getRootMessageLogger()); - Object proxy = Proxy.newProxyInstance(MBeanServerForwarder.class.getClassLoader(), interfaces, handler); return MBeanServerForwarder.class.cast(proxy); } + private boolean invokeDirectly(String methodName, Object[] args, Subject subject) + { + // Allow operations performed locally on behalf of the connector server itself + if (subject == null) + { + return true; + } + + if (args == null || DELEGATE.equals(args[0])) + { + return true; + } + + // Allow querying available object names + if (methodName.equals("queryNames")) + { + return true; + } + + if (args[0] instanceof ObjectName) + { + ObjectName mbean = (ObjectName) args[0]; + + if(!DefaultManagedObject.DOMAIN.equalsIgnoreCase(mbean.getDomain())) + { + return true; + } + } + + return false; + } + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final String methodName = getMethodName(method, args); @@ -95,36 +129,24 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati return null; } + // Restrict access to "createMBean" and "unregisterMBean" to any user + if (methodName.equals("createMBean") || methodName.equals("unregisterMBean")) + { + _logger.debug("User trying to create or unregister an MBean"); + throw new SecurityException("Access denied: " + methodName); + } + // Retrieve Subject from current AccessControlContext AccessControlContext acc = AccessController.getContext(); Subject subject = Subject.getSubject(acc); try { - // Allow operations performed locally on behalf of the connector server itself - if (subject == null) + if(invokeDirectly(methodName, args, subject)) { return method.invoke(_mbs, args); } - - if (args == null || DELEGATE.equals(args[0])) - { - return method.invoke(_mbs, args); - } - - // Restrict access to "createMBean" and "unregisterMBean" to any user - if (methodName.equals("createMBean") || methodName.equals("unregisterMBean")) - { - _logger.debug("User trying to create or unregister an MBean"); - throw new SecurityException("Access denied: " + methodName); - } - - // Allow querying available object names - if (methodName.equals("queryNames")) - { - return method.invoke(_mbs, args); - } - + // Retrieve JMXPrincipal from Subject Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class); if (principals == null || principals.isEmpty()) @@ -134,23 +156,23 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati // Save the subject SecurityManager.setThreadSubject(subject); - + // Get the component, type and impact, which may be null String type = getType(method, args); String vhost = getVirtualHost(method, args); int impact = getImpact(method, args); - + // Get the security manager for the virtual host (if set) SecurityManager security; if (vhost == null) { - security = ApplicationRegistry.getInstance().getSecurityManager(); + security = _appRegistry.getSecurityManager(); } else { - security = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(vhost).getSecurityManager(); + security = _appRegistry.getVirtualHostRegistry().getVirtualHost(vhost).getSecurityManager(); } - + if (isAccessMethod(methodName) || impact == MBeanOperationInfo.INFO) { // Check for read-only method invocation permission @@ -159,25 +181,33 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati throw new SecurityException("Permission denied: Access " + methodName); } } - else if (isUpdateMethod(methodName)) - { - // Check for setting properties permission - if (!security.authoriseMethod(Operation.UPDATE, type, methodName)) - { - throw new SecurityException("Permission denied: Update " + methodName); - } - } - else - { - // Check for invoking/executing method action/operation permission - if (!security.authoriseMethod(Operation.EXECUTE, type, methodName)) - { - throw new SecurityException("Permission denied: Execute " + methodName); - } - } - - // Actually invoke the method - return method.invoke(_mbs, args); + else + { + // Check for setting properties permission + if (!security.authoriseMethod(Operation.UPDATE, type, methodName)) + { + throw new SecurityException("Permission denied: Update " + methodName); + } + } + + boolean oldAccessChecksDisabled = false; + if(_managementRightsInferAllAccess) + { + oldAccessChecksDisabled = SecurityManager.setAccessChecksDisabled(true); + } + + try + { + // Actually invoke the method + return method.invoke(_mbs, args); + } + finally + { + if(_managementRightsInferAllAccess) + { + SecurityManager.setAccessChecksDisabled(oldAccessChecksDisabled); + } + } } catch (InvocationTargetException e) { @@ -290,28 +320,44 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati return (methodName.startsWith("query") || methodName.startsWith("get") || methodName.startsWith("is")); } - - private boolean isUpdateMethod(String methodName) - { - //handle standard set methods from MBeanServer - return methodName.startsWith("set"); - } - - public void handleNotification(Notification notification, Object handback) + /** + * Receives notifications from the MBeanServer. + */ + public void handleNotification(final Notification notification, final Object handback) { assert notification instanceof JMXConnectionNotification; - // only RMI Connections are serviced here, Local API atta - // rmi://169.24.29.116 guest 3 - String[] connectionData = ((JMXConnectionNotification) notification).getConnectionId().split(" "); - String user = connectionData[1]; + final String connectionId = ((JMXConnectionNotification) notification).getConnectionId(); + final String type = notification.getType(); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Notification connectionId : " + connectionId + " type : " + type + + " Notification handback : " + handback); + } + + // Normally JMXManagedObjectRegistry provides a Map as handback data containing a map + // between connection id and username. + String user = null; + if (handback != null && handback instanceof Map) + { + final Map<String, String> connectionIdUsernameMap = (Map<String, String>) handback; + user = connectionIdUsernameMap.get(connectionId); + } + + // If user is still null, fallback to an unordered list of Principals from the connection id. + if (user == null) + { + final String[] splitConnectionId = connectionId.split(" "); + user = splitConnectionId[1]; + } - if (notification.getType().equals(JMXConnectionNotification.OPENED)) + if (JMXConnectionNotification.OPENED.equals(type)) { _logActor.message(ManagementConsoleMessages.OPEN(user)); } - else if (notification.getType().equals(JMXConnectionNotification.CLOSED) || - notification.getType().equals(JMXConnectionNotification.FAILED)) + else if (JMXConnectionNotification.CLOSED.equals(type) || + JMXConnectionNotification.FAILED.equals(type)) { _logActor.message(ManagementConsoleMessages.CLOSE(user)); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessage.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessage.java index 0e0b18aa2f..4fcbaa237e 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessage.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AMQMessage.java @@ -22,6 +22,7 @@ package org.apache.qpid.server.message; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.AMQChannel; @@ -65,7 +66,6 @@ public class AMQMessage extends AbstractServerMessageImpl WeakReference<AMQChannel> _channelRef; - public AMQMessage(StoredMessage<MessageMetaData> handle) { this(handle, null); @@ -122,7 +122,15 @@ public class AMQMessage extends AbstractServerMessageImpl public String getRoutingKey() { - // TODO + MessageMetaData messageMetaData = getMessageMetaData(); + if (messageMetaData != null) + { + AMQShortString routingKey = messageMetaData.getMessagePublishInfo().getRoutingKey(); + if (routingKey != null) + { + return routingKey.asString(); + } + } return null; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AbstractServerMessageImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AbstractServerMessageImpl.java index 186bb8601c..80c28332c0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AbstractServerMessageImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/AbstractServerMessageImpl.java @@ -1,3 +1,23 @@ +/* + * + * 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.message; import java.util.concurrent.atomic.AtomicInteger; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java index 31cf223428..2f30f260c9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/message/MessageTransferHeader.java @@ -79,7 +79,7 @@ class MessageTransferHeader implements AMQMessageHeader public byte getPriority() { - MessageDeliveryPriority priority = _deliveryProps == null + MessageDeliveryPriority priority = _deliveryProps == null || !_deliveryProps.hasPriority() ? MessageDeliveryPriority.MEDIUM : _deliveryProps.getPriority(); return (byte) priority.getValue(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties index aaab4f76cc..badeffca05 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties @@ -75,6 +75,7 @@ org.apache.qpid.server.exchange=0.0.0 org.apache.qpid.server.logging=0.0.0 org.apache.qpid.server.logging.actors=0.0.0 org.apache.qpid.server.logging.subjects=0.0.0 +org.apache.qpid.server.message=0.0.0 org.apache.qpid.server.management=0.0.0 org.apache.qpid.server.persistent=0.0.0 org.apache.qpid.server.plugins=0.0.0 diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index b960ce8608..4e5088808a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.protocol; +import java.io.DataOutput; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -36,36 +37,17 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicBoolean; - import javax.management.JMException; import javax.security.auth.Subject; import javax.security.sasl.SaslServer; - import org.apache.log4j.Logger; import org.apache.qpid.AMQChannelException; import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.codec.AMQCodecFactory; -import org.apache.qpid.codec.AMQDecoder; import org.apache.qpid.common.ClientProperties; -import org.apache.qpid.framing.AMQBody; -import org.apache.qpid.framing.AMQDataBlock; -import org.apache.qpid.framing.AMQFrame; -import org.apache.qpid.framing.AMQMethodBody; -import org.apache.qpid.framing.AMQProtocolHeaderException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ChannelCloseBody; -import org.apache.qpid.framing.ChannelCloseOkBody; -import org.apache.qpid.framing.ConnectionCloseBody; -import org.apache.qpid.framing.ContentBody; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.HeartbeatBody; -import org.apache.qpid.framing.MethodDispatcher; -import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.framing.ProtocolInitiation; -import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.framing.*; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.protocol.AMQMethodListener; @@ -87,11 +69,15 @@ import org.apache.qpid.server.management.Managable; import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.output.ProtocolOutputConverter; import org.apache.qpid.server.output.ProtocolOutputConverterRegistry; +import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; import org.apache.qpid.server.state.AMQState; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.stats.StatisticsCounter; +import org.apache.qpid.server.subscription.ClientDeliveryMethod; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionImpl; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; import org.apache.qpid.transport.Sender; @@ -139,7 +125,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr /* AMQP Version for this session */ private ProtocolVersion _protocolVersion = ProtocolVersion.getLatestSupportedVersion(); - + private MethodRegistry _methodRegistry = MethodRegistry.getMethodRegistry(_protocolVersion); private FieldTable _clientProperties; private final List<Task> _taskList = new CopyOnWriteArrayList<Task>(); @@ -173,6 +159,9 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr private NetworkConnection _network; private Sender<ByteBuffer> _sender; + private volatile boolean _deferFlush; + private long _lastReceivedTime; + public ManagedObject getManagedObject() { return _managedObject; @@ -240,14 +229,29 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr return _closing.get(); } + public synchronized void flushBatched() + { + _sender.flush(); + } + + + public ClientDeliveryMethod createDeliveryMethod(int channelId) + { + return new WriteDeliverMethod(channelId); + } + public void received(final ByteBuffer msg) { - _lastIoTime = System.currentTimeMillis(); + final long arrivalTime = System.currentTimeMillis(); + _lastReceivedTime = arrivalTime; + _lastIoTime = arrivalTime; try { final ArrayList<AMQDataBlock> dataBlocks = _codecFactory.getDecoder().decodeBuffer(msg); - for (AMQDataBlock dataBlock : dataBlocks) + final int len = dataBlocks.size(); + for (int i = 0; i < len; i++) { + AMQDataBlock dataBlock = dataBlocks.get(i); try { dataBlockReceived(dataBlock); @@ -347,7 +351,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr } } - private void protocolInitiationReceived(ProtocolInitiation pi) + private synchronized void protocolInitiationReceived(ProtocolInitiation pi) { // this ensures the codec never checks for a PI message again (_codecFactory.getDecoder()).setExpectProtocolInitiation(false); @@ -524,12 +528,15 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr */ public synchronized void writeFrame(AMQDataBlock frame) { - _lastSent = frame; + final ByteBuffer buf = asByteBuffer(frame); - _lastIoTime = System.currentTimeMillis(); _writtenBytes += buf.remaining(); _sender.send(buf); - _sender.flush(); + _lastIoTime = System.currentTimeMillis(); + if(!_deferFlush) + { + _sender.flush(); + } } public AMQShortString getContextKey() @@ -918,7 +925,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr private void setProtocolVersion(ProtocolVersion pv) { _protocolVersion = pv; - + _methodRegistry = MethodRegistry.getMethodRegistry(_protocolVersion); _protocolOutputConverter = ProtocolOutputConverterRegistry.getConverter(this); _dispatcher = ServerMethodDispatcherImpl.createMethodDispatcher(_stateManager, _protocolVersion); } @@ -1023,7 +1030,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr public MethodRegistry getMethodRegistry() { - return MethodRegistry.getMethodRegistry(getProtocolVersion()); + return _methodRegistry; } public MethodDispatcher getMethodDispatcher() @@ -1052,7 +1059,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr // Nothing } - public void writerIdle() + public synchronized void writerIdle() { _sender.send(asByteBuffer(HeartbeatBody.FRAME)); } @@ -1109,6 +1116,11 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr return _lastIoTime; } + public long getLastReceivedTime() + { + return _lastReceivedTime; + } + public ProtocolSessionIdentifier getSessionIdentifier() { return _sessionIdentifier; @@ -1395,16 +1407,220 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr _statisticsEnabled = enabled; } - @Override public boolean isSessionNameUnique(byte[] name) { // 0-8/0-9/0-9-1 sessions don't have names return true; } - @Override + public void setDeferFlush(boolean deferFlush) + { + _deferFlush = deferFlush; + } + + + public String getUserName() { return getAuthorizedPrincipal().getName(); } + + private static class ByteBufferOutputStream extends OutputStream + { + + + private final ByteBuffer _buf; + + public ByteBufferOutputStream(ByteBuffer buf) + { + _buf = buf; + } + + @Override + public void write(int b) throws IOException + { + _buf.put((byte) b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException + { + _buf.put(b, off, len); + } + } + + public final class WriteDeliverMethod + implements ClientDeliveryMethod + { + private final int _channelId; + + public WriteDeliverMethod(int channelId) + { + _channelId = channelId; + } + + public void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag) + throws AMQException + { + registerMessageDelivered(entry.getMessage().getSize()); + _protocolOutputConverter.writeDeliver(entry, _channelId, deliveryTag, ((SubscriptionImpl)sub).getConsumerTag()); + entry.incrementDeliveryCount(); + } + + } + + private static class BytesDataOutput implements DataOutput + { + int _pos = 0; + byte[] _buf; + + public BytesDataOutput(byte[] buf) + { + _buf = buf; + } + + public void setBuffer(byte[] buf) + { + _buf = buf; + _pos = 0; + } + + public void reset() + { + _pos = 0; + } + + public int length() + { + return _pos; + } + + public void write(int b) + { + _buf[_pos++] = (byte) b; + } + + public void write(byte[] b) + { + System.arraycopy(b, 0, _buf, _pos, b.length); + _pos+=b.length; + } + + + public void write(byte[] b, int off, int len) + { + System.arraycopy(b, off, _buf, _pos, len); + _pos+=len; + + } + + public void writeBoolean(boolean v) + { + _buf[_pos++] = v ? (byte) 1 : (byte) 0; + } + + public void writeByte(int v) + { + _buf[_pos++] = (byte) v; + } + + public void writeShort(int v) + { + _buf[_pos++] = (byte) (v >>> 8); + _buf[_pos++] = (byte) v; + } + + public void writeChar(int v) + { + _buf[_pos++] = (byte) (v >>> 8); + _buf[_pos++] = (byte) v; + } + + public void writeInt(int v) + { + _buf[_pos++] = (byte) (v >>> 24); + _buf[_pos++] = (byte) (v >>> 16); + _buf[_pos++] = (byte) (v >>> 8); + _buf[_pos++] = (byte) v; + } + + public void writeLong(long v) + { + _buf[_pos++] = (byte) (v >>> 56); + _buf[_pos++] = (byte) (v >>> 48); + _buf[_pos++] = (byte) (v >>> 40); + _buf[_pos++] = (byte) (v >>> 32); + _buf[_pos++] = (byte) (v >>> 24); + _buf[_pos++] = (byte) (v >>> 16); + _buf[_pos++] = (byte) (v >>> 8); + _buf[_pos++] = (byte)v; + } + + public void writeFloat(float v) + { + writeInt(Float.floatToIntBits(v)); + } + + public void writeDouble(double v) + { + writeLong(Double.doubleToLongBits(v)); + } + + public void writeBytes(String s) + { + int len = s.length(); + for (int i = 0 ; i < len ; i++) + { + _buf[_pos++] = ((byte)s.charAt(i)); + } + } + + public void writeChars(String s) + { + int len = s.length(); + for (int i = 0 ; i < len ; i++) + { + int v = s.charAt(i); + _buf[_pos++] = (byte) (v >>> 8); + _buf[_pos++] = (byte) v; + } + } + + public void writeUTF(String s) + { + int strlen = s.length(); + + int pos = _pos; + _pos+=2; + + + for (int i = 0; i < strlen; i++) + { + int c = s.charAt(i); + if ((c >= 0x0001) && (c <= 0x007F)) + { + c = s.charAt(i); + _buf[_pos++] = (byte) c; + + } + else if (c > 0x07FF) + { + _buf[_pos++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); + _buf[_pos++] = (byte) (0x80 | ((c >> 6) & 0x3F)); + _buf[_pos++] = (byte) (0x80 | (c & 0x3F)); + } + else + { + _buf[_pos++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); + _buf[_pos++] = (byte) (0x80 | (c & 0x3F)); + } + } + + int len = _pos - (pos + 2); + + _buf[pos++] = (byte) (len >>> 8); + _buf[pos] = (byte) len; + } + + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java index c1b5b02f8f..dfba10750c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java @@ -32,6 +32,7 @@ import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.logging.LogActor; import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.subscription.ClientDeliveryMethod; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.List; @@ -49,6 +50,14 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession, Auth boolean isClosing(); + void flushBatched(); + + void setDeferFlush(boolean defer); + + ClientDeliveryMethod createDeliveryMethod(int channelId); + + long getLastReceivedTime(); + public static final class ProtocolSessionIdentifier { private final Object _sessionIdentifier; @@ -77,15 +86,6 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession, Auth } /** - * Called when a protocol data block is received - * - * @param message the data block that has been received - * - * @throws Exception if processing the datablock fails - */ - void dataBlockReceived(AMQDataBlock message) throws Exception; - - /** * Get the context key associated with this session. Context key is described in the AMQ protocol specification (RFC * 6). * @@ -234,4 +234,5 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession, Auth List<AMQChannel> getChannels(); void mgmtCloseChannel(int channelId); + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java index 16d99de492..8d39420631 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java @@ -39,89 +39,44 @@ package org.apache.qpid.server.protocol; import java.util.Date; import java.util.List; - import javax.management.JMException; import javax.management.MBeanException; -import javax.management.MBeanNotificationInfo; import javax.management.NotCompliantMBeanException; -import javax.management.Notification; -import javax.management.ObjectName; -import javax.management.monitor.MonitorNotification; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; -import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.OpenType; -import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularDataSupport; -import javax.management.openmbean.TabularType; - import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ConnectionCloseBody; import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.management.common.mbeans.ManagedConnection; import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.ManagementActor; -import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.AbstractAMQManagedConnectionObject; import org.apache.qpid.server.management.ManagedObject; /** * This MBean class implements the management interface. In order to make more attributes, operations and notifications * available over JMX simply augment the ManagedConnection interface and add the appropriate implementation here. */ -@MBeanDescription("Management Bean for an AMQ Broker Connection") -public class AMQProtocolSessionMBean extends AMQManagedObject implements ManagedConnection +@MBeanDescription("Management Bean for an AMQ Broker 0-9-1/0-9/0-8 Connections") +public class AMQProtocolSessionMBean extends AbstractAMQManagedConnectionObject { private AMQProtocolSession _protocolSession = null; - private String _name = null; - // openmbean data types for representing the channel attributes - - private static final OpenType[] _channelAttributeTypes = - { SimpleType.INTEGER, SimpleType.BOOLEAN, SimpleType.STRING, SimpleType.INTEGER, SimpleType.BOOLEAN }; - private static CompositeType _channelType = null; // represents the data type for channel data - private static TabularType _channelsType = null; // Data type for list of channels type private static final AMQShortString BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION = - new AMQShortString("Broker Management Console has closed the connection."); + new AMQShortString(BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION_STR); - @MBeanConstructor("Creates an MBean exposing an AMQ Broker Connection") + @MBeanConstructor("Creates an MBean exposing an AMQ Broker 0-9-1/0-9/0-8 Connection") public AMQProtocolSessionMBean(AMQProtocolSession amqProtocolSession) throws NotCompliantMBeanException, OpenDataException { - super(ManagedConnection.class, ManagedConnection.TYPE); + super(amqProtocolSession.getRemoteAddress().toString()); _protocolSession = amqProtocolSession; - String remote = getRemoteAddress(); - _name = "anonymous".equals(remote) ? (remote + hashCode()) : remote; - init(); - } - - static - { - try - { - init(); - } - catch (JMException ex) - { - // This is not expected to ever occur. - throw new RuntimeException("Got JMException in static initializer.", ex); - } - } - - /** - * initialises the openmbean data types - */ - private static void init() throws OpenDataException - { - _channelType = - new CompositeType("Channel", "Channel Details", COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), - COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), _channelAttributeTypes); - _channelsType = new TabularType("Channels", "Channels", _channelType, TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()])); } public String getClientId() @@ -169,16 +124,6 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed return _protocolSession.getMaximumNumberOfChannels(); } - public void setMaximumNumberOfChannels(Long value) - { - _protocolSession.setMaximumNumberOfChannels(value); - } - - public String getObjectInstanceName() - { - return ObjectName.quote(_name); - } - /** * commits transactions for a transactional channel * @@ -321,25 +266,6 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed } } - @Override - public MBeanNotificationInfo[] getNotificationInfo() - { - String[] notificationTypes = new String[] { MonitorNotification.THRESHOLD_VALUE_EXCEEDED }; - String name = MonitorNotification.class.getName(); - String description = "Channel count has reached threshold value"; - MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description); - - return new MBeanNotificationInfo[] { info1 }; - } - - public void notifyClients(String notificationMsg) - { - Notification n = - new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, ++_notificationSequenceNumber, - System.currentTimeMillis(), notificationMsg); - _broadcaster.sendNotification(n); - } - public void resetStatistics() throws Exception { _protocolSession.resetStatistics(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java index 48a8a1bf42..5d4b8c603b 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java @@ -100,6 +100,12 @@ public class ProtocolEngine_0_10 extends InputHandler implements ServerProtocol return _network.getLocalAddress(); } + public void received(final ByteBuffer buf) + { + super.received(buf); + _connection.receivedComplete(); + } + public long getReadBytes() { return _readBytes; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java index a9ddf9bb0c..dd4bd2bb1c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java @@ -20,6 +20,14 @@ */ package org.apache.qpid.server.protocol.v1_0; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.qpid.AMQException; import org.apache.qpid.AMQInternalException; import org.apache.qpid.AMQInvalidArgumentException; import org.apache.qpid.AMQSecurityException; @@ -27,16 +35,28 @@ import org.apache.qpid.amqp_1_0.transport.DeliveryStateHandler; import org.apache.qpid.amqp_1_0.transport.LinkEndpoint; import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; import org.apache.qpid.amqp_1_0.transport.SendingLinkListener; -import org.apache.qpid.amqp_1_0.type.*; - -import org.apache.qpid.amqp_1_0.type.messaging.*; +import org.apache.qpid.amqp_1_0.type.AmqpErrorException; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.DeliveryState; +import org.apache.qpid.amqp_1_0.type.Outcome; +import org.apache.qpid.amqp_1_0.type.Symbol; +import org.apache.qpid.amqp_1_0.type.UnsignedInteger; +import org.apache.qpid.amqp_1_0.type.messaging.Accepted; +import org.apache.qpid.amqp_1_0.type.messaging.ExactSubjectFilter; +import org.apache.qpid.amqp_1_0.type.messaging.Filter; +import org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter; +import org.apache.qpid.amqp_1_0.type.messaging.MatchingSubjectFilter; +import org.apache.qpid.amqp_1_0.type.messaging.NoLocalFilter; +import org.apache.qpid.amqp_1_0.type.messaging.Released; import org.apache.qpid.amqp_1_0.type.messaging.Source; -import org.apache.qpid.amqp_1_0.type.transport.*; -import org.apache.qpid.AMQException; +import org.apache.qpid.amqp_1_0.type.messaging.StdDistMode; +import org.apache.qpid.amqp_1_0.type.messaging.TerminusDurability; +import org.apache.qpid.amqp_1_0.type.transport.AmqpError; +import org.apache.qpid.amqp_1_0.type.transport.Detach; import org.apache.qpid.amqp_1_0.type.transport.Error; +import org.apache.qpid.amqp_1_0.type.transport.Transfer; import org.apache.qpid.server.exchange.DirectExchange; import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.exchange.ExchangeType; import org.apache.qpid.server.exchange.TopicExchange; import org.apache.qpid.server.filter.JMSSelectorMessageFilter; import org.apache.qpid.server.filter.SimpleFilterManager; @@ -47,9 +67,6 @@ import org.apache.qpid.server.txn.AutoCommitTransaction; import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.virtualhost.VirtualHost; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryStateHandler { private VirtualHost _vhost; @@ -140,7 +157,7 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS try { queue = AMQQueueFactory.createAMQQueueImpl(UUID.randomUUID().toString(), false, null, true, - false, _vhost, Collections.EMPTY_MAP); + false, _vhost, Collections.EMPTY_MAP); Exchange exchange = ((ExchangeDestination) destination).getExchange(); String binding = ""; @@ -183,6 +200,9 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS catch (AMQInternalException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } catch (AMQException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java index 4b189652d3..e08e7f3e39 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java @@ -365,6 +365,9 @@ public class Session_1_0 implements SessionEventListener catch (AMQSecurityException e) { e.printStackTrace(); //TODO. + } catch (AMQException e) + { + e.printStackTrace(); //TODO } return queue; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java index fc64527f3b..5dea91b6d4 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java @@ -20,6 +20,13 @@ */ package org.apache.qpid.server.protocol.v1_0; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; +import org.apache.qpid.AMQException; import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; import org.apache.qpid.amqp_1_0.type.Binary; import org.apache.qpid.amqp_1_0.type.DeliveryState; @@ -31,24 +38,14 @@ import org.apache.qpid.amqp_1_0.type.messaging.StdDistMode; import org.apache.qpid.amqp_1_0.type.transaction.TransactionalState; import org.apache.qpid.amqp_1_0.type.transport.SenderSettleMode; import org.apache.qpid.amqp_1_0.type.transport.Transfer; - -import org.apache.qpid.AMQException; import org.apache.qpid.server.filter.FilterManager; import org.apache.qpid.server.logging.LogActor; -import org.apache.qpid.server.message.MessageTransferMessage; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.txn.ServerTransaction; -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.ReentrantLock; - class Subscription_1_0 implements Subscription { private SendingLink_1_0 _link; @@ -171,6 +168,17 @@ class Subscription_1_0 implements Subscription getEndpoint().detach(); } + public void send(QueueEntry entry, boolean batch) throws AMQException + { + // TODO + send(entry); + } + + public void flushBatched() + { + // TODO + } + public void send(final QueueEntry queueEntry) throws AMQException { //TODO @@ -296,6 +304,11 @@ class Subscription_1_0 implements Subscription return !hasCredit; } + public boolean trySendLock() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + public void suspend() { if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) @@ -314,6 +327,11 @@ class Subscription_1_0 implements Subscription _stateChangeLock.unlock(); } + public void releaseQueueEntry(QueueEntry queueEntryImpl) + { + //To change body of implemented methods use File | Settings | File Templates. + } + public void onDequeue(final QueueEntry queueEntry) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java index 371ae0de50..2c04a626ff 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java @@ -20,71 +20,25 @@ */ package org.apache.qpid.server.queue; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.subscription.SubscriptionList; -import org.apache.qpid.server.virtualhost.VirtualHost; - import java.util.Map; +import org.apache.qpid.server.virtualhost.VirtualHost; -public class AMQPriorityQueue extends SimpleAMQQueue +public class AMQPriorityQueue extends OutOfOrderQueue { - protected AMQPriorityQueue(final AMQShortString name, - final boolean durable, - final AMQShortString owner, - final boolean autoDelete, - boolean exclusive, - final VirtualHost virtualHost, - int priorities, Map<String, Object> arguments) - { - super(name, durable, owner, autoDelete, exclusive, virtualHost,new PriorityQueueList.Factory(priorities), arguments); - } - - public AMQPriorityQueue(String queueName, - boolean durable, - String owner, - boolean autoDelete, - boolean exclusive, VirtualHost virtualHost, int priorities, Map<String,Object> arguments) + protected AMQPriorityQueue(final String name, + final boolean durable, + final String owner, + final boolean autoDelete, + boolean exclusive, + final VirtualHost virtualHost, + Map<String, Object> arguments, + int priorities) { - this(queueName == null ? null : new AMQShortString(queueName), durable, owner == null ? null : new AMQShortString(owner), - autoDelete, exclusive,virtualHost, priorities, arguments); + super(name, durable, owner, autoDelete, exclusive, virtualHost, new PriorityQueueList.Factory(priorities), arguments); } public int getPriorities() { return ((PriorityQueueList) _entries).getPriorities(); } - - @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.isAvailable()) - { - final Subscription subscription = subIter.getNode().getSubscription(); - if(!subscription.isClosed()) - { - QueueContext context = (QueueContext) subscription.getQueueContext(); - if(context != null) - { - QueueEntry subnode = context._lastSeenEntry; - QueueEntry released = context._releasedEntry; - while(subnode != null && entry.compareTo(subnode) < 0 && entry.isAvailable() && (released == null || released.compareTo(entry) < 0)) - { - if(QueueContext._releasedUpdater.compareAndSet(context,released,entry)) - { - break; - } - else - { - subnode = context._lastSeenEntry; - released = context._releasedEntry; - } - } - } - } - - } - } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index 9140a13625..6dfdc5e8b4 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -213,6 +213,8 @@ public interface AMQQueue extends Managable, Comparable<AMQQueue>, ExchangeRefer void setAlternateExchange(Exchange exchange); + void setAlternateExchange(String exchangeName); + Map<String, Object> getArguments(); void checkCapacity(AMQChannel channel); @@ -272,4 +274,22 @@ public interface AMQQueue extends Managable, Comparable<AMQQueue>, ExchangeRefer ManagedObject getManagedObject(); void setExclusive(boolean exclusive) throws AMQException; + + /** + * Gets the maximum delivery count. If a message on this queue + * is delivered more than maximumDeliveryCount, the message will be + * routed to the {@link #getAlternateExchange()} (if set), or otherwise + * discarded. 0 indicates that maximum deliver count should not be enforced. + * + * @return maximum delivery count + */ + int getMaximumDeliveryCount(); + + /** + * Sets the maximum delivery count. + * + * @param maximumDeliveryCount maximum delivery count + */ + public void setMaximumDeliveryCount(final int maximumDeliveryCount); + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java index 5fbad74978..14ca147982 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java @@ -20,22 +20,33 @@ */ package org.apache.qpid.server.queue; +import java.util.HashMap; +import java.util.Map; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.configuration.QueueConfiguration; - -import java.util.Map; -import java.util.HashMap; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; public class AMQQueueFactory { - public static final AMQShortString X_QPID_PRIORITIES = new AMQShortString("x-qpid-priorities"); + public static final String X_QPID_PRIORITIES = "x-qpid-priorities"; public static final String QPID_LVQ_KEY = "qpid.LVQ_key"; public static final String QPID_LAST_VALUE_QUEUE = "qpid.last_value_queue"; public static final String QPID_LAST_VALUE_QUEUE_KEY = "qpid.last_value_queue_key"; + public static final String QPID_QUEUE_SORT_KEY = "qpid.queue_sort_key"; + + public static final String DLQ_ROUTING_KEY = "dlq"; + public static final String X_QPID_DLQ_ENABLED = "x-qpid-dlq-enabled"; + public static final String X_QPID_MAXIMUM_DELIVERY_COUNT = "x-qpid-maximum-delivery-count"; + public static final String DEFAULT_DLQ_NAME_SUFFIX = "_DLQ"; private abstract static class QueueProperty { @@ -80,6 +91,24 @@ public class AMQQueueFactory } + private abstract static class QueueIntegerProperty extends QueueProperty + { + public QueueIntegerProperty(String argumentName) + { + super(argumentName); + } + + public void setPropertyValue(AMQQueue queue, Object value) + { + if(value instanceof Number) + { + setPropertyValue(queue, ((Number)value).intValue()); + } + + } + abstract void setPropertyValue(AMQQueue queue, int value); + } + private static final QueueProperty[] DECLAREABLE_PROPERTIES = { new QueueLongProperty("x-qpid-maximum-message-age") { @@ -122,8 +151,14 @@ public class AMQQueueFactory { queue.setFlowResumeCapacity(value); } + }, + new QueueIntegerProperty(X_QPID_MAXIMUM_DELIVERY_COUNT) + { + public void setPropertyValue(AMQQueue queue, int value) + { + queue.setMaximumDeliveryCount(value); + } } - }; @@ -149,17 +184,31 @@ public class AMQQueueFactory String owner, boolean autoDelete, boolean exclusive, - VirtualHost virtualHost, Map<String, Object> arguments) throws AMQSecurityException + VirtualHost virtualHost, Map<String, Object> arguments) throws AMQSecurityException, AMQException { + if (queueName == null) + { + throw new IllegalArgumentException("Queue name must not be null"); + } + // Access check if (!virtualHost.getSecurityManager().authoriseCreateQueue(autoDelete, durable, exclusive, null, null, new AMQShortString(queueName), owner)) { String description = "Permission denied: queue-name '" + queueName + "'"; throw new AMQSecurityException(description); } - + + QueueConfiguration queueConfiguration = virtualHost.getConfiguration().getQueueConfiguration(queueName); + boolean isDLQEnabled = isDLQEnabled(autoDelete, arguments, queueConfiguration); + if (isDLQEnabled) + { + validateDLNames(queueName); + } + int priorities = 1; String conflationKey = null; + String sortingKey = null; + if(arguments != null) { if(arguments.containsKey(QPID_LAST_VALUE_QUEUE) || arguments.containsKey(QPID_LAST_VALUE_QUEUE_KEY)) @@ -170,24 +219,32 @@ public class AMQQueueFactory conflationKey = QPID_LVQ_KEY; } } - else if(arguments.containsKey(X_QPID_PRIORITIES.toString())) + else if(arguments.containsKey(X_QPID_PRIORITIES)) { - Object prioritiesObj = arguments.get(X_QPID_PRIORITIES.toString()); + Object prioritiesObj = arguments.get(X_QPID_PRIORITIES); if(prioritiesObj instanceof Number) { priorities = ((Number)prioritiesObj).intValue(); } } + else if(arguments.containsKey(QPID_QUEUE_SORT_KEY)) + { + sortingKey = (String)arguments.get(QPID_QUEUE_SORT_KEY); + } } AMQQueue q; - if(conflationKey != null) + if(sortingKey != null) + { + q = new SortedQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, sortingKey); + } + else if(conflationKey != null) { q = new ConflationQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, conflationKey); } else if(priorities > 1) { - q = new AMQPriorityQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, priorities, arguments); + q = new AMQPriorityQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, priorities); } else { @@ -209,10 +266,63 @@ public class AMQQueueFactory } } - return q; + if(isDLQEnabled) + { + final String dlExchangeName = getDeadLetterExchangeName(queueName); + final String dlQueueName = getDeadLetterQueueName(queueName); - } + final ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + final ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory(); + final QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + Exchange dlExchange = null; + synchronized(exchangeRegistry) + { + dlExchange = exchangeRegistry.getExchange(dlExchangeName); + + if(dlExchange == null) + { + dlExchange = exchangeFactory.createExchange(new AMQShortString(dlExchangeName), ExchangeDefaults.FANOUT_EXCHANGE_CLASS, true, false, 0); + + exchangeRegistry.registerExchange(dlExchange); + + //enter the dle in the persistent store + virtualHost.getDurableConfigurationStore().createExchange(dlExchange); + } + } + + AMQQueue dlQueue = null; + + synchronized(queueRegistry) + { + dlQueue = queueRegistry.getQueue(dlQueueName); + + if(dlQueue == null) + { + //set args to disable DLQ'ing/MDC from the DLQ itself, preventing loops etc + final Map<String, Object> args = new HashMap<String, Object>(); + args.put(X_QPID_DLQ_ENABLED, false); + args.put(X_QPID_MAXIMUM_DELIVERY_COUNT, 0); + + dlQueue = createAMQQueueImpl(dlQueueName, true, owner, false, exclusive, virtualHost, args); + //enter the dlq in the persistent store + virtualHost.getDurableConfigurationStore().createQueue(dlQueue, FieldTable.convertToFieldTable(args)); + } + } + + //ensure the queue is bound to the exchange + if(!dlExchange.isBound(DLQ_ROUTING_KEY, dlQueue)) + { + //actual routing key used does not matter due to use of fanout exchange, + //but we will make the key 'dlq' as it can be logged at creation. + virtualHost.getBindingFactory().addBinding(DLQ_ROUTING_KEY, dlQueue, dlExchange, null); + } + q.setAlternateExchange(dlExchange); + } + + return q; + } public static AMQQueue createAMQQueueImpl(QueueConfiguration config, VirtualHost host) throws AMQException { @@ -223,26 +333,30 @@ public class AMQQueueFactory boolean exclusive = config.getExclusive(); String owner = config.getOwner(); Map<String,Object> arguments = null; + if(config.isLVQ() || config.getLVQKey() != null) { - arguments = new HashMap<String,Object>(); arguments.put(QPID_LAST_VALUE_QUEUE, 1); arguments.put(QPID_LAST_VALUE_QUEUE_KEY, config.getLVQKey() == null ? QPID_LVQ_KEY : config.getLVQKey()); } - else + else if (config.getPriority() || config.getPriorities() > 0) + { + arguments = new HashMap<String,Object>(); + arguments.put(X_QPID_PRIORITIES, config.getPriorities() < 0 ? 10 : config.getPriorities()); + } + else if (config.getQueueSortKey() != null && !"".equals(config.getQueueSortKey())) + { + arguments = new HashMap<String,Object>(); + arguments.put(QPID_QUEUE_SORT_KEY, config.getQueueSortKey()); + } + if (!config.getAutoDelete() && config.isDeadLetterQueueEnabled()) { - boolean priority = config.getPriority(); - int priorities = config.getPriorities(); - if(priority || priorities > 0) + if (arguments == null) { arguments = new HashMap<String,Object>(); - if (priorities < 0) - { - priorities = 10; - } - arguments.put("x-qpid-priorities", priorities); } + arguments.put(X_QPID_DLQ_ENABLED, true); } if(config.isTopic()) @@ -259,4 +373,94 @@ public class AMQQueueFactory return q; } + + /** + * Validates DLQ and DLE names + * <p> + * DLQ name and DLQ exchange name need to be validated in order to keep + * integrity in cases when queue name passes validation check but DLQ name + * or DL exchange name fails to pass it. Otherwise, we might have situations + * when queue is created but DL exchange or/and DLQ creation fail. + * <p> + * + * @param name + * queue name + * @throws IllegalArgumentException + * thrown if length of queue name or exchange name exceed 255 + */ + protected static void validateDLNames(String name) + { + // check if DLQ name and DLQ exchange name do not exceed 255 + String exchangeName = getDeadLetterExchangeName(name); + if (exchangeName.length() > AMQShortString.MAX_LENGTH) + { + throw new IllegalArgumentException("DL exchange name '" + exchangeName + + "' length exceeds limit of " + AMQShortString.MAX_LENGTH + " characters for queue " + name); + } + String queueName = getDeadLetterQueueName(name); + if (queueName.length() > AMQShortString.MAX_LENGTH) + { + throw new IllegalArgumentException("DLQ queue name '" + queueName + "' length exceeds limit of " + + AMQShortString.MAX_LENGTH + " characters for queue " + name); + } + } + + /** + * Checks if DLQ is enabled for the queue. + * + * @param autoDelete + * queue auto-delete flag + * @param arguments + * queue arguments + * @param qConfig + * queue configuration + * @return true if DLQ enabled + */ + protected static boolean isDLQEnabled(boolean autoDelete, Map<String, Object> arguments, QueueConfiguration qConfig) + { + //feature is not to be enabled for temporary queues or when explicitly disabled by argument + if (!autoDelete) + { + boolean dlqArgumentPresent = arguments != null && arguments.containsKey(X_QPID_DLQ_ENABLED); + if (dlqArgumentPresent || qConfig.isDeadLetterQueueEnabled()) + { + boolean dlqEnabled = true; + if (dlqArgumentPresent) + { + Object argument = arguments.get(X_QPID_DLQ_ENABLED); + dlqEnabled = argument instanceof Boolean && ((Boolean)argument).booleanValue(); + } + return dlqEnabled; + } + } + return false; + } + + /** + * Generates a dead letter queue name for a given queue name + * + * @param name + * queue name + * @return DLQ name + */ + protected static String getDeadLetterQueueName(String name) + { + ServerConfiguration serverConfig = ApplicationRegistry.getInstance().getConfiguration(); + String dlQueueName = name + serverConfig.getDeadLetterQueueSuffix(); + return dlQueueName; + } + + /** + * Generates a dead letter exchange name for a given queue name + * + * @param name + * queue name + * @return DL exchange name + */ + protected static String getDeadLetterExchangeName(String name) + { + ServerConfiguration serverConfig = ApplicationRegistry.getInstance().getConfiguration(); + String dlExchangeName = name + serverConfig.getDeadLetterExchangeSuffix(); + return dlExchangeName; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java index c8eb118b11..b4765d6227 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java @@ -28,7 +28,7 @@ import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.management.common.mbeans.ManagedQueue; import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; -import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.management.AMQManagedObject; import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.message.ServerMessage; @@ -63,23 +63,25 @@ import java.util.*; /** * AMQQueueMBean is the management bean for an {@link AMQQueue}. * - * <p/><tablse id="crc"><caption>CRC Caption</caption> + * <p/><table id="crc"><caption>CRC Caption</caption> * <tr><th> Responsibilities <th> Collaborations * </table> */ @MBeanDescription("Management Interface for AMQQueue") public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, QueueNotificationListener { + /** Used for debugging purposes. */ private static final Logger _logger = Logger.getLogger(AMQQueueMBean.class); - private static final SimpleDateFormat _dateFormat = new SimpleDateFormat("MM-dd-yy HH:mm:ss.SSS z"); + /** Date/time format used for message expiration and message timestamp formatting */ + public static final String JMSTIMESTAMP_DATETIME_FORMAT = "MM-dd-yy HH:mm:ss.SSS z"; - private AMQQueue _queue = null; - private String _queueName = null; + private final AMQQueue _queue; + private final String _queueName; // OpenMBean data types for viewMessages method - private static OpenType[] _msgAttributeTypes = new OpenType[5]; // AMQ message attribute types. + private static OpenType[] _msgAttributeTypes = new OpenType[6]; // AMQ message attribute types. private static CompositeType _messageDataType = null; // Composite type for representing AMQ Message data. private static TabularType _messagelistDataType = null; // Datatype for representing AMQ messages list. @@ -138,6 +140,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que _msgAttributeTypes[2] = SimpleType.LONG; // For size _msgAttributeTypes[3] = SimpleType.BOOLEAN; // For redelivered _msgAttributeTypes[4] = SimpleType.LONG; // For queue position + _msgAttributeTypes[5] = SimpleType.INTEGER; // For delivery count _messageDataType = new CompositeType("Message", "AMQ Message", VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.toArray(new String[VIEW_MSGS_COMPOSITE_ITEM_NAMES_DESC.size()]), @@ -176,6 +179,11 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que return _queue.getMessageCount(); } + public Integer getMaximumDeliveryCount() + { + return _queue.getMaximumDeliveryCount(); + } + public Long getMaximumMessageSize() { return _queue.getMaximumMessageSize(); @@ -294,6 +302,18 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que } } + public void setAlternateExchange(String exchangeName) + { + _queue.setAlternateExchange(exchangeName); + } + + public String getAlternateExchange() + { + Exchange exchange = _queue.getAlternateExchange(); + String name = exchange == null ? null : exchange.getName(); + return name == null ? null : name; + } + /** * Checks if there is any notification to be send to the listeners */ @@ -471,7 +491,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que ContentHeaderBody headerBody = msg.getContentHeaderBody(); // Create header attributes list headerAttributes = getMessageHeaderProperties(headerBody); - itemValues = new Object[]{msg.getMessageId(), headerAttributes, headerBody.bodySize, queueEntry.isRedelivered(), position}; + itemValues = new Object[]{msg.getMessageId(), headerAttributes, headerBody.bodySize, queueEntry.isRedelivered(), position, queueEntry.getDeliveryCount()}; } else if(serverMsg instanceof MessageTransferMessage) { @@ -480,13 +500,13 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que // Create header attributes list headerAttributes = getMessageTransferMessageHeaderProps(msg); - itemValues = new Object[]{msg.getMessageNumber(), headerAttributes, msg.getSize(), queueEntry.isRedelivered(), position}; + itemValues = new Object[]{msg.getMessageNumber(), headerAttributes, msg.getSize(), queueEntry.isRedelivered(), position, queueEntry.getDeliveryCount()}; } else { //unknown message headerAttributes = new String[]{"N/A"}; - itemValues = new Object[]{serverMsg.getMessageNumber(), headerAttributes, serverMsg.getSize(), queueEntry.isRedelivered(), position}; + itemValues = new Object[]{serverMsg.getMessageNumber(), headerAttributes, serverMsg.getSize(), queueEntry.isRedelivered(), position, queueEntry.getDeliveryCount()}; } CompositeData messageData = new CompositeDataSupport(_messageDataType, @@ -523,13 +543,11 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que list.add("JMSPriority = " + headerProperties.getPriority()); list.add("JMSType = " + headerProperties.getType()); - long longDate = headerProperties.getExpiration(); - String strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null; - list.add("JMSExpiration = " + strDate); + final long expirationDate = headerProperties.getExpiration(); + final long timestampDate = headerProperties.getTimestamp(); - longDate = headerProperties.getTimestamp(); - strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null; - list.add("JMSTimestamp = " + strDate); + addStringifiedJMSTimestamoAndJMSExpiration(list, expirationDate, + timestampDate); return list.toArray(new String[list.size()]); } @@ -561,17 +579,32 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que list.add("JMSPriority = " + header.getPriority()); list.add("JMSType = " + header.getType()); - long longDate = header.getExpiration(); - String strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null; - list.add("JMSExpiration = " + strDate); - - longDate = header.getTimestamp(); - strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null; - list.add("JMSTimestamp = " + strDate); + final long expirationDate = header.getExpiration(); + final long timestampDate = header.getTimestamp(); + addStringifiedJMSTimestamoAndJMSExpiration(list, expirationDate, timestampDate); return list.toArray(new String[list.size()]); } + private void addStringifiedJMSTimestamoAndJMSExpiration(final List<String> list, + final long expirationDate, final long timestampDate) + { + final SimpleDateFormat dateFormat; + if (expirationDate != 0 || timestampDate != 0) + { + dateFormat = new SimpleDateFormat(JMSTIMESTAMP_DATETIME_FORMAT); + } + else + { + dateFormat = null; + } + + final String formattedExpirationDate = (expirationDate != 0) ? dateFormat.format(new Date(expirationDate)) : null; + final String formattedTimestampDate = (timestampDate != 0) ? dateFormat.format(new Date(timestampDate)) : null; + list.add("JMSExpiration = " + formattedExpirationDate); + list.add("JMSTimestamp = " + formattedTimestampDate); + } + /** * @see ManagedQueue#moveMessages * @param fromMessageId diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java index 2c1883e763..c4762c98c9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java @@ -54,7 +54,7 @@ public class ConflationQueueList extends SimpleQueueEntryList @Override - public QueueEntry add(final ServerMessage message) + public ConflationQueueEntry add(final ServerMessage message) { ConflationQueueEntry entry = (ConflationQueueEntry) (super.add(message)); AtomicReference<QueueEntry> latestValueReference = null; @@ -117,7 +117,7 @@ public class ConflationQueueList extends SimpleQueueEntryList } } - private final class ConflationQueueEntry extends QueueEntryImpl + private final class ConflationQueueEntry extends SimpleQueueEntryImpl { @@ -158,7 +158,7 @@ public class ConflationQueueList extends SimpleQueueEntryList _conflationKey = conflationKey; } - public QueueEntryList createQueueEntryList(AMQQueue queue) + public ConflationQueueList createQueueEntryList(AMQQueue queue) { return new ConflationQueueList(queue, _conflationKey); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InboundMessageAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InboundMessageAdapter.java index 77da08d8c4..26112d9f53 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InboundMessageAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InboundMessageAdapter.java @@ -24,7 +24,7 @@ package org.apache.qpid.server.queue; import org.apache.qpid.server.message.InboundMessage; import org.apache.qpid.server.message.AMQMessageHeader; -class InboundMessageAdapter implements InboundMessage +public class InboundMessageAdapter implements InboundMessage { private QueueEntry _entry; @@ -33,7 +33,7 @@ class InboundMessageAdapter implements InboundMessage { } - InboundMessageAdapter(QueueEntry entry) + public InboundMessageAdapter(QueueEntry entry) { _entry = entry; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java new file mode 100644 index 0000000000..b16d1eb8e3 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java @@ -0,0 +1,53 @@ +package org.apache.qpid.server.queue; + +import java.util.Map; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionList; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public abstract class OutOfOrderQueue extends SimpleAMQQueue +{ + protected OutOfOrderQueue(String name, boolean durable, String owner, + boolean autoDelete, boolean exclusive, VirtualHost virtualHost, + QueueEntryListFactory entryListFactory, Map<String, Object> arguments) + { + super(name, durable, owner, autoDelete, exclusive, virtualHost, entryListFactory, arguments); + } + + @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(); + if(!subscription.isClosed()) + { + QueueContext context = (QueueContext) subscription.getQueueContext(); + if(context != null) + { + QueueEntry subnode = context._lastSeenEntry; + QueueEntry released = context._releasedEntry; + + while(subnode != null && entry.compareTo(subnode) < 0 && !entry.isAcquired() + && (released == null || released.compareTo(entry) > 0)) + { + if(QueueContext._releasedUpdater.compareAndSet(context,released,entry)) + { + break; + } + else + { + subnode = context._lastSeenEntry; + released = context._releasedEntry; + } + + } + } + } + + } + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java index 0c6b84d2b6..79d3ab5bd0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java @@ -20,21 +20,19 @@ */ package org.apache.qpid.server.queue; -import org.apache.qpid.framing.CommonContentHeaderProperties; -import org.apache.qpid.AMQException; import org.apache.qpid.server.message.ServerMessage; -public class PriorityQueueList implements QueueEntryList +public class PriorityQueueList implements QueueEntryList<SimpleQueueEntryImpl> { private final AMQQueue _queue; - private final QueueEntryList[] _priorityLists; + private final SimpleQueueEntryList[] _priorityLists; private final int _priorities; private final int _priorityOffset; public PriorityQueueList(AMQQueue queue, int priorities) { _queue = queue; - _priorityLists = new QueueEntryList[priorities]; + _priorityLists = new SimpleQueueEntryList[priorities]; _priorities = priorities; _priorityOffset = 5-((priorities + 1)/2); for(int i = 0; i < priorities; i++) @@ -53,7 +51,7 @@ public class PriorityQueueList implements QueueEntryList return _queue; } - public QueueEntry add(ServerMessage message) + public SimpleQueueEntryImpl add(ServerMessage message) { int index = message.getMessageHeader().getPriority() - _priorityOffset; if(index >= _priorities) @@ -68,31 +66,30 @@ public class PriorityQueueList implements QueueEntryList } - public QueueEntry next(QueueEntry node) + public SimpleQueueEntryImpl next(SimpleQueueEntryImpl node) { - QueueEntryImpl nodeImpl = (QueueEntryImpl)node; - QueueEntry next = nodeImpl.getNext(); + SimpleQueueEntryImpl next = node.getNextValidEntry(); if(next == null) { - QueueEntryList nodeEntryList = nodeImpl.getQueueEntryList(); + final QueueEntryList<?> nodeEntryList = node.getQueueEntryList(); int index; for(index = _priorityLists.length-1; _priorityLists[index] != nodeEntryList; index--); while(next == null && index != 0) { index--; - next = ((QueueEntryImpl)_priorityLists[index].getHead()).getNext(); + next = _priorityLists[index].getHead().getNextValidEntry(); } } return next; } - private final class PriorityQueueEntryListIterator implements QueueEntryIterator + private final class PriorityQueueEntryListIterator implements QueueEntryIterator<SimpleQueueEntryImpl> { - private final QueueEntryIterator[] _iterators = new QueueEntryIterator[ _priorityLists.length ]; - private QueueEntry _lastNode; + private final SimpleQueueEntryList.QueueEntryIteratorImpl[] _iterators = new SimpleQueueEntryList.QueueEntryIteratorImpl[ _priorityLists.length ]; + private SimpleQueueEntryImpl _lastNode; PriorityQueueEntryListIterator() { @@ -116,7 +113,7 @@ public class PriorityQueueList implements QueueEntryList return true; } - public QueueEntry getNode() + public SimpleQueueEntryImpl getNode() { return _lastNode; } @@ -135,16 +132,21 @@ public class PriorityQueueList implements QueueEntryList } } - public QueueEntryIterator iterator() + public PriorityQueueEntryListIterator iterator() { return new PriorityQueueEntryListIterator(); } - public QueueEntry getHead() + public SimpleQueueEntryImpl getHead() { return _priorityLists[_priorities-1].getHead(); } + public void entryDeleted(final SimpleQueueEntryImpl queueEntry) + { + + } + static class Factory implements QueueEntryListFactory { private final int _priorities; @@ -154,7 +156,7 @@ public class PriorityQueueList implements QueueEntryList _priorities = priorities; } - public QueueEntryList createQueueEntryList(AMQQueue queue) + public PriorityQueueList createQueueEntryList(AMQQueue queue) { return new PriorityQueueList(queue, _priorities); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java index be29245901..37fad54c07 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java @@ -1,5 +1,7 @@ package org.apache.qpid.server.queue; +import java.util.Collection; + import org.apache.qpid.AMQException; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.message.ServerMessage; @@ -214,6 +216,10 @@ public interface QueueEntry extends Comparable<QueueEntry>, Filterable boolean isQueueDeleted(); + QueueEntry getNextNode(); + + QueueEntry getNextValidEntry(); + void addStateChangeListener(StateChangeListener listener); boolean removeStateChangeListener(StateChangeListener listener); @@ -230,4 +236,16 @@ public interface QueueEntry extends Comparable<QueueEntry>, Filterable * @return true if entry is either DEQUED or DELETED state */ boolean isDispensed(); + + /** + * Number of times this queue entry has been delivered. + * + * @return delivery count + */ + int getDeliveryCount(); + + void incrementDeliveryCount(); + + void decrementDeliveryCount(); + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java index 5b57e40a82..f1e50427b1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -20,8 +20,14 @@ */ package org.apache.qpid.server.queue; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.message.AMQMessageHeader; @@ -31,23 +37,11 @@ import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.txn.AutoCommitTransaction; import org.apache.qpid.server.txn.ServerTransaction; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.atomic.AtomicLongFieldUpdater; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; - - -public class QueueEntryImpl implements QueueEntry +public abstract class QueueEntryImpl implements QueueEntry { - - /** - * Used for debugging purposes. - */ private static final Logger _log = Logger.getLogger(QueueEntryImpl.class); - private final SimpleQueueEntryList _queueEntryList; + private final QueueEntryList _queueEntryList; private MessageReference _message; @@ -80,22 +74,26 @@ public class QueueEntryImpl implements QueueEntry private volatile long _entryId; - volatile QueueEntryImpl _next; - private static final int DELIVERED_TO_CONSUMER = 1; private static final int REDELIVERED = 2; private volatile int _deliveryState; + /** Number of times this message has been delivered */ + private volatile int _deliveryCount = 0; + private static final AtomicIntegerFieldUpdater<QueueEntryImpl> _deliveryCountUpdater = AtomicIntegerFieldUpdater + .newUpdater(QueueEntryImpl.class, "_deliveryCount"); + - QueueEntryImpl(SimpleQueueEntryList queueEntryList) + + public QueueEntryImpl(QueueEntryList<?> queueEntryList) { this(queueEntryList,null,Long.MIN_VALUE); _state = DELETED_STATE; } - public QueueEntryImpl(SimpleQueueEntryList queueEntryList, ServerMessage message, final long entryId) + public QueueEntryImpl(QueueEntryList<?> queueEntryList, ServerMessage message, final long entryId) { _queueEntryList = queueEntryList; @@ -104,7 +102,7 @@ public class QueueEntryImpl implements QueueEntry _entryIdUpdater.set(this, entryId); } - public QueueEntryImpl(SimpleQueueEntryList queueEntryList, ServerMessage message) + public QueueEntryImpl(QueueEntryList<?> queueEntryList, ServerMessage message) { _queueEntryList = queueEntryList; _message = message == null ? null : message.newReference(); @@ -233,8 +231,13 @@ public class QueueEntryImpl implements QueueEntry if(state instanceof SubscriptionAcquiredState) { getQueue().decrementUnackedMsgCount(); + Subscription subscription = ((SubscriptionAcquiredState)state).getSubscription(); + if (subscription != null) + { + subscription.releaseQueueEntry(this); + } } - + if(!getQueue().isDeleted()) { getQueue().requeue(this); @@ -311,16 +314,15 @@ public class QueueEntryImpl implements QueueEntry public Subscription getDeliveredSubscription() { - EntryState state = _state; - if (state instanceof SubscriptionAcquiredState) - { - return ((SubscriptionAcquiredState) state).getSubscription(); - } - else - { - return null; - } - + EntryState state = _state; + if (state instanceof SubscriptionAcquiredState) + { + return ((SubscriptionAcquiredState) state).getSubscription(); + } + else + { + return null; + } } public void reject() @@ -409,50 +411,51 @@ public class QueueEntryImpl implements QueueEntry public void routeToAlternate() { final AMQQueue currentQueue = getQueue(); - Exchange alternateExchange = currentQueue.getAlternateExchange(); + Exchange alternateExchange = currentQueue.getAlternateExchange(); - if(alternateExchange != null) + if (alternateExchange != null) + { + final List<? extends BaseQueue> rerouteQueues = alternateExchange.route(new InboundMessageAdapter(this)); + final ServerMessage message = getMessage(); + if (rerouteQueues != null && rerouteQueues.size() != 0) { - final List<? extends BaseQueue> rerouteQueues = alternateExchange.route(new InboundMessageAdapter(this)); - final ServerMessage message = getMessage(); - if(rerouteQueues != null && rerouteQueues.size() != 0) - { - ServerTransaction txn = new AutoCommitTransaction(getQueue().getVirtualHost().getTransactionLog()); - txn.enqueue(rerouteQueues, message, new ServerTransaction.Action() { - public void postCommit() + ServerTransaction txn = new AutoCommitTransaction(getQueue().getVirtualHost().getTransactionLog()); + + txn.enqueue(rerouteQueues, message, new ServerTransaction.Action() + { + public void postCommit() + { + try { - try + for (BaseQueue queue : rerouteQueues) { - for(BaseQueue queue : rerouteQueues) - { - queue.enqueue(message); - } - } - catch (AMQException e) - { - throw new RuntimeException(e); + queue.enqueue(message); } } - - public void onRollback() + catch (AMQException e) { - + throw new RuntimeException(e); } - }); - txn.dequeue(currentQueue,message, - new ServerTransaction.Action() - { - public void postCommit() - { - discard(); - } - - public void onRollback() - { - - } - }); + } + + public void onRollback() + { + + } + }); + txn.dequeue(currentQueue, message, new ServerTransaction.Action() + { + public void postCommit() + { + discard(); + } + + public void onRollback() + { + + } + }); } } } @@ -492,33 +495,6 @@ public class QueueEntryImpl implements QueueEntry return getEntryId() > other.getEntryId() ? 1 : getEntryId() < other.getEntryId() ? -1 : 0; } - public QueueEntryImpl getNext() - { - - QueueEntryImpl next = nextNode(); - while(next != null && next.isDispensed() ) - { - - 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; @@ -530,7 +506,7 @@ public class QueueEntryImpl implements QueueEntry if(state != DELETED_STATE && _stateUpdater.compareAndSet(this,state,DELETED_STATE)) { - _queueEntryList.advanceHead(); + _queueEntryList.entryDeleted(this); return true; } else @@ -554,4 +530,19 @@ public class QueueEntryImpl implements QueueEntry return _state.isDispensed(); } + public int getDeliveryCount() + { + return _deliveryCount; + } + + public void incrementDeliveryCount() + { + _deliveryCountUpdater.incrementAndGet(this); + } + + public void decrementDeliveryCount() + { + _deliveryCountUpdater.decrementAndGet(this); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java index c5c115a2d1..73ebb0f300 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java @@ -20,11 +20,11 @@ */ package org.apache.qpid.server.queue; -public interface QueueEntryIterator +public interface QueueEntryIterator<QE extends QueueEntry> { boolean atTail(); - QueueEntry getNode(); + QE getNode(); boolean advance(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java index b4042ce02c..77c4b912e0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java @@ -22,15 +22,17 @@ package org.apache.qpid.server.queue; import org.apache.qpid.server.message.ServerMessage; -public interface QueueEntryList +public interface QueueEntryList<Q extends QueueEntry> { AMQQueue getQueue(); - QueueEntry add(ServerMessage message); + Q add(ServerMessage message); - QueueEntry next(QueueEntry node); + Q next(Q node); - QueueEntryIterator iterator(); + QueueEntryIterator<Q> iterator(); - QueueEntry getHead(); + Q getHead(); + + void entryDeleted(Q queueEntry); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRunner.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRunner.java index 7e1d57e205..5270f9f740 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRunner.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRunner.java @@ -27,6 +27,11 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.queue.QueueRunner; import org.apache.qpid.server.queue.SimpleAMQQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + /** * QueueRunners are Runnables used to process a queue when requiring * asynchronous message delivery to subscriptions, which is necessary @@ -37,33 +42,64 @@ public class QueueRunner implements ReadWriteRunnable { private static final Logger _logger = Logger.getLogger(QueueRunner.class); - private final String _name; private final SimpleAMQQueue _queue; - public QueueRunner(SimpleAMQQueue queue, long count) + private static int IDLE = 0; + private static int SCHEDULED = 1; + private static int RUNNING = 2; + + + private final AtomicInteger _scheduled = new AtomicInteger(IDLE); + + private static final long ITERATIONS = SimpleAMQQueue.MAX_ASYNC_DELIVERIES; + private final AtomicBoolean _stateChange = new AtomicBoolean(); + + private final AtomicLong _lastRunAgain = new AtomicLong(); + private final AtomicLong _lastRunTime = new AtomicLong(); + + private long _runs; + private long _continues; + + public QueueRunner(SimpleAMQQueue queue) { _queue = queue; - _name = "QueueRunner-" + count + "-" + queue.getLogActor(); } + private int trouble = 0; + public void run() { - String originalName = Thread.currentThread().getName(); - try + if(_scheduled.compareAndSet(SCHEDULED,RUNNING)) { - Thread.currentThread().setName(_name); - CurrentActor.set(_queue.getLogActor()); + long runAgain = Long.MIN_VALUE; + _stateChange.set(false); + try + { + CurrentActor.set(_queue.getLogActor()); + + runAgain = _queue.processQueue(this); + } + catch (AMQException e) + { + _logger.error("Exception during asynchronous delivery by " + toString(), e); + } + finally + { + CurrentActor.remove(); + } + _scheduled.compareAndSet(RUNNING, IDLE); + long stateChangeCount = _queue.getStateChangeCount(); + _lastRunAgain.set(runAgain); + _lastRunTime.set(System.nanoTime()); + if(runAgain == 0L || runAgain != stateChangeCount || _stateChange.compareAndSet(true,false)) + { + _continues++; + if(_scheduled.compareAndSet(IDLE, SCHEDULED)) + { + _queue.execute(this); + } + } - _queue.processQueue(this); - } - catch (AMQException e) - { - _logger.error("Exception during asynchronous delivery by " + _name, e); - } - finally - { - CurrentActor.remove(); - Thread.currentThread().setName(originalName); } } @@ -79,6 +115,21 @@ public class QueueRunner implements ReadWriteRunnable public String toString() { - return _name; + return "QueueRunner-" + _queue.getLogActor(); } -}
\ No newline at end of file + + public void execute(Executor executor) + { + _stateChange.set(true); + if(_scheduled.compareAndSet(IDLE, SCHEDULED)) + { + executor.execute(this); + } + } + + public boolean isIdle() + { + return _scheduled.get() == IDLE; + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java index 7effb1c0f8..08dab4e5fc 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -155,11 +155,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private final Set<NotificationCheck> _notificationChecks = EnumSet.noneOf(NotificationCheck.class); - static final int MAX_ASYNC_DELIVERIES = 10; + static final int MAX_ASYNC_DELIVERIES = 80; private final AtomicLong _stateChangeCount = new AtomicLong(Long.MIN_VALUE); - private AtomicReference<Runnable> _asynchronousRunner = new AtomicReference<Runnable>(null); + private final Executor _asyncDelivery; private AtomicInteger _deliveredMessages = new AtomicInteger(); private AtomicBoolean _stopped = new AtomicBoolean(false); @@ -188,6 +188,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private ConfigurationPlugin _queueConfiguration; private final boolean _isTopic; + /** the maximum delivery count for each message on this queue or 0 if maximum delivery count is not to be enforced. */ + private int _maximumDeliveryCount = ApplicationRegistry.getInstance().getConfiguration().getMaxDeliveryCount(); protected SimpleAMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, Map<String,Object> arguments) { @@ -358,6 +360,22 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _alternateExchange = exchange; } + public void setAlternateExchange(String exchangeName) + { + if(exchangeName == null || exchangeName.equals("")) + { + _alternateExchange = null; + return; + } + + Exchange exchange = getVirtualHost().getExchangeRegistry().getExchange(new AMQShortString(exchangeName)); + if (exchange == null) + { + throw new RuntimeException("Exchange '" + exchangeName + "' is not registered with the VirtualHost."); + } + setAlternateExchange(exchange); + } + public Map<String, Object> getArguments() { return _arguments; @@ -528,13 +546,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener //Reconfigure the queue for to reflect this new binding. ConfigurationPlugin config = getVirtualHost().getConfiguration().getQueueConfiguration(this); - if (_logger.isDebugEnabled()) - { - _logger.debug("Reconfiguring queue(" + this + ") with config:" + config + " was "+ _queueConfiguration); - } - if (config != null) { + if (_logger.isDebugEnabled()) + { + _logger.debug("Reconfiguring queue(" + this + ") with config:" + config + " was "+ _queueConfiguration); + } // Reconfigure with new config. configure(config); } @@ -575,40 +592,22 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public void enqueue(ServerMessage message, PostEnqueueAction action) throws AMQException { + incrementTxnEnqueueStats(message); + incrementQueueCount(); + incrementQueueSize(message); + _totalMessagesReceived.incrementAndGet(); - Subscription exclusiveSub = _exclusiveSubscriber; + final Subscription exclusiveSub = _exclusiveSubscriber; if(!_isTopic || _subscriptionList.size()!=0) { - incrementTxnEnqueueStats(message); - incrementQueueCount(); - incrementQueueSize(message); - - QueueEntry entry; + QueueEntry entry = _entries.add(message); - if (exclusiveSub != null) + if(action != null || (exclusiveSub == null && _queueRunner.isIdle())) { - exclusiveSub.getSendLock(); - - try - { - entry = _entries.add(message); - - 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 = _subscriptionList.getMarkedNode(); SubscriptionList.SubscriptionNode nextNode = node.findNext(); @@ -654,12 +653,20 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } } + if (entry.isAvailable()) { checkSubscriptionsNotAheadOfDelivery(entry); - deliverAsync(); + if (exclusiveSub != null) + { + deliverAsync(exclusiveSub); + } + else + { + deliverAsync(); + } } if(_managedObject != null) @@ -678,30 +685,32 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener throws AMQException { - sub.getSendLock(); - try + if(sub.trySendLock()) { - if (subscriptionReadyAndHasInterest(sub, entry) - && !sub.isSuspended()) + try { - if (!sub.wouldSuspend(entry)) + if (subscriptionReadyAndHasInterest(sub, entry) + && !sub.isSuspended()) { - if (sub.acquires() && !entry.acquire(sub)) + if (!sub.wouldSuspend(entry)) { - // restore credit here that would have been taken away by wouldSuspend since we didn't manage - // to acquire the entry for this subscription - sub.onDequeue(entry); - } - else - { - deliverMessage(sub, entry); + if (sub.acquires() && !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, false); + } } } } - } - finally - { - sub.releaseSendLock(); + finally + { + sub.releaseSendLock(); + } } } @@ -745,7 +754,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _byteTxnDequeues.addAndGet(entry.getSize()); } - private void deliverMessage(final Subscription sub, final QueueEntry entry) + private void deliverMessage(final Subscription sub, final QueueEntry entry, boolean batch) throws AMQException { setLastSeenEntry(sub, entry); @@ -753,7 +762,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _deliveredMessages.incrementAndGet(); incrementUnackedMsgCount(); - sub.send(entry); + sub.send(entry, batch); if(_isTopic) { @@ -893,7 +902,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { if (!subscription.isClosed()) { - deliverMessage(subscription, entry); + deliverMessage(subscription, entry, false); return true; } else @@ -1035,6 +1044,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _exclusiveSubscriber = exclusiveSubscriber; } + long getStateChangeCount() + { + return _stateChangeCount.get(); + } + + public static interface QueueEntryFilter { public boolean accept(QueueEntry entry); @@ -1335,7 +1350,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener QueueEntryIterator queueListIterator = _entries.iterator(); long count = 0; - ServerTransaction txn = new LocalTransaction(getVirtualHost().getTransactionLog()); + ServerTransaction txn = new LocalTransaction(getVirtualHost().getMessageStore()); while (queueListIterator.advance()) { @@ -1358,7 +1373,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener private void dequeueEntry(final QueueEntry node) { - ServerTransaction txn = new AutoCommitTransaction(getVirtualHost().getTransactionLog()); + ServerTransaction txn = new AutoCommitTransaction(getVirtualHost().getMessageStore()); dequeueEntry(node, txn); } @@ -1435,7 +1450,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } }); - ServerTransaction txn = new LocalTransaction(getVirtualHost().getTransactionLog()); + ServerTransaction txn = new LocalTransaction(getVirtualHost().getMessageStore()); if(_alternateExchange != null) { @@ -1604,26 +1619,34 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } } + private QueueRunner _queueRunner = new QueueRunner(this); public void deliverAsync() { - QueueRunner runner = new QueueRunner(this, _stateChangeCount.incrementAndGet()); + _stateChangeCount.incrementAndGet(); + + _queueRunner.execute(_asyncDelivery); - if (_asynchronousRunner.compareAndSet(null, runner)) - { - _asyncDelivery.execute(runner); - } } public void deliverAsync(Subscription sub) { - SubFlushRunner flusher = (SubFlushRunner) sub.get(SUB_FLUSH_RUNNER); - if(flusher == null) + //_stateChangeCount.incrementAndGet(); + if(_exclusiveSubscriber == null) { - flusher = new SubFlushRunner(sub); - sub.set(SUB_FLUSH_RUNNER, flusher); + deliverAsync(); } - _asyncDelivery.execute(flusher); + else + { + SubFlushRunner flusher = (SubFlushRunner) sub.get(SUB_FLUSH_RUNNER); + if(flusher == null) + { + flusher = new SubFlushRunner(sub); + sub.set(SUB_FLUSH_RUNNER, flusher); + } + flusher.execute(_asyncDelivery); + } + } public void flushSubscription(Subscription sub) throws AMQException @@ -1639,25 +1662,49 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public boolean flushSubscription(Subscription sub, long iterations) throws AMQException { boolean atTail = false; + final boolean keepSendLockHeld = iterations <= SimpleAMQQueue.MAX_ASYNC_DELIVERIES; + boolean queueEmpty = false; - while (!sub.isSuspended() && !atTail && iterations != 0) + try { - boolean queueEmpty = false; - try + + if(keepSendLockHeld) { sub.getSendLock(); - atTail = attemptDelivery(sub); - if (atTail && getNextAvailableEntry(sub) == null) + } + + while (!sub.isSuspended() && !atTail && iterations != 0) + { + try { - queueEmpty = true; + if(!keepSendLockHeld) + { + sub.getSendLock(); + } + + atTail = attemptDelivery(sub, true); + if (atTail && getNextAvailableEntry(sub) == null) + { + queueEmpty = true; + } + else if (!atTail) + { + iterations--; + } } - else if (!atTail) + finally { - iterations--; + if(!keepSendLockHeld) + { + sub.releaseSendLock(); + } } } - finally + } + finally + { + if(keepSendLockHeld) { sub.releaseSendLock(); } @@ -1665,8 +1712,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { sub.queueEmpty(); } + sub.flushBatched(); + } + // 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". @@ -1684,11 +1734,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener * * Looks up the next node for the subscription and attempts to deliver it. * + * * @param sub + * @param batch * @return true if we have completed all possible deliveries for this sub. * @throws AMQException */ - private boolean attemptDelivery(Subscription sub) throws AMQException + private boolean attemptDelivery(Subscription sub, boolean batch) throws AMQException { boolean atTail = false; @@ -1706,11 +1758,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { if (sub.acquires() && !node.acquire(sub)) { - sub.onDequeue(node); + // 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(node); } else { - deliverMessage(sub, node); + deliverMessage(sub, node, batch); } } @@ -1814,23 +1868,26 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener * @param runner the Runner to schedule * @throws AMQException */ - public void processQueue(QueueRunner runner) throws AMQException + public long processQueue(QueueRunner runner) throws AMQException { - long stateChangeCount; + long stateChangeCount = Long.MIN_VALUE; long previousStateChangeCount = Long.MIN_VALUE; + long rVal = Long.MIN_VALUE; boolean deliveryIncomplete = true; boolean lastLoop = false; int iterations = MAX_ASYNC_DELIVERIES; - _asynchronousRunner.compareAndSet(runner, null); + final int numSubs = _subscriptionList.size(); + + final int perSub = Math.max(iterations / Math.max(numSubs,1), 1); // For every message enqueue/requeue the we fire deliveryAsync() which // increases _stateChangeCount. If _sCC changes whilst we are in our loop // (detected by setting previousStateChangeCount to stateChangeCount in the loop body) // then we will continue to run for a maximum of iterations. // So whilst delivery/rejection is going on a processQueue thread will be running - while (iterations != 0 && ((previousStateChangeCount != (stateChangeCount = _stateChangeCount.get())) || deliveryIncomplete) && _asynchronousRunner.compareAndSet(null, runner)) + while (iterations != 0 && ((previousStateChangeCount != (stateChangeCount = _stateChangeCount.get())) || deliveryIncomplete)) { // 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 @@ -1841,6 +1898,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener //further asynchronous delivery is required since the //previous loop. keep going if iteration slicing allows. lastLoop = false; + rVal = stateChangeCount; } previousStateChangeCount = stateChangeCount; @@ -1853,30 +1911,43 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { Subscription sub = subscriptionIter.getNode().getSubscription(); sub.getSendLock(); - try - { - //attempt delivery. returns true if no further delivery currently possible to this sub - subscriptionDone = attemptDelivery(sub); - if (subscriptionDone) + + try { - if(lastLoop) + for(int i = 0 ; i < perSub; i++) { - sub.queueEmpty(); + //attempt delivery. returns true if no further delivery currently possible to this sub + subscriptionDone = attemptDelivery(sub, true); + if (subscriptionDone) + { + sub.flushBatched(); + //close autoClose subscriptions if we are not currently intent on continuing + if (lastLoop && !sub.isSuspended() ) + { + sub.queueEmpty(); + } + break; + } + else + { + //this subscription can accept additional deliveries, so we must + //keep going after this (if iteration slicing allows it) + allSubscriptionsDone = false; + lastLoop = false; + if(--iterations == 0) + { + sub.flushBatched(); + break; + } + } } + + sub.flushBatched(); } - else + finally { - //this subscription can accept additional deliveries, so we must - //keep going after this (if iteration slicing allows it) - allSubscriptionsDone = false; - lastLoop = false; - iterations--; + sub.releaseSendLock(); } - } - finally - { - sub.releaseSendLock(); - } } if(allSubscriptionsDone && lastLoop) @@ -1902,24 +1973,24 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener deliveryIncomplete = true; } - _asynchronousRunner.set(null); } // If iterations == 0 then the limiting 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 (iterations == 0 && _asynchronousRunner.compareAndSet(null, runner)) + if (iterations == 0) { if (_logger.isDebugEnabled()) { _logger.debug("Rescheduling runner:" + runner); } - _asyncDelivery.execute(runner); + return 0L; } + return rVal; + } public void checkMessageStatus() throws AMQException { - QueueEntryIterator queueListIterator = _entries.iterator(); while (queueListIterator.advance()) @@ -2150,6 +2221,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener setMaximumMessageSize(((QueueConfiguration)config).getMaximumMessageSize()); setMaximumMessageCount(((QueueConfiguration)config).getMaximumMessageCount()); setMinimumAlertRepeatGap(((QueueConfiguration)config).getMinimumAlertRepeatGap()); + setMaximumDeliveryCount(((QueueConfiguration)config).getMaxDeliveryCount()); _capacity = ((QueueConfiguration)config).getCapacity(); _flowResumeCapacity = ((QueueConfiguration)config).getFlowResumeCapacity(); } @@ -2271,4 +2343,15 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener { return _logActor; } + + public int getMaximumDeliveryCount() + { + return _maximumDeliveryCount; + } + + public void setMaximumDeliveryCount(final int maximumDeliveryCount) + { + _maximumDeliveryCount = maximumDeliveryCount; + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryImpl.java new file mode 100644 index 0000000000..0707dc045c --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryImpl.java @@ -0,0 +1,71 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.server.message.ServerMessage; + +public class SimpleQueueEntryImpl extends QueueEntryImpl +{ + volatile SimpleQueueEntryImpl _next; + + public SimpleQueueEntryImpl(SimpleQueueEntryList queueEntryList) + { + super(queueEntryList); + } + + public SimpleQueueEntryImpl(SimpleQueueEntryList queueEntryList, ServerMessage message, final long entryId) + { + super(queueEntryList, message, entryId); + } + + public SimpleQueueEntryImpl(SimpleQueueEntryList queueEntryList, ServerMessage message) + { + super(queueEntryList, message); + } + + public SimpleQueueEntryImpl getNextNode() + { + return _next; + } + + public SimpleQueueEntryImpl getNextValidEntry() + { + + SimpleQueueEntryImpl next = getNextNode(); + while(next != null && next.isDispensed()) + { + + final SimpleQueueEntryImpl newNext = next.getNextNode(); + if(newNext != null) + { + SimpleQueueEntryList._nextUpdater.compareAndSet(this,next, newNext); + next = getNextNode(); + } + else + { + next = null; + } + + } + return next; + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java index 46baab8c85..0bb5dcc219 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java @@ -1,10 +1,3 @@ -package org.apache.qpid.server.queue; - -import org.apache.qpid.server.message.ServerMessage; - -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import java.util.concurrent.atomic.AtomicLong; - /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -25,25 +18,31 @@ import java.util.concurrent.atomic.AtomicLong; * under the License. * */ -public class SimpleQueueEntryList implements QueueEntryList +package org.apache.qpid.server.queue; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import org.apache.qpid.server.message.ServerMessage; + +public class SimpleQueueEntryList implements QueueEntryList<SimpleQueueEntryImpl> { - private final QueueEntryImpl _head; + private final SimpleQueueEntryImpl _head; - private volatile QueueEntryImpl _tail; + private volatile SimpleQueueEntryImpl _tail; - static final AtomicReferenceFieldUpdater<SimpleQueueEntryList, QueueEntryImpl> + static final AtomicReferenceFieldUpdater<SimpleQueueEntryList, SimpleQueueEntryImpl> _tailUpdater = AtomicReferenceFieldUpdater.newUpdater - (SimpleQueueEntryList.class, QueueEntryImpl.class, "_tail"); + (SimpleQueueEntryList.class, SimpleQueueEntryImpl.class, "_tail"); private final AMQQueue _queue; - static final AtomicReferenceFieldUpdater<QueueEntryImpl, QueueEntryImpl> + static final AtomicReferenceFieldUpdater<SimpleQueueEntryImpl, SimpleQueueEntryImpl> _nextUpdater = AtomicReferenceFieldUpdater.newUpdater - (QueueEntryImpl.class, QueueEntryImpl.class, "_next"); + (SimpleQueueEntryImpl.class, SimpleQueueEntryImpl.class, "_next"); private AtomicLong _scavenges = new AtomicLong(0L); private final long _scavengeCount = Integer.getInteger("qpid.queue.scavenge_count", 50); @@ -52,14 +51,14 @@ public class SimpleQueueEntryList implements QueueEntryList public SimpleQueueEntryList(AMQQueue queue) { _queue = queue; - _head = new QueueEntryImpl(this); + _head = new SimpleQueueEntryImpl(this); _tail = _head; } void advanceHead() { - QueueEntryImpl next = _head.nextNode(); - QueueEntryImpl newNext = _head.getNext(); + SimpleQueueEntryImpl next = _head.getNextNode(); + SimpleQueueEntryImpl newNext = _head.getNextValidEntry(); if (next == newNext) { @@ -73,11 +72,11 @@ public class SimpleQueueEntryList implements QueueEntryList void scavenge() { - QueueEntryImpl next = _head.getNext(); + SimpleQueueEntryImpl next = _head.getNextValidEntry(); while (next != null) { - next = next.getNext(); + next = next.getNextValidEntry(); } } @@ -88,13 +87,13 @@ public class SimpleQueueEntryList implements QueueEntryList } - public QueueEntry add(ServerMessage message) + public SimpleQueueEntryImpl add(ServerMessage message) { - QueueEntryImpl node = createQueueEntry(message); + SimpleQueueEntryImpl node = createQueueEntry(message); for (;;) { - QueueEntryImpl tail = _tail; - QueueEntryImpl next = tail.nextNode(); + SimpleQueueEntryImpl tail = _tail; + SimpleQueueEntryImpl next = tail.getNextNode(); if (tail == _tail) { if (next == null) @@ -115,23 +114,22 @@ public class SimpleQueueEntryList implements QueueEntryList } } - protected QueueEntryImpl createQueueEntry(ServerMessage message) + protected SimpleQueueEntryImpl createQueueEntry(ServerMessage message) { - return new QueueEntryImpl(this, message); + return new SimpleQueueEntryImpl(this, message); } - public QueueEntry next(QueueEntry node) + public SimpleQueueEntryImpl next(SimpleQueueEntryImpl node) { - return ((QueueEntryImpl)node).getNext(); + return node.getNextValidEntry(); } - - public static class QueueEntryIteratorImpl implements QueueEntryIterator + public static class QueueEntryIteratorImpl implements QueueEntryIterator<SimpleQueueEntryImpl> { - private QueueEntryImpl _lastNode; + private SimpleQueueEntryImpl _lastNode; - QueueEntryIteratorImpl(QueueEntryImpl startNode) + QueueEntryIteratorImpl(SimpleQueueEntryImpl startNode) { _lastNode = startNode; } @@ -139,14 +137,12 @@ public class SimpleQueueEntryList implements QueueEntryList public boolean atTail() { - return _lastNode.nextNode() == null; + return _lastNode.getNextNode() == null; } - public QueueEntry getNode() + public SimpleQueueEntryImpl getNode() { - return _lastNode; - } public boolean advance() @@ -154,10 +150,10 @@ public class SimpleQueueEntryList implements QueueEntryList if(!atTail()) { - QueueEntryImpl nextNode = _lastNode.nextNode(); - while(nextNode.isDispensed() && nextNode.nextNode() != null) + SimpleQueueEntryImpl nextNode = _lastNode.getNextNode(); + while(nextNode.isDispensed() && nextNode.getNextNode() != null) { - nextNode = nextNode.nextNode(); + nextNode = nextNode.getNextNode(); } _lastNode = nextNode; return true; @@ -173,21 +169,26 @@ public class SimpleQueueEntryList implements QueueEntryList } - public QueueEntryIterator iterator() + public QueueEntryIteratorImpl iterator() { return new QueueEntryIteratorImpl(_head); } - public QueueEntry getHead() + public SimpleQueueEntryImpl getHead() { return _head; } + public void entryDeleted(SimpleQueueEntryImpl queueEntry) + { + advanceHead(); + } + static class Factory implements QueueEntryListFactory { - public QueueEntryList createQueueEntryList(AMQQueue queue) + public SimpleQueueEntryList createQueueEntryList(AMQQueue queue) { return new SimpleQueueEntryList(queue); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueue.java new file mode 100644 index 0000000000..3f02442704 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueue.java @@ -0,0 +1,30 @@ +package org.apache.qpid.server.queue; + +import java.util.Map; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class SortedQueue extends OutOfOrderQueue +{ + private String _sortedPropertyName; + + protected SortedQueue(final String name, final boolean durable, + final String owner, final boolean autoDelete, final boolean exclusive, + final VirtualHost virtualHost, Map<String, Object> arguments, String sortedPropertyName) + { + super(name, durable, owner, autoDelete, exclusive, virtualHost, + new SortedQueueEntryListFactory(sortedPropertyName), arguments); + this._sortedPropertyName = sortedPropertyName; + } + + public String getSortedPropertyName() + { + return _sortedPropertyName; + } + + public synchronized void enqueue(ServerMessage message, PostEnqueueAction action) throws AMQException + { + super.enqueue(message, action); + } +}
\ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryImpl.java new file mode 100644 index 0000000000..1052adbe67 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryImpl.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.server.message.ServerMessage; + +/** + * An implementation of QueueEntryImpl to be used in SortedQueueEntryList. + */ +public class SortedQueueEntryImpl extends QueueEntryImpl +{ + public static enum Colour + { + RED, BLACK + }; + + private volatile SortedQueueEntryImpl _next; + private SortedQueueEntryImpl _prev; + private String _key; + + private Colour _colour = Colour.BLACK; + private SortedQueueEntryImpl _parent; + private SortedQueueEntryImpl _left; + private SortedQueueEntryImpl _right; + + public SortedQueueEntryImpl(final SortedQueueEntryList queueEntryList) + { + super(queueEntryList); + } + + public SortedQueueEntryImpl(final SortedQueueEntryList queueEntryList, + final ServerMessage message, final long entryId) + { + super(queueEntryList, message, entryId); + } + + @Override + public int compareTo(final QueueEntry o) + { + final String otherKey = ((SortedQueueEntryImpl) o)._key; + final int compare = _key == null ? (otherKey == null ? 0 : -1) : otherKey == null ? 1 : _key.compareTo(otherKey); + return compare == 0 ? super.compareTo(o) : compare; + } + + public Colour getColour() + { + return _colour; + } + + public String getKey() + { + return _key; + } + + public SortedQueueEntryImpl getLeft() + { + return _left; + } + + public SortedQueueEntryImpl getNextNode() + { + return _next; + } + + @Override + public SortedQueueEntryImpl getNextValidEntry() + { + return getNextNode(); + } + + public SortedQueueEntryImpl getParent() + { + return _parent; + } + + public SortedQueueEntryImpl getPrev() + { + return _prev; + } + + public SortedQueueEntryImpl getRight() + { + return _right; + } + + public void setColour(final Colour colour) + { + _colour = colour; + } + + public void setKey(final String key) + { + _key = key; + } + + public void setLeft(final SortedQueueEntryImpl left) + { + _left = left; + } + + public void setNext(final SortedQueueEntryImpl next) + { + _next = next; + } + + public void setParent(final SortedQueueEntryImpl parent) + { + _parent = parent; + } + + public void setPrev(final SortedQueueEntryImpl prev) + { + _prev = prev; + } + + public void setRight(final SortedQueueEntryImpl right) + { + _right = right; + } + + @Override + public String toString() + { + return "(" + (_colour == Colour.RED ? "Red," : "Black,") + _key + ")"; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryList.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryList.java new file mode 100644 index 0000000000..5f8ab16c06 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryList.java @@ -0,0 +1,665 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.server.message.ServerMessage; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.server.queue.SortedQueueEntryImpl.Colour; +import org.apache.qpid.server.store.StoreContext; + +/** + * A sorted implementation of QueueEntryList. + * Uses the red/black tree algorithm specified in "Introduction to Algorithms". + * ISBN-10: 0262033844 + * ISBN-13: 978-0262033848 + * @see http://en.wikipedia.org/wiki/Red-black_tree + */ +public class SortedQueueEntryList implements QueueEntryList<SortedQueueEntryImpl> +{ + private final SortedQueueEntryImpl _head; + private SortedQueueEntryImpl _root; + private long _entryId = Long.MIN_VALUE; + private final Object _lock = new Object(); + private final AMQQueue _queue; + private final String _propertyName; + + public SortedQueueEntryList(final AMQQueue queue, final String propertyName) + { + _queue = queue; + _head = new SortedQueueEntryImpl(this); + _propertyName = propertyName; + } + + @Override + public AMQQueue getQueue() + { + return _queue; + } + + @Override + public SortedQueueEntryImpl add(final ServerMessage message) + { + synchronized(_lock) + { + String key = null; + final Object val = message.getMessageHeader().getHeader(_propertyName); + if(val != null) + { + key = val.toString(); + } + + final SortedQueueEntryImpl entry = new SortedQueueEntryImpl(this,message, ++_entryId); + entry.setKey(key); + + insert(entry); + + return entry; + } + } + + /** + * Red Black Tree insert implementation. + * @param entry the entry to insert. + */ + private void insert(final SortedQueueEntryImpl entry) + { + SortedQueueEntryImpl node = _root; + if((node = _root) == null) + { + _root = entry; + _head.setNext(entry); + entry.setPrev(_head); + return; + } + else + { + SortedQueueEntryImpl parent = null; + while(node != null) + { + parent = node; + if(entry.compareTo(node) < 0) + { + node = node.getLeft(); + } + else + { + node = node.getRight(); + } + } + entry.setParent(parent); + + if(entry.compareTo(parent) < 0) + { + parent.setLeft(entry); + final SortedQueueEntryImpl prev = parent.getPrev(); + entry.setNext(parent); + prev.setNext(entry); + entry.setPrev(prev); + parent.setPrev(entry); + } + else + { + parent.setRight(entry); + + final SortedQueueEntryImpl next = parent.getNextValidEntry(); + entry.setNext(next); + parent.setNext(entry); + + if(next != null) + { + next.setPrev(entry); + } + entry.setPrev(parent); + } + } + entry.setColour(Colour.RED); + insertFixup(entry); + } + + private void insertFixup(SortedQueueEntryImpl entry) + { + while(isParentColour(entry, Colour.RED)) + { + final SortedQueueEntryImpl grandparent = nodeGrandparent(entry); + + if(nodeParent(entry) == leftChild(grandparent)) + { + final SortedQueueEntryImpl y = rightChild(grandparent); + if(isNodeColour(y, Colour.RED)) + { + setColour(nodeParent(entry), Colour.BLACK); + setColour(y, Colour.BLACK); + setColour(grandparent, Colour.RED); + entry = grandparent; + } + else + { + if(entry == rightChild(nodeParent(entry))) + { + entry = nodeParent(entry); + leftRotate(entry); + } + setColour(nodeParent(entry), Colour.BLACK); + setColour(nodeGrandparent(entry), Colour.RED); + rightRotate(nodeGrandparent(entry)); + } + } + else + { + final SortedQueueEntryImpl y = leftChild(grandparent); + if(isNodeColour(y, Colour.RED)) + { + setColour(nodeParent(entry), Colour.BLACK); + setColour(y, Colour.BLACK); + setColour(grandparent, Colour.RED); + entry = grandparent; + } + else + { + if(entry == leftChild(nodeParent(entry))) + { + entry = nodeParent(entry); + rightRotate(entry); + } + setColour(nodeParent(entry), Colour.BLACK); + setColour(nodeGrandparent(entry), Colour.RED); + leftRotate(nodeGrandparent(entry)); + } + } + } + _root.setColour(Colour.BLACK); + } + + private void leftRotate(final SortedQueueEntryImpl entry) + { + if(entry != null) + { + final SortedQueueEntryImpl rightChild = rightChild(entry); + entry.setRight(rightChild.getLeft()); + if(entry.getRight() != null) + { + entry.getRight().setParent(entry); + } + rightChild.setParent(entry.getParent()); + if(entry.getParent() == null) + { + _root = rightChild; + } + else if(entry == entry.getParent().getLeft()) + { + entry.getParent().setLeft(rightChild); + } + else + { + entry.getParent().setRight(rightChild); + } + rightChild.setLeft(entry); + entry.setParent(rightChild); + } + } + + private void rightRotate(final SortedQueueEntryImpl entry) + { + if(entry != null) + { + final SortedQueueEntryImpl leftChild = leftChild(entry); + entry.setLeft(leftChild.getRight()); + if(entry.getLeft() != null) + { + leftChild.getRight().setParent(entry); + } + leftChild.setParent(entry.getParent()); + if(leftChild.getParent() == null) + { + _root = leftChild; + } + else if(entry == entry.getParent().getRight()) + { + entry.getParent().setRight(leftChild); + } + else + { + entry.getParent().setLeft(leftChild); + } + leftChild.setRight(entry); + entry.setParent(leftChild); + } + } + + private void setColour(final SortedQueueEntryImpl node, final Colour colour) + { + if(node != null) + { + node.setColour(colour); + } + } + + private SortedQueueEntryImpl leftChild(final SortedQueueEntryImpl node) + { + return node == null ? null : node.getLeft(); + } + + private SortedQueueEntryImpl rightChild(final SortedQueueEntryImpl node) + { + return node == null ? null : node.getRight(); + } + + private SortedQueueEntryImpl nodeParent(final SortedQueueEntryImpl node) + { + return node == null ? null : node.getParent(); + } + + private SortedQueueEntryImpl nodeGrandparent(final SortedQueueEntryImpl node) + { + return nodeParent(nodeParent(node)); + } + + private boolean isParentColour(final SortedQueueEntryImpl node, final SortedQueueEntryImpl.Colour colour) + { + + return node != null && isNodeColour(node.getParent(), colour); + } + + protected boolean isNodeColour(final SortedQueueEntryImpl node, final SortedQueueEntryImpl.Colour colour) + { + return (node == null ? Colour.BLACK : node.getColour()) == colour; + } + + @Override + public SortedQueueEntryImpl next(final SortedQueueEntryImpl node) + { + synchronized(_lock) + { + if(node.isDispensed() && _head != node) + { + SortedQueueEntryImpl current = _head; + SortedQueueEntryImpl next; + while(current != null) + { + next = current.getNextValidEntry(); + if(current.compareTo(node)>0 && !current.isDispensed()) + { + break; + } + else + { + current = next; + } + } + return current; + } + else + { + return node.getNextValidEntry(); + } + } + } + + @Override + public QueueEntryIterator<SortedQueueEntryImpl> iterator() + { + return new QueueEntryIteratorImpl(_head); + } + + @Override + public SortedQueueEntryImpl getHead() + { + return _head; + } + + protected SortedQueueEntryImpl getRoot() + { + return _root; + } + + @Override + public void entryDeleted(final SortedQueueEntryImpl entry) + { + synchronized(_lock) + { + // If the node to be removed has two children, we swap the position + // of the node and its successor in the tree + if(leftChild(entry) != null && rightChild(entry) != null) + { + swapWithSuccessor(entry); + } + + // Then deal with the easy doubly linked list deletion (need to do + // this after the swap as the swap uses next + final SortedQueueEntryImpl prev = entry.getPrev(); + if(prev != null) + { + prev.setNext(entry.getNextValidEntry()); + } + + final SortedQueueEntryImpl next = entry.getNextValidEntry(); + if(next != null) + { + next.setPrev(prev); + } + + // now deal with splicing + final SortedQueueEntryImpl chosenChild; + + if(leftChild(entry) != null) + { + chosenChild = leftChild(entry); + } + else + { + chosenChild = rightChild(entry); + } + + if(chosenChild != null) + { + // we have one child (x), we can move it up to replace x; + chosenChild.setParent(entry.getParent()); + if(chosenChild.getParent() == null) + { + _root = chosenChild; + } + else if(entry == entry.getParent().getLeft()) + { + entry.getParent().setLeft(chosenChild); + } + else + { + entry.getParent().setRight(chosenChild); + } + + entry.setLeft(null); + entry.setRight(null); + entry.setParent(null); + + if(entry.getColour() == Colour.BLACK) + { + deleteFixup(chosenChild); + } + + } + else + { + // no children + if(entry.getParent() == null) + { + // no parent either - the tree is empty + _root = null; + } + else + { + if(entry.getColour() == Colour.BLACK) + { + deleteFixup(entry); + } + + if(entry.getParent() != null) + { + if(entry.getParent().getLeft() == entry) + { + entry.getParent().setLeft(null); + } + else if(entry.getParent().getRight() == entry) + { + entry.getParent().setRight(null); + } + entry.setParent(null); + } + } + } + + } + } + + /** + * Swaps the position of the node in the tree with it's successor + * (that is the node with the next highest key) + * @param entry + */ + private void swapWithSuccessor(final SortedQueueEntryImpl entry) + { + final SortedQueueEntryImpl next = entry.getNextValidEntry(); + final SortedQueueEntryImpl nextParent = next.getParent(); + final SortedQueueEntryImpl nextLeft = next.getLeft(); + final SortedQueueEntryImpl nextRight = next.getRight(); + final Colour nextColour = next.getColour(); + + // Special case - the successor is the right child of the node + if(next == entry.getRight()) + { + next.setParent(entry.getParent()); + if(next.getParent() == null) + { + _root = next; + } + else if(next.getParent().getLeft() == entry) + { + next.getParent().setLeft(next); + } + else + { + next.getParent().setRight(next); + } + + next.setRight(entry); + entry.setParent(next); + next.setLeft(entry.getLeft()); + + if(next.getLeft() != null) + { + next.getLeft().setParent(next); + } + + next.setColour(entry.getColour()); + entry.setColour(nextColour); + entry.setLeft(nextLeft); + + if(nextLeft != null) + { + nextLeft.setParent(entry); + } + entry.setRight(nextRight); + if(nextRight != null) + { + nextRight.setParent(entry); + } + } + else + { + next.setParent(entry.getParent()); + if(next.getParent() == null) + { + _root = next; + } + else if(next.getParent().getLeft() == entry) + { + next.getParent().setLeft(next); + } + else + { + next.getParent().setRight(next); + } + + next.setLeft(entry.getLeft()); + if(next.getLeft() != null) + { + next.getLeft().setParent(next); + } + next.setRight(entry.getRight()); + if(next.getRight() != null) + { + next.getRight().setParent(next); + } + next.setColour(entry.getColour()); + + entry.setParent(nextParent); + if(nextParent.getLeft() == next) + { + nextParent.setLeft(entry); + } + else + { + nextParent.setRight(entry); + } + + entry.setLeft(nextLeft); + if(nextLeft != null) + { + nextLeft.setParent(entry); + } + entry.setRight(nextRight); + if(nextRight != null) + { + nextRight.setParent(entry); + } + entry.setColour(nextColour); + } + } + + private void deleteFixup(SortedQueueEntryImpl entry) + { + int i = 0; + while(entry != null && entry != _root + && isNodeColour(entry, Colour.BLACK)) + { + i++; + + if(i > 1000) + { + return; + } + + if(entry == leftChild(nodeParent(entry))) + { + SortedQueueEntryImpl rightSibling = rightChild(nodeParent(entry)); + if(isNodeColour(rightSibling, Colour.RED)) + { + setColour(rightSibling, Colour.BLACK); + nodeParent(entry).setColour(Colour.RED); + leftRotate(nodeParent(entry)); + rightSibling = rightChild(nodeParent(entry)); + } + + if(isNodeColour(leftChild(rightSibling), Colour.BLACK) + && isNodeColour(rightChild(rightSibling), Colour.BLACK)) + { + setColour(rightSibling, Colour.RED); + entry = nodeParent(entry); + } + else + { + if(isNodeColour(rightChild(rightSibling), Colour.BLACK)) + { + setColour(leftChild(rightSibling), Colour.BLACK); + rightSibling.setColour(Colour.RED); + rightRotate(rightSibling); + rightSibling = rightChild(nodeParent(entry)); + } + setColour(rightSibling, getColour(nodeParent(entry))); + setColour(nodeParent(entry), Colour.BLACK); + setColour(rightChild(rightSibling), Colour.BLACK); + leftRotate(nodeParent(entry)); + entry = _root; + } + } + else + { + SortedQueueEntryImpl leftSibling = leftChild(nodeParent(entry)); + if(isNodeColour(leftSibling, Colour.RED)) + { + setColour(leftSibling, Colour.BLACK); + nodeParent(entry).setColour(Colour.RED); + rightRotate(nodeParent(entry)); + leftSibling = leftChild(nodeParent(entry)); + } + + if(isNodeColour(leftChild(leftSibling), Colour.BLACK) + && isNodeColour(rightChild(leftSibling), Colour.BLACK)) + { + setColour(leftSibling, Colour.RED); + entry = nodeParent(entry); + } + else + { + if(isNodeColour(leftChild(leftSibling), Colour.BLACK)) + { + setColour(rightChild(leftSibling), Colour.BLACK); + leftSibling.setColour(Colour.RED); + leftRotate(leftSibling); + leftSibling = leftChild(nodeParent(entry)); + } + setColour(leftSibling, getColour(nodeParent(entry))); + setColour(nodeParent(entry), Colour.BLACK); + setColour(leftChild(leftSibling), Colour.BLACK); + rightRotate(nodeParent(entry)); + entry = _root; + } + } + } + setColour(entry, Colour.BLACK); + } + + private Colour getColour(final SortedQueueEntryImpl x) + { + return x == null ? null : x.getColour(); + } + + public class QueueEntryIteratorImpl implements QueueEntryIterator<SortedQueueEntryImpl> + { + private SortedQueueEntryImpl _lastNode; + + public QueueEntryIteratorImpl(final SortedQueueEntryImpl startNode) + { + _lastNode = startNode; + } + + public boolean atTail() + { + return next(_lastNode) == null; + } + + public SortedQueueEntryImpl getNode() + { + return _lastNode; + } + + public boolean advance() + { + if(!atTail()) + { + SortedQueueEntryImpl nextNode = next(_lastNode); + while(nextNode.isDispensed() && next(nextNode) != null) + { + nextNode = next(nextNode); + } + _lastNode = nextNode; + return true; + + } + else + { + return false; + } + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryListFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryListFactory.java new file mode 100644 index 0000000000..7a70795e77 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryListFactory.java @@ -0,0 +1,19 @@ +package org.apache.qpid.server.queue; + +public class SortedQueueEntryListFactory implements QueueEntryListFactory +{ + + private final String _propertyName; + + public SortedQueueEntryListFactory(final String propertyName) + { + _propertyName = propertyName; + } + + @Override + public QueueEntryList<SortedQueueEntryImpl> createQueueEntryList(final AMQQueue queue) + { + return new SortedQueueEntryList(queue, _propertyName); + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java index 46c1a6af9a..fbef23dca1 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java @@ -27,6 +27,10 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.AMQException; import org.apache.log4j.Logger; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + class SubFlushRunner implements ReadWriteRunnable { @@ -34,29 +38,33 @@ class SubFlushRunner implements ReadWriteRunnable private final Subscription _sub; - private final String _name; + + private static int IDLE = 0; + private static int SCHEDULED = 1; + private static int RUNNING = 2; + + + private final AtomicInteger _scheduled = new AtomicInteger(IDLE); + + private static final long ITERATIONS = SimpleAMQQueue.MAX_ASYNC_DELIVERIES; + private final AtomicBoolean _stateChange = new AtomicBoolean(); public SubFlushRunner(Subscription sub) { _sub = sub; - _name = "SubFlushRunner-"+_sub; } public void run() { - - String originalName = Thread.currentThread().getName(); - try + if(_scheduled.compareAndSet(SCHEDULED, RUNNING)) { - Thread.currentThread().setName(_name); - boolean complete = false; + _stateChange.set(false); try { CurrentActor.set(_sub.getLogActor()); complete = getQueue().flushSubscription(_sub, ITERATIONS); - } catch (AMQException e) { @@ -66,17 +74,15 @@ class SubFlushRunner implements ReadWriteRunnable { CurrentActor.remove(); } - if (!complete && !_sub.isSuspended()) + _scheduled.compareAndSet(RUNNING, IDLE); + if ((!complete || _stateChange.compareAndSet(true,false))&& !_sub.isSuspended()) { - getQueue().execute(this); + if(_scheduled.compareAndSet(IDLE,SCHEDULED)) + { + getQueue().execute(this); + } } - } - finally - { - Thread.currentThread().setName(originalName); - } - } private SimpleAMQQueue getQueue() @@ -93,4 +99,18 @@ class SubFlushRunner implements ReadWriteRunnable { return true; } + + public String toString() + { + return "SubFlushRunner-" + _sub.getLogActor(); + } + + public void execute(Executor executor) + { + _stateChange.set(true); + if(_scheduled.compareAndSet(IDLE,SCHEDULED)) + { + executor.execute(this); + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index c07074f69c..7eb1b54693 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -250,14 +250,14 @@ public abstract class ApplicationRegistry implements IApplicationRegistry try { + initialiseManagedObjectRegistry(); + configure(); _qmfService = new QMFService(getConfigStore(), this); CurrentActor.get().message(BrokerMessages.STARTUP(QpidProperties.getReleaseVersion(), QpidProperties.getBuildVersion())); - initialiseManagedObjectRegistry(); - _virtualHostRegistry = new VirtualHostRegistry(this); _securityManager = new SecurityManager(_configuration, _pluginManager); @@ -471,12 +471,12 @@ public abstract class ApplicationRegistry implements IApplicationRegistry close(_authenticationManager); - close(_managedObjectRegistry); - close(_qmfService); close(_pluginManager); + close(_managedObjectRegistry); + CurrentActor.get().message(BrokerMessages.STOPPED()); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AbstractProxyPlugin.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AbstractProxyPlugin.java index 8b5ff6781d..ec11e2d39c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AbstractProxyPlugin.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AbstractProxyPlugin.java @@ -73,11 +73,6 @@ public abstract class AbstractProxyPlugin extends AbstractPlugin return getDefault(); } - public Result authoriseExecute(ObjectType object, ObjectProperties properties) - { - return getDefault(); - } - public Result authoriseUpdate(ObjectType object, ObjectProperties properties) { return getDefault(); @@ -121,8 +116,6 @@ public abstract class AbstractProxyPlugin extends AbstractPlugin return authoriseDelete(objectType, properties); case PURGE: return authorisePurge(objectType, properties); - case EXECUTE: - return authoriseExecute(objectType, properties); case UPDATE: return authoriseUpdate(objectType, properties); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java index f582fed6a0..abf9e3379d 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java @@ -20,10 +20,8 @@ package org.apache.qpid.server.security; import static org.apache.qpid.server.security.access.ObjectType.EXCHANGE; import static org.apache.qpid.server.security.access.ObjectType.METHOD; -import static org.apache.qpid.server.security.access.ObjectType.OBJECT; import static org.apache.qpid.server.security.access.ObjectType.QUEUE; import static org.apache.qpid.server.security.access.ObjectType.VIRTUALHOST; -import static org.apache.qpid.server.security.access.Operation.ACCESS; import static org.apache.qpid.server.security.access.Operation.BIND; import static org.apache.qpid.server.security.access.Operation.CONSUME; import static org.apache.qpid.server.security.access.Operation.CREATE; @@ -67,7 +65,14 @@ public class SecurityManager /** Container for the {@link Principal} that is using to this thread. */ private static final ThreadLocal<Subject> _subject = new ThreadLocal<Subject>(); - + private static final ThreadLocal<Boolean> _accessChecksDisabled = new ThreadLocal<Boolean>() + { + protected Boolean initialValue() + { + return false; + } + }; + private PluginManager _pluginManager; private Map<String, SecurityPluginFactory> _pluginFactories = new HashMap<String, SecurityPluginFactory>(); private Map<String, SecurityPlugin> _globalPlugins = new HashMap<String, SecurityPlugin>(); @@ -194,6 +199,11 @@ public class SecurityManager private boolean checkAllPlugins(AccessCheck checker) { + if(_accessChecksDisabled.get()) + { + return true; + } + HashMap<String, SecurityPlugin> remainingPlugins = new HashMap<String, SecurityPlugin>(_globalPlugins); for (Entry<String, SecurityPlugin> hostEntry : _hostPlugins.entrySet()) @@ -273,21 +283,6 @@ public class SecurityManager } }); } - - // TODO not implemented yet, awaiting consensus - public boolean authoriseObject(final String packageName, final String className) - { - return checkAllPlugins(new AccessCheck() - { - Result allowed(SecurityPlugin plugin) - { - ObjectProperties properties = new ObjectProperties(); - properties.put(ObjectProperties.Property.PACKAGE, packageName); - properties.put(ObjectProperties.Property.CLASS, className); - return plugin.authorise(ACCESS, OBJECT, properties); - } - }); - } public boolean authoriseMethod(final Operation operation, final String componentName, final String methodName) { @@ -329,17 +324,6 @@ public class SecurityManager }); } - public boolean authoriseConsume(final boolean exclusive, final boolean noAck, final boolean noLocal, final boolean nowait, final AMQQueue queue) - { - return checkAllPlugins(new AccessCheck() - { - Result allowed(SecurityPlugin plugin) - { - return plugin.authorise(CONSUME, QUEUE, new ObjectProperties(exclusive, noAck, noLocal, nowait, queue)); - } - }); - } - public boolean authoriseCreateExchange(final Boolean autoDelete, final Boolean durable, final AMQShortString exchangeName, final Boolean internal, final Boolean nowait, final Boolean passive, final AMQShortString exchangeType) { @@ -419,4 +403,14 @@ public class SecurityManager } }); } + + public static boolean setAccessChecksDisabled(final boolean status) + { + //remember current value + boolean current = _accessChecksDisabled.get(); + + _accessChecksDisabled.set(status); + + return current; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java index 7103b30283..69c7ff185a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java @@ -32,14 +32,12 @@ import java.util.Set; public enum ObjectType { ALL(Operation.ALL), - VIRTUALHOST(ACCESS), - QUEUE(CREATE, DELETE, PURGE, CONSUME), - TOPIC(CREATE, DELETE, PURGE, CONSUME), - EXCHANGE(ACCESS, CREATE, DELETE, BIND, UNBIND, PUBLISH), + VIRTUALHOST(Operation.ALL, ACCESS), + QUEUE(Operation.ALL, CREATE, DELETE, PURGE, CONSUME), + EXCHANGE(Operation.ALL, ACCESS, CREATE, DELETE, BIND, UNBIND, PUBLISH), LINK, // Not allowed in the Java broker ROUTE, // Not allowed in the Java broker - METHOD(Operation.ALL, ACCESS, UPDATE, EXECUTE), - OBJECT(ACCESS); + METHOD(Operation.ALL, ACCESS, UPDATE); private EnumSet<Operation> _actions; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java index 06d7f8fd0c..21ea042eed 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Operation.java @@ -32,8 +32,7 @@ public enum Operation UNBIND, DELETE, PURGE, - UPDATE, - EXECUTE; + UPDATE; public static Operation parse(String text) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java index 1612d13b1b..7cb34da804 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java @@ -384,16 +384,8 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase BufferedReader reader = null; PrintStream writer = null; - - Random r = new Random(); - File tmp; - do - { - tmp = new File(_passwordFile.getPath() + r.nextInt() + ".tmp"); - } - while(tmp.exists()); - - tmp.deleteOnExit(); + + final File tmp = createTempFileOnSameFilesystem(_passwordFile); try { @@ -458,52 +450,72 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase } finally { - if (reader != null) - { - reader.close(); - } - if (writer != null) { writer.close(); } - } - - // Swap temp file to main password file. - File old = new File(_passwordFile.getAbsoluteFile() + ".old"); - if (old.exists()) - { - old.delete(); - } - - if(!_passwordFile.renameTo(old)) - { - //unable to rename the existing file to the backup name - _logger.error("Could not backup the existing password file"); - throw new IOException("Could not backup the existing password file"); - } - - if(!tmp.renameTo(_passwordFile)) - { - //failed to rename the new file to the required filename - - if(!old.renameTo(_passwordFile)) + if (reader != null) { - //unable to return the backup to required filename - _logger.error("Could not rename the new password file into place, and unable to restore original file"); - throw new IOException("Could not rename the new password file into place, and unable to restore original file"); + reader.close(); } - - _logger.error("Could not rename the new password file into place"); - throw new IOException("Could not rename the new password file into place"); } - + + swapTempFileToLive(_passwordFile, tmp); + } finally { _userUpdate.unlock(); } } + + private void swapTempFileToLive(final File live, final File temp) throws IOException + { + // Remove any existing ".old" file + final File old = new File(live.getAbsoluteFile() + ".old"); + if (old.exists()) + { + old.delete(); + } + + // Create an new ".old" file + if(!live.renameTo(old)) + { + //unable to rename the existing file to the backup name + _logger.error("Could not backup the existing password file"); + throw new IOException("Could not backup the existing password file"); + } + + // Move temp file to be the new "live" file + if(!temp.renameTo(live)) + { + //failed to rename the new file to the required filename + if(!old.renameTo(live)) + { + //unable to return the backup to required filename + _logger.error("Could not rename the new password file into place, and unable to restore original file"); + throw new IOException("Could not rename the new password file into place, and unable to restore original file"); + } + + _logger.error("Could not rename the new password file into place"); + throw new IOException("Could not rename the new password file into place"); + } + } + + private File createTempFileOnSameFilesystem(final File liveFile) + { + File tmp; + final Random r = new Random(); + + do + { + tmp = new File(liveFile.getPath() + r.nextInt() + ".tmp"); + } + while(tmp.exists()); + + tmp.deleteOnExit(); + return tmp; + } public void reload() throws IOException { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java index 8b099b62ce..d3f46d2e90 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java @@ -142,6 +142,19 @@ public class DerbyMessageStore implements MessageStore private boolean _configured; + private static final class CommitStoreFuture implements StoreFuture + { + public boolean isComplete() + { + return true; + } + + public void waitForCompletion() + { + + } + } + private enum State { INITIAL, @@ -910,19 +923,19 @@ public class DerbyMessageStore implements MessageStore throws AMQStoreException { Connection conn = null; + PreparedStatement stmt = null; try { conn = newAutoCommitConnection(); // 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); + stmt = conn.prepareStatement(DELETE_FROM_BINDINGS); stmt.setString(1, exchange.getNameShortString().toString() ); stmt.setString(2, queue.getNameShortString().toString()); stmt.setString(3, routingKey == null ? null : routingKey.toString()); - + int result = stmt.executeUpdate(); - stmt.close(); - + if(result != 1) { throw new AMQStoreException("Queue binding for queue with name " + queue.getNameShortString() + " to exchange " @@ -936,21 +949,9 @@ public class DerbyMessageStore implements MessageStore } finally { - if(conn != null) - { - try - { - conn.close(); - } - catch (SQLException e) - { - _logger.error(e); - } - } - + closePreparedStatement(stmt); + closeConnection(conn); } - - } public void createQueue(AMQQueue queue) throws AMQStoreException @@ -1153,15 +1154,14 @@ public class DerbyMessageStore implements MessageStore AMQShortString name = queue.getNameShortString(); _logger.debug("public void removeQueue(AMQShortString name = " + name + "): called"); Connection conn = null; - + PreparedStatement stmt = null; try { conn = newAutoCommitConnection(); - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_QUEUE); + stmt = conn.prepareStatement(DELETE_FROM_QUEUE); stmt.setString(1, name.toString()); int results = stmt.executeUpdate(); - stmt.close(); - + if (results == 0) { throw new AMQStoreException("Queue " + name + " not found"); @@ -1173,18 +1173,8 @@ public class DerbyMessageStore implements MessageStore } finally { - if(conn != null) - { - try - { - conn.close(); - } - catch (SQLException e) - { - _logger.error(e); - } - } - + closePreparedStatement(stmt); + closeConnection(conn); } @@ -1317,19 +1307,7 @@ public class DerbyMessageStore implements MessageStore public StoreFuture commitTranAsync(ConnectionWrapper connWrapper) throws AMQStoreException { commitTran(connWrapper); - return new StoreFuture() - { - public boolean isComplete() - { - return true; - } - - public void waitForCompletion() - { - - } - }; - + return new CommitStoreFuture(); } public void abortTran(ConnectionWrapper connWrapper) throws AMQStoreException @@ -1572,6 +1550,7 @@ public class DerbyMessageStore implements MessageStore { _logger.debug("Adding content chunk offset " + offset + " for message " +messageId); } + PreparedStatement stmt = null; try { @@ -1580,7 +1559,7 @@ public class DerbyMessageStore implements MessageStore byte[] chunkData = new byte[src.limit()]; src.duplicate().get(chunkData); - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_CONTENT); + stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_CONTENT); stmt.setLong(1,messageId); stmt.setInt(2, offset); stmt.setInt(3, offset+chunkData.length); @@ -1594,24 +1573,16 @@ public class DerbyMessageStore implements MessageStore ByteArrayInputStream bis = new ByteArrayInputStream(chunkData); stmt.setBinaryStream(4, bis, chunkData.length); stmt.executeUpdate(); - stmt.close(); } catch (SQLException e) { - if(conn != null) - { - try - { - conn.close(); - } - catch (SQLException e1) - { - - } - } - + closeConnection(conn); throw new RuntimeException("Error adding content chunk offset " + offset + " for message " + messageId + ": " + e.getMessage(), e); } + finally + { + closePreparedStatement(stmt); + } } @@ -1619,13 +1590,13 @@ public class DerbyMessageStore implements MessageStore public int getContent(long messageId, int offset, ByteBuffer dst) { Connection conn = null; - + PreparedStatement stmt = null; try { conn = newAutoCommitConnection(); - PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_CONTENT); + stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_CONTENT); stmt.setLong(1,messageId); stmt.setInt(2, offset); stmt.setInt(3, offset+dst.remaining()); @@ -1656,28 +1627,18 @@ public class DerbyMessageStore implements MessageStore } } - stmt.close(); - conn.close(); return written; } catch (SQLException e) { - if(conn != null) - { - try - { - conn.close(); - } - catch (SQLException e1) - { - - } - } - throw new RuntimeException("Error retrieving content from offset " + offset + " for message " + messageId + ": " + e.getMessage(), e); } - + finally + { + closePreparedStatement(stmt); + closeConnection(conn); + } } @@ -1849,5 +1810,33 @@ public class DerbyMessageStore implements MessageStore } } + private void closeConnection(final Connection conn) + { + if(conn != null) + { + try + { + conn.close(); + } + catch (SQLException e) + { + _logger.error("Problem closing connection", e); + } + } + } + private void closePreparedStatement(final PreparedStatement stmt) + { + if (stmt != null) + { + try + { + stmt.close(); + } + catch(SQLException e) + { + _logger.error("Problem closing prepared statement", e); + } + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java index b49b12fb79..80c5e2866c 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ExplicitAcceptDispositionChangeListener.java @@ -53,12 +53,12 @@ class ExplicitAcceptDispositionChangeListener implements ServerSession.MessageDi } - public void onRelease() + public void onRelease(boolean setRedelivered) { final Subscription_0_10 subscription = getSubscription(); if(subscription != null && _entry.isAcquiredBy(_sub)) { - subscription.release(_entry); + subscription.release(_entry, setRedelivered); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java index b5bb2014b5..a61b0b4e82 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ImplicitAcceptDispositionChangeListener.java @@ -43,11 +43,11 @@ class ImplicitAcceptDispositionChangeListener implements ServerSession.MessageDi _logger.warn("MessageAccept received for message which is using NONE as the accept mode (likely client error)"); } - public void onRelease() + public void onRelease(boolean setRedelivered) { if(_entry.isAcquiredBy(_sub)) { - getSubscription().release(_entry); + getSubscription().release(_entry, setRedelivered); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java index 5c4e413989..bc1be90531 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java @@ -69,17 +69,24 @@ public interface Subscription void close(); - void send(QueueEntry msg) throws AMQException; + void send(QueueEntry entry, boolean batch) throws AMQException; + + void flushBatched(); void queueDeleted(AMQQueue queue); boolean wouldSuspend(QueueEntry msg); + boolean trySendLock(); + + void getSendLock(); void releaseSendLock(); + void releaseQueueEntry(final QueueEntry queueEntryImpl); + void onDequeue(final QueueEntry queueEntry); void restoreCredit(final QueueEntry queueEntry); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java index adb0a84151..23ae14eef1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -119,11 +119,13 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage * 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 entry + * @param batch * @throws AMQException */ @Override - public void send(QueueEntry msg) throws AMQException + public void send(QueueEntry entry, boolean batch) 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. @@ -131,7 +133,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage synchronized (getChannel()) { long deliveryTag = getChannel().getNextDeliveryTag(); - sendToClient(msg, deliveryTag); + sendToClient(entry, deliveryTag); } } @@ -173,11 +175,13 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage * 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 + * @param batch * @throws AMQException */ @Override - public void send(QueueEntry entry) throws AMQException + public void send(QueueEntry entry, boolean batch) throws AMQException { // if we do not need to wait for client acknowledgements // we can decrement the reference count immediately. @@ -193,6 +197,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage synchronized (getChannel()) { + getChannel().getProtocolSession().setDeferFlush(batch); long deliveryTag = getChannel().getNextDeliveryTag(); sendToClient(entry, deliveryTag); @@ -234,7 +239,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage public boolean wouldSuspend(QueueEntry msg) { - return !getCreditManager().useCreditForMessage(msg.getMessage()); + return !getCreditManager().useCreditForMessage(msg.getMessage().getSize()); } } @@ -263,11 +268,13 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage * 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 + * @param batch * @throws AMQException */ @Override - public void send(QueueEntry entry) throws AMQException + public void send(QueueEntry entry, boolean batch) throws AMQException { // if we do not need to wait for client acknowledgements @@ -282,6 +289,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage synchronized (getChannel()) { + getChannel().getProtocolSession().setDeferFlush(batch); long deliveryTag = getChannel().getNextDeliveryTag(); @@ -441,10 +449,12 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage * 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 entry + * @param batch * @throws AMQException */ - abstract public void send(QueueEntry msg) throws AMQException; + abstract public void send(QueueEntry entry, boolean batch) throws AMQException; public boolean isSuspended() @@ -575,7 +585,12 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage public boolean wouldSuspend(QueueEntry msg) { - return !_creditManager.useCreditForMessage(msg.getMessage());//_channel.wouldSuspend(msg.getMessage()); + return !_creditManager.useCreditForMessage(msg.getMessage().getSize());//_channel.wouldSuspend(msg.getMessage()); + } + + public boolean trySendLock() + { + return _stateChangeLock.tryLock(); } public void getSendLock() @@ -623,13 +638,16 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage restoreCredit(queueEntry); } + public void releaseQueueEntry(final QueueEntry queueEntry) + { + restoreCredit(queueEntry); + } + public void restoreCredit(final QueueEntry queueEntry) { _creditManager.restoreCredit(1, queueEntry.getSize()); } - - public void creditStateChanged(boolean hasCredit) { @@ -821,4 +839,11 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage confirmAutoClose(); } } + + public void flushBatched() + { + _channel.getProtocolSession().setDeferFlush(false); + + _channel.getProtocolSession().flushBatched(); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java index d9845c5fa6..6db58ce9c1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java @@ -24,12 +24,15 @@ import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.SUBSCRIPT import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.QUEUE_FORMAT; import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.queue.InboundMessageAdapter; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.configuration.ConfigStore; import org.apache.qpid.server.configuration.ConfiguredObject; import org.apache.qpid.server.configuration.SessionConfig; import org.apache.qpid.server.configuration.SubscriptionConfig; import org.apache.qpid.server.configuration.SubscriptionConfigType; +import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.flow.FlowCreditManager; import org.apache.qpid.server.flow.CreditCreditManager; import org.apache.qpid.server.flow.WindowCreditManager; @@ -37,9 +40,11 @@ import org.apache.qpid.server.flow.FlowCreditManager_0_10; import org.apache.qpid.server.filter.FilterManager; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.GenericActor; +import org.apache.qpid.server.logging.messages.ChannelMessages; import org.apache.qpid.server.logging.messages.SubscriptionMessages; import org.apache.qpid.server.logging.LogActor; import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.message.InboundMessage; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.message.MessageTransferMessage; import org.apache.qpid.server.message.AMQMessage; @@ -56,6 +61,8 @@ import org.apache.qpid.transport.MessageFlowMode; import org.apache.qpid.transport.MessageProperties; import org.apache.qpid.transport.MessageTransfer; import org.apache.qpid.transport.Method; +import org.apache.qpid.transport.Option; +import org.apache.qpid.transport.Session; import org.apache.qpid.transport.Struct; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; @@ -79,11 +86,14 @@ import java.nio.ByteBuffer; public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCreditManagerListener, SubscriptionConfig, LogSubject { + private final long _subscriptionID; private final QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this); private final QueueEntry.SubscriptionAssignedState _assignedState = new QueueEntry.SubscriptionAssignedState(this); + private static final Option[] BATCHED = new Option[] { Option.BATCH }; + private final Lock _stateChangeLock = new ReentrantLock(); private final AtomicReference<State> _state = new AtomicReference<State>(State.ACTIVE); @@ -120,6 +130,8 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr private final long _createTime = System.currentTimeMillis(); private final AtomicLong _deliveredCount = new AtomicLong(0); private final Map<String, Object> _arguments; + private int _deferredMessageCredit; + private long _deferredSizeCredit; public Subscription_0_10(ServerSession session, String destination, MessageAcceptMode acceptMode, @@ -130,6 +142,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { _subscriptionID = subscriptionId; _session = session; + _postIdSettingAction = new AddMessageDispositionListenerAction(session); _destination = destination; _acceptMode = acceptMode; _acquireMode = acquireMode; @@ -204,13 +217,13 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return false; } - - - if (_noLocal - && (entry.getMessage() instanceof MessageTransferMessage) - && ((MessageTransferMessage)entry.getMessage()).getSession() == _session) + if (_noLocal && entry.getMessage() instanceof MessageTransferMessage) { - return false; + Session messageSession= ((MessageTransferMessage)entry.getMessage()).getSession(); + if (messageSession != null && messageSession.getConnection() == _session.getConnection()) + { + return false; + } } @@ -307,10 +320,26 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr } - private class AddMessageDispositionListnerAction implements Runnable + public static class AddMessageDispositionListenerAction implements Runnable { - public MessageTransfer _xfr; - public ServerSession.MessageDispositionChangeListener _action; + private MessageTransfer _xfr; + private ServerSession.MessageDispositionChangeListener _action; + private ServerSession _session; + + public AddMessageDispositionListenerAction(ServerSession session) + { + _session = session; + } + + public void setXfr(MessageTransfer xfr) + { + _xfr = xfr; + } + + public void setAction(ServerSession.MessageDispositionChangeListener action) + { + _action = action; + } public void run() { @@ -321,9 +350,9 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr } } - private final AddMessageDispositionListnerAction _postIdSettingAction = new AddMessageDispositionListnerAction(); + private final AddMessageDispositionListenerAction _postIdSettingAction; - public void send(final QueueEntry entry) throws AMQException + public void send(final QueueEntry entry, boolean batch) throws AMQException { ServerMessage serverMsg = entry.getMessage(); @@ -568,27 +597,29 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { public void onComplete(Method method) { - restoreCredit(entry); + deferredAddCredit(1, entry.getSize()); } }); } - _postIdSettingAction._xfr = xfr; + _postIdSettingAction.setXfr(xfr); if(_acceptMode == MessageAcceptMode.EXPLICIT) { - _postIdSettingAction._action = new ExplicitAcceptDispositionChangeListener(entry, this); + _postIdSettingAction.setAction(new ExplicitAcceptDispositionChangeListener(entry, this)); } else if(_acquireMode != MessageAcquireMode.PRE_ACQUIRED) { - _postIdSettingAction._action = new ImplicitAcceptDispositionChangeListener(entry, this); + _postIdSettingAction.setAction(new ImplicitAcceptDispositionChangeListener(entry, this)); } else { - _postIdSettingAction._action = null; + _postIdSettingAction.setAction(null); } + _session.sendMessage(xfr, _postIdSettingAction); + entry.incrementDeliveryCount(); _deliveredCount.incrementAndGet(); if(_acceptMode == MessageAcceptMode.NONE && _acquireMode == MessageAcquireMode.PRE_ACQUIRED) { @@ -624,17 +655,74 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr }); } - void reject(QueueEntry entry) + void reject(final QueueEntry entry) { entry.setRedelivered(); entry.routeToAlternate(); } - void release(QueueEntry entry) + void release(final QueueEntry entry, final boolean setRedelivered) { - entry.setRedelivered(); - entry.release(); + if (setRedelivered) + { + entry.setRedelivered(); + } + + if (getSession().isClosing() || !setRedelivered) + { + entry.decrementDeliveryCount(); + } + + if (isMaxDeliveryLimitReached(entry)) + { + sendToDLQOrDiscard(entry); + } + else + { + entry.release(); + } + } + + protected void sendToDLQOrDiscard(QueueEntry entry) + { + final Exchange alternateExchange = entry.getQueue().getAlternateExchange(); + final LogActor logActor = CurrentActor.get(); + final ServerMessage msg = entry.getMessage(); + if (alternateExchange != null) + { + final InboundMessage m = new InboundMessageAdapter(entry); + + final ArrayList<? extends BaseQueue> destinationQueues = alternateExchange.route(m); + + if (destinationQueues == null || destinationQueues.isEmpty()) + { + entry.discard(); + + logActor.message( ChannelMessages.DISCARDMSG_NOROUTE(msg.getMessageNumber(), alternateExchange.getName())); + } + else + { + entry.routeToAlternate(); + + //output operational logging for each delivery post commit + for (final BaseQueue destinationQueue : destinationQueues) + { + logActor.message( ChannelMessages.DEADLETTERMSG(msg.getMessageNumber(), destinationQueue.getNameShortString().asString())); + } + } + } + else + { + entry.discard(); + logActor.message(ChannelMessages.DISCARDMSG_NOALTEXCH(msg.getMessageNumber(), entry.getQueue().getName(), msg.getRoutingKey())); + } + } + + private boolean isMaxDeliveryLimitReached(QueueEntry entry) + { + final int maxDeliveryLimit = entry.getQueue().getMaximumDeliveryCount(); + return (maxDeliveryLimit > 0 && entry.getDeliveryCount() >= maxDeliveryLimit); } public void queueDeleted(AMQQueue queue) @@ -642,9 +730,14 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr _deleted.set(true); } - public boolean wouldSuspend(QueueEntry msg) + public boolean wouldSuspend(QueueEntry entry) { - return !_creditManager.useCreditForMessage(msg.getMessage()); + return !_creditManager.useCreditForMessage(entry.getMessage().getSize()); + } + + public boolean trySendLock() + { + return _stateChangeLock.tryLock(); } public void getSendLock() @@ -664,7 +757,12 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr public void onDequeue(QueueEntry queueEntry) { + // no-op for 0-10, credit restored by completing command. + } + public void releaseQueueEntry(QueueEntry queueEntry) + { + // no-op for 0-10, credit restored by completing command. } public void setStateListener(StateListener listener) @@ -702,6 +800,28 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return _properties.get(key); } + private void deferredAddCredit(final int deferredMessageCredit, final long deferredSizeCredit) + { + _deferredMessageCredit += deferredMessageCredit; + _deferredSizeCredit += deferredSizeCredit; + + } + + public void flushCreditState() + { + flushCreditState(false); + } + public void flushCreditState(boolean strict) + { + if(strict || !isSuspended() || _deferredMessageCredit >= 200 + || !(_creditManager instanceof WindowCreditManager) + || ((WindowCreditManager)_creditManager).getMessageCreditLimit() < 400 ) + { + _creditManager.restoreCredit(_deferredMessageCredit, _deferredSizeCredit); + _deferredMessageCredit = 0; + _deferredSizeCredit = 0l; + } + } public FlowCreditManager_0_10 getCreditManager() { @@ -804,6 +924,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr public void flush() throws AMQException { + flushCreditState(true); _queue.flushSubscription(this); stop(); } @@ -947,4 +1068,9 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return (LogSubject) this; } + + public void flushBatched() + { + _session.getConnection().flush(); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java index e18b453db3..00f0c9f0f1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java @@ -24,6 +24,14 @@ import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.CONNECTIO import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.SOCKET_FORMAT; import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.USER_FORMAT; +import java.util.concurrent.atomic.AtomicLong; + +import javax.management.JMException; + +import org.apache.qpid.server.management.ManagedObject; + +import org.apache.qpid.server.management.Managable; + import java.security.Principal; import java.text.MessageFormat; import java.util.ArrayList; @@ -55,7 +63,7 @@ import org.apache.qpid.transport.Method; import org.apache.qpid.transport.ProtocolEvent; import org.apache.qpid.transport.Session; -public class ServerConnection extends Connection implements AMQConnectionModel, LogSubject, AuthorizationHolder +public class ServerConnection extends Connection implements Managable, AMQConnectionModel, LogSubject, AuthorizationHolder { private ConnectionConfig _config; private Runnable _onOpenTask; @@ -67,6 +75,10 @@ public class ServerConnection extends Connection implements AMQConnectionModel, private boolean _statisticsEnabled = false; private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; private final long _connectionId; + + private ServerConnectionMBean _mBean; + private VirtualHost _virtualHost; + private AtomicLong _lastIoTime = new AtomicLong(); public ServerConnection(final long connectionId) { @@ -133,9 +145,6 @@ public class ServerConnection extends Connection implements AMQConnectionModel, super.setConnectionDelegate(delegate); } - private VirtualHost _virtualHost; - - public VirtualHost getVirtualHost() { return _virtualHost; @@ -144,8 +153,18 @@ public class ServerConnection extends Connection implements AMQConnectionModel, public void setVirtualHost(VirtualHost virtualHost) { _virtualHost = virtualHost; - + initialiseStatistics(); + + try + { + _mBean = new ServerConnectionMBean(this); + _mBean.register(); + } + catch (JMException jme) + { + log.error("Unable to create mBean for ServerConnection",jme); + } } public void setConnectionConfig(final ConnectionConfig config) @@ -190,6 +209,7 @@ public class ServerConnection extends Connection implements AMQConnectionModel, @Override public void received(ProtocolEvent event) { + _lastIoTime.set(System.currentTimeMillis()); if (event.isConnectionControl()) { CurrentActor.set(_actor); @@ -260,6 +280,11 @@ public class ServerConnection extends Connection implements AMQConnectionModel, public void close(AMQConstant cause, String message) throws AMQException { closeSubscriptions(); + if (_mBean != null) + { + _mBean.unregister(); + _mBean = null; + } ConnectionCloseCode replyCode = ConnectionCloseCode.NORMAL; try { @@ -405,6 +430,11 @@ public class ServerConnection extends Connection implements AMQConnectionModel, public void closed() { closeSubscriptions(); + if (_mBean != null) + { + _mBean.unregister(); + _mBean = null; + } super.closed(); } @@ -416,4 +446,38 @@ public class ServerConnection extends Connection implements AMQConnectionModel, } } + public void receivedComplete() + { + for (Session ssn : getChannels()) + { + ((ServerSession)ssn).flushCreditState(); + } + } + + @Override + public ManagedObject getManagedObject() + { + return _mBean; + } + + @Override + public void send(ProtocolEvent event) + { + _lastIoTime.set(System.currentTimeMillis()); + super.send(event); + } + + public AtomicLong getLastIoTime() + { + return _lastIoTime; + } + + void checkForNotification() + { + int channelsCount = getSessionModels().size(); + if (_mBean != null && channelsCount >= getConnectionDelegate().getChannelMax()) + { + _mBean.notifyClients("Channel count (" + channelsCount + ") has reached the threshold value"); + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java index 8d6e0e0d80..66ed6f1e62 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java @@ -28,10 +28,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; - import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; - import org.apache.qpid.common.ServerPropertyNames; import org.apache.qpid.protocol.ProtocolEngine; import org.apache.qpid.server.configuration.BrokerConfig; @@ -49,6 +47,7 @@ import org.apache.qpid.transport.ConnectionClose; import org.apache.qpid.transport.ConnectionCloseCode; import org.apache.qpid.transport.ConnectionOpen; import org.apache.qpid.transport.ConnectionOpenOk; +import org.apache.qpid.transport.ConnectionStartOk; import org.apache.qpid.transport.ConnectionTuneOk; import org.apache.qpid.transport.ServerDelegate; import org.apache.qpid.transport.Session; @@ -62,6 +61,8 @@ public class ServerConnectionDelegate extends ServerDelegate { private final String _localFQDN; private final IApplicationRegistry _appRegistry; + private int _maxNoOfChannels; + private Map<String,Object> _clientProperties; public ServerConnectionDelegate(IApplicationRegistry appRegistry, String localFQDN) { @@ -77,6 +78,7 @@ public class ServerConnectionDelegate extends ServerDelegate _appRegistry = appRegistry; _localFQDN = localFQDN; + _maxNoOfChannels = ApplicationRegistry.getInstance().getConfiguration().getMaxChannelCount(); } private static Map<String, Object> createConnectionProperties(final BrokerConfig brokerConfig) @@ -154,7 +156,7 @@ public class ServerConnectionDelegate extends ServerDelegate public void connectionOpen(Connection conn, ConnectionOpen open) { final ServerConnection sconn = (ServerConnection) conn; - + VirtualHost vhost; String vhostName; if(open.hasVirtualHost()) @@ -222,7 +224,12 @@ public class ServerConnectionDelegate extends ServerDelegate @Override protected int getChannelMax() { - return ApplicationRegistry.getInstance().getConfiguration().getMaxChannelCount(); + return _maxNoOfChannels; + } + + protected void setChannelMax(int channelMax) + { + _maxNoOfChannels = channelMax; } @Override public void sessionDetach(Connection conn, SessionDetach dtc) @@ -253,6 +260,7 @@ public class ServerConnectionDelegate extends ServerDelegate { ssn = sessionAttachImpl(conn, atc); conn.registerSession(ssn); + ((ServerConnection)conn).checkForNotification(); } else { @@ -279,4 +287,16 @@ public class ServerConnectionDelegate extends ServerDelegate } return true; } + + @Override + public void connectionStartOk(Connection conn, ConnectionStartOk ok) + { + _clientProperties = ok.getClientProperties(); + super.connectionStartOk(conn, ok); + } + + public Map<String,Object> getClientProperties() + { + return _clientProperties; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionMBean.java new file mode 100644 index 0000000000..17c7bed601 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionMBean.java @@ -0,0 +1,264 @@ +/* + * + * 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.transport; + +import java.io.IOException; +import java.util.Date; +import java.util.List; +import javax.management.JMException; +import javax.management.NotCompliantMBeanException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; + +import org.apache.qpid.common.ClientProperties; +import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; +import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.ManagementActor; +import org.apache.qpid.server.management.AbstractAMQManagedConnectionObject; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.protocol.AMQSessionModel; + +/** + * This MBean class implements the management interface. In order to make more attributes, operations and notifications + * available over JMX simply augment the ManagedConnection interface and add the appropriate implementation here. + */ +@MBeanDescription("Management Bean for an AMQ Broker 0-10 Connection") +public class ServerConnectionMBean extends AbstractAMQManagedConnectionObject +{ + private final ServerConnection _serverConnection; + + @MBeanConstructor("Creates an MBean exposing an AMQ Broker 0-10 Connection") + protected ServerConnectionMBean(final ServerConnection serverConnection) throws NotCompliantMBeanException + { + super(serverConnection.getConfig().getAddress()); + _serverConnection = serverConnection; + } + + @Override + public ManagedObject getParentObject() + { + return _serverConnection.getVirtualHost().getManagedObject(); + } + + @Override + public String getClientId() + { + return _serverConnection.getClientId(); + } + + @Override + public String getAuthorizedId() + { + return _serverConnection.getAuthorizedPrincipal().getName(); + } + + @Override + public String getVersion() + { + return String.valueOf(_serverConnection.getConnectionDelegate().getClientProperties().get(ClientProperties.version.toString())); + } + + @Override + public String getRemoteAddress() + { + return _serverConnection.getConfig().getAddress(); + } + + @Override + public Date getLastIoTime() + { + return new Date(_serverConnection.getLastIoTime().longValue()); + } + + @Override + public Long getMaximumNumberOfChannels() + { + return (long) _serverConnection.getConnectionDelegate().getChannelMax(); + } + + @Override + public TabularData channels() throws IOException, JMException + { + final TabularDataSupport channelsList = new TabularDataSupport(_channelsType); + final List<AMQSessionModel> list = _serverConnection.getSessionModels(); + + for (final AMQSessionModel channel : list) + { + final ServerSession session = (ServerSession)channel; + Object[] itemValues = + { + session.getChannel(), + session.isTransactional(), + null, + session.getUnacknowledgedMessageCount(), + session.getBlocking() + }; + + final CompositeData channelData = new CompositeDataSupport(_channelType, + COMPOSITE_ITEM_NAMES_DESC.toArray(new String[COMPOSITE_ITEM_NAMES_DESC.size()]), itemValues); + channelsList.put(channelData); + } + return channelsList; + } + + @Override + public void commitTransactions(int channelId) throws JMException + { + final ServerSession session = (ServerSession)_serverConnection.getSession(channelId); + if (session == null) + { + throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); + } + else if (session.isTransactional()) + { + CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger())); + try + { + session.commit(); + } + finally + { + CurrentActor.remove(); + } + } + } + + @Override + public void rollbackTransactions(int channelId) throws JMException + { + final ServerSession session = (ServerSession)_serverConnection.getSession(channelId); + if (session == null) + { + throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); + } + else if (session.isTransactional()) + { + CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger())); + try + { + session.rollback(); + } + finally + { + CurrentActor.remove(); + } + } + } + + @Override + public void closeConnection() throws Exception + { + _serverConnection.mgmtClose(); + } + + @Override + public void resetStatistics() throws Exception + { + _serverConnection.resetStatistics(); + } + + @Override + public double getPeakMessageDeliveryRate() + { + return _serverConnection.getMessageDeliveryStatistics().getPeak(); + } + + @Override + public double getPeakDataDeliveryRate() + { + return _serverConnection.getDataDeliveryStatistics().getPeak(); + } + + @Override + public double getMessageDeliveryRate() + { + return _serverConnection.getMessageDeliveryStatistics().getRate(); + } + + @Override + public double getDataDeliveryRate() + { + return _serverConnection.getDataDeliveryStatistics().getRate(); + } + + @Override + public long getTotalMessagesDelivered() + { + return _serverConnection.getMessageDeliveryStatistics().getTotal(); + } + + @Override + public long getTotalDataDelivered() + { + return _serverConnection.getDataDeliveryStatistics().getTotal(); + } + + @Override + public double getPeakMessageReceiptRate() + { + return _serverConnection.getMessageReceiptStatistics().getPeak(); + } + + @Override + public double getPeakDataReceiptRate() + { + return _serverConnection.getDataReceiptStatistics().getPeak(); + } + + @Override + public double getMessageReceiptRate() + { + return _serverConnection.getMessageReceiptStatistics().getRate(); + } + + @Override + public double getDataReceiptRate() + { + return _serverConnection.getDataReceiptStatistics().getRate(); + } + + @Override + public long getTotalMessagesReceived() + { + return _serverConnection.getMessageReceiptStatistics().getTotal(); + } + + @Override + public long getTotalDataReceived() + { + return _serverConnection.getDataReceiptStatistics().getTotal(); + } + + @Override + public boolean isStatisticsEnabled() + { + return _serverConnection.isStatisticsEnabled(); + } + + @Override + public void setStatisticsEnabled(boolean enabled) + { + _serverConnection.setStatisticsEnabled(enabled); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java index 429cc4cf66..23b77b1fd9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java @@ -37,9 +37,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; - import javax.security.auth.Subject; - import org.apache.qpid.AMQException; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.ProtocolEngine; @@ -93,7 +91,7 @@ public class ServerSession extends Session implements AuthorizationHolder, Sessi { public void onAccept(); - public void onRelease(); + public void onRelease(boolean setRedelivered); public void onReject(); @@ -230,13 +228,13 @@ public class ServerSession extends Session implements AuthorizationHolder, Sessi } - public void release(RangeSet ranges) + public void release(RangeSet ranges, final boolean setRedelivered) { dispositionChange(ranges, new MessageDispositionAction() { public void performAction(MessageDispositionChangeListener listener) { - listener.onRelease(); + listener.onRelease(setRedelivered); } }); } @@ -350,7 +348,7 @@ public class ServerSession extends Session implements AuthorizationHolder, Sessi _transaction.rollback(); for(MessageDispositionChangeListener listener : _messageDispositionListenerMap.values()) { - listener.onRelease(); + listener.onRelease(true); } _messageDispositionListenerMap.clear(); @@ -698,4 +696,23 @@ public class ServerSession extends Session implements AuthorizationHolder, Sessi unregister(subscription_0_10); } } + + public void flushCreditState() + { + final Collection<Subscription_0_10> subscriptions = getSubscriptions(); + for (Subscription_0_10 subscription_0_10 : subscriptions) + { + subscription_0_10.flushCreditState(false); + } + } + + public int getUnacknowledgedMessageCount() + { + return _messageDispositionListenerMap.size(); + } + + public boolean getBlocking() + { + return false; //TODO: Blocking not implemented on 0-10 yet. + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java index c87919b478..a0dca53ed0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java @@ -100,6 +100,11 @@ public class ServerSessionDelegate extends SessionDelegate { private static final Logger LOGGER = Logger.getLogger(ServerSessionDelegate.class); + /** + * No-local queue argument is used to support the no-local feature of Durable Subscribers. + */ + private static final String QUEUE_ARGUMENT_NO_LOCAL = "no-local"; + public ServerSessionDelegate() { @@ -143,7 +148,7 @@ public class ServerSessionDelegate extends SessionDelegate @Override public void messageRelease(Session session, MessageRelease method) { - ((ServerSession)session).release(method.getTransfers()); + ((ServerSession)session).release(method.getTransfers(), method.getSetRedelivered()); } @Override @@ -963,13 +968,10 @@ public class ServerSessionDelegate extends SessionDelegate if(method.hasArguments() && method.getArguments() != null) { - if(method.getArguments().containsKey("no-local")) + if(method.getArguments().containsKey(QUEUE_ARGUMENT_NO_LOCAL)) { - Object no_local = method.getArguments().get("no-local"); - if(no_local instanceof Boolean && ((Boolean)no_local)) - { - queue.setNoLocal(true); - } + Object noLocal = method.getArguments().get(QUEUE_ARGUMENT_NO_LOCAL); + queue.setNoLocal(convertBooleanValue(noLocal)); } } @@ -1079,6 +1081,30 @@ public class ServerSessionDelegate extends SessionDelegate } } + /** + * Converts a queue argument into a boolean value. For compatibility with the C++ + * and the clients, accepts with Boolean, String, or Number types. + * @param argValue argument value. + * + * @return true if set + */ + private boolean convertBooleanValue(Object argValue) + { + if(argValue instanceof Boolean && ((Boolean)argValue)) + { + return true; + } + else if (argValue instanceof String && Boolean.parseBoolean((String)argValue)) + { + return true; + } + else if (argValue instanceof Number && ((Number)argValue).intValue() != 0) + { + return true; + } + return false; + } + protected AMQQueue createQueue(final String queueName, final QueueDeclare body, VirtualHost virtualHost, diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetection.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetection.java index 5c4fe0aab8..248b3b2143 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetection.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/plugins/SlowConsumerDetection.java @@ -26,11 +26,13 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionConfiguration; import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionQueueConfiguration; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.plugins.Plugin; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.plugins.logging.SlowConsumerDetectionMessages; +import org.apache.qpid.slowconsumerdetection.policies.SlowConsumerPolicyPlugin; public class SlowConsumerDetection extends VirtualHostHouseKeepingPlugin { @@ -56,7 +58,7 @@ public class SlowConsumerDetection extends VirtualHostHouseKeepingPlugin /** * Configures the slow consumer disconnect plugin by adding a listener to each exchange on this - * cirtual host to record all the configured queues in a cache for processing by the housekeeping + * virtual host to record all the configured queues in a cache for processing by the housekeeping * thread. * * @see Plugin#configure(ConfigurationPlugin) @@ -65,9 +67,10 @@ public class SlowConsumerDetection extends VirtualHostHouseKeepingPlugin { _config = (SlowConsumerDetectionConfiguration) config; _listener = new ConfiguredQueueBindingListener(getVirtualHost().getName()); - for (AMQShortString exchangeName : getVirtualHost().getExchangeRegistry().getExchangeNames()) + final ExchangeRegistry exchangeRegistry = getVirtualHost().getExchangeRegistry(); + for (AMQShortString exchangeName : exchangeRegistry.getExchangeNames()) { - getVirtualHost().getExchangeRegistry().getExchange(exchangeName).addBindingListener(_listener); + exchangeRegistry.getExchange(exchangeName).addBindingListener(_listener); } } @@ -87,11 +90,21 @@ public class SlowConsumerDetection extends VirtualHostHouseKeepingPlugin try { - SlowConsumerDetectionQueueConfiguration config = + final SlowConsumerDetectionQueueConfiguration config = q.getConfiguration().getConfiguration(SlowConsumerDetectionQueueConfiguration.class.getName()); if (checkQueueStatus(q, config)) { - config.getPolicy().performPolicy(q); + final SlowConsumerPolicyPlugin policy = config.getPolicy(); + if (policy == null) + { + // We would only expect to see this during shutdown + _logger.warn("No slow consumer policy for queue " + q.getName()); + } + else + { + policy.performPolicy(q); + } + } } catch (Exception e) @@ -126,7 +139,10 @@ public class SlowConsumerDetection extends VirtualHostHouseKeepingPlugin { if (config != null) { - _logger.info("Retrieved Queue(" + q.getName() + ") Config:" + config); + if (_logger.isInfoEnabled()) + { + _logger.info("Retrieved Queue(" + q.getName() + ") Config:" + config); + } int count = q.getMessageCount(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java deleted file mode 100644 index 2c0ceed80b..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java +++ /dev/null @@ -1,652 +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.tools.messagestore; - -import org.apache.commons.cli.Option; -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.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; -import org.apache.qpid.tools.messagestore.commands.Dump; -import org.apache.qpid.tools.messagestore.commands.Help; -import org.apache.qpid.tools.messagestore.commands.List; -import org.apache.qpid.tools.messagestore.commands.Load; -import org.apache.qpid.tools.messagestore.commands.Quit; -import org.apache.qpid.tools.messagestore.commands.Select; -import org.apache.qpid.tools.messagestore.commands.Show; -import org.apache.qpid.tools.messagestore.commands.Move; -import org.apache.qpid.tools.messagestore.commands.Purge; -import org.apache.qpid.tools.utils.CommandParser; -import org.apache.qpid.tools.utils.Console; -import org.apache.qpid.tools.utils.SimpleCommandParser; -import org.apache.qpid.tools.utils.SimpleConsole; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.StringTokenizer; - -/** - * MessageStoreTool. - */ -public class MessageStoreTool -{ - /** Text outputted at the start of each console.*/ - private static final String BOILER_PLATE = "MessageStoreTool - for examining Persistent Qpid Broker MessageStore instances"; - - /** I/O Wrapper. */ - protected Console _console; - - /** Batch mode flag. */ - protected boolean _batchMode; - - /** Internal State object. */ - private State _state = new State(); - - private HashMap<String, Command> _commands = new HashMap<String, Command>(); - - /** SLF4J Logger. */ - private static Logger _devlog = LoggerFactory.getLogger(MessageStoreTool.class); - - /** Loaded configuration file. */ - private Configuration _config; - - /** Control used for main run loop. */ - private boolean _running = true; - private boolean _initialised = false; - - //---------------------------------------------------------------------------------------------------/ - - public static void main(String[] args) throws Configuration.InitException - { - - MessageStoreTool tool = new MessageStoreTool(args); - - tool.start(); - } - - - public MessageStoreTool(String[] args) throws Configuration.InitException - { - this(args, System.in, System.out); - } - - public MessageStoreTool(String[] args, InputStream in, OutputStream out) throws Configuration.InitException - { - BufferedReader consoleReader = new BufferedReader(new InputStreamReader(in)); - BufferedWriter consoleWriter = new BufferedWriter(new OutputStreamWriter(out)); - - Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook(this))); - _batchMode = false; - - _console = new SimpleConsole(consoleWriter, consoleReader); - - _config = new Configuration(); - - setOptions(); - _config.processCommandline(args); - } - - - private void setOptions() - { - Option help = new Option("h", "help", false, "print this message"); - Option version = new Option("v", "version", false, "print the version information and exit"); - Option configFile = - OptionBuilder.withArgName("file").hasArg() - .withDescription("use given configuration file By " - + "default looks for a file named " - + Configuration.DEFAULT_CONFIG_FILE + " in " + Configuration.QPID_HOME) - .withLongOpt("config") - .create("c"); - - _config.setOption(help); - _config.setOption(version); - _config.setOption(configFile); - } - - public State getState() - { - return _state; - } - - public Map<String, Command> getCommands() - { - return _commands; - } - - public void setConfigurationFile(String configfile) throws Configuration.InitException - { - _config.loadConfig(new File(configfile)); - setup(); - } - - public Console getConsole() - { - return _console; - } - - public void setConsole(Console console) - { - _console = console; - } - - /** - * Simple ShutdownHook to cleanly shutdown the databases - */ - static class ShutdownHook implements Runnable - { - MessageStoreTool _tool; - - ShutdownHook(MessageStoreTool messageStoreTool) - { - _tool = messageStoreTool; - } - - public void run() - { - _tool.quit(); - } - } - - public void quit() - { - _running = false; - - if (_initialised) - { - ApplicationRegistry.remove(); - } - - _console.println("...exiting"); - - _console.close(); - } - - public void setBatchMode(boolean batchmode) - { - _batchMode = batchmode; - } - - /** - * Main loop - */ - protected void start() - { - setup(); - - if (!_initialised) - { - System.exit(1); - } - - _console.println(""); - - _console.println(BOILER_PLATE); - - runCLI(); - } - - private void setup() - { - loadDefaultVirtualHosts(); - - loadCommands(); - - _state.clearAll(); - } - - private void loadCommands() - { - _commands.clear(); - //todo Dynamically load the classes that exis in com.redhat.etp.qpid.commands - _commands.put("close", new Clear(this)); - _commands.put("copy", new Copy(this)); - _commands.put("dump", new Dump(this)); - _commands.put("help", new Help(this)); - _commands.put("list", new List(this)); - _commands.put("load", new Load(this)); - _commands.put("move", new Move(this)); - _commands.put("purge", new Purge(this)); - _commands.put("quit", new Quit(this)); - _commands.put("select", new Select(this)); - _commands.put("show", new Show(this)); - } - - private void loadDefaultVirtualHosts() - { - final File configFile = _config.getConfigFile(); - - loadVirtualHosts(configFile); - } - - private void loadVirtualHosts(File configFile) - { - - if (!configFile.exists()) - { - _devlog.error("Config file not found:" + configFile.getAbsolutePath()); - return; - } - else - { - _devlog.debug("using config file :" + configFile.getAbsolutePath()); - } - - try - { - ConfigurationFileApplicationRegistry registry = new ConfigurationFileApplicationRegistry(configFile); - - ApplicationRegistry.remove(); - - ApplicationRegistry.initialise(registry); - - checkMessageStores(); - _initialised = true; - } - catch (ConfigurationException e) - { - _console.println("Unable to load configuration due to configuration error: " + e.getMessage()); - e.printStackTrace(); - } - catch (Exception e) - { - _console.println("Unable to load configuration due to: " + e.getMessage()); - e.printStackTrace(); - } - - - } - - private void checkMessageStores() - { - Collection<VirtualHost> vhosts = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts(); - - boolean warning = false; - for (VirtualHost vhost : vhosts) - { - if (vhost.getMessageStore() instanceof MemoryMessageStore) - { - _console.println("WARNING: Virtualhost '" + vhost.getName() + "' is using a MemoryMessageStore. " - + "Changes will not persist."); - warning = true; - } - } - - if (warning) - { - _console.println(""); - _console.println("Please ensure you are using the correct config file currently using '" - + _config.getConfigFile().getAbsolutePath() + "'"); - _console.println("New config file can be specifed by 'load <config file>' or -c on the commandline."); - _console.println(""); - } - } - - private void runCLI() - { - while (_running) - { - if (!_batchMode) - { - printPrompt(); - } - - String[] args = _console.readCommand(); - - while (args != null) - { - exec(args); - - if (_running) - { - if (!_batchMode) - { - printPrompt(); - } - - args = _console.readCommand(); - } - } - } - } - - private void printPrompt() - { - _console.print(prompt()); - } - - - /** - * Execute a script (batch mode). - * - * @param script The file script - */ - protected void runScripts(String script) - { - //Store Current State - boolean oldBatch = _batchMode; - CommandParser oldParser = _console.getCommandParser(); - setBatchMode(true); - - try - { - _devlog.debug("Running script '" + script + "'"); - - _console.setCommandParser(new SimpleCommandParser(new BufferedReader(new FileReader(script)))); - - start(); - } - catch (java.io.FileNotFoundException e) - { - _devlog.error("Script not found: '" + script + "' due to:" + e.getMessage()); - } - - //Restore previous state - _console.setCommandParser(oldParser); - setBatchMode(oldBatch); - } - - public String prompt() - { - String state = _state.toString(); - if (state != null && state.length() != 0) - { - return state + ":bdb$ "; - } - else - { - return "bdb$ "; - } - } - - /** - * Execute the command. - * - * @param args [command, arg0, arg1...]. - */ - protected void exec(String[] args) - { - // Comment lines start with a # - if (args.length == 0 || args[0].startsWith("#")) - { - return; - } - - final String command = args[0]; - - Command cmd = _commands.get(command); - - if (cmd == null) - { - _console.println("Command not understood: " + command); - } - else - { - cmd.execute(args); - } - } - - - /** - * Displays usage info. - */ - protected static void help() - { - System.out.println(BOILER_PLATE); - System.out.println("Usage: java " + MessageStoreTool.class + " [Options]"); - System.out.println(" [-c <broker config file>] : Defaults to \"$QPID_HOME/etc/config.xml\""); - } - - - /** - * This class is used to store the current state of the tool. - * - * This is then interrogated by the various commands to augment their behaviour. - * - * - */ - public static class State - { - private VirtualHost _vhost = null; - private AMQQueue _queue = null; - private Exchange _exchange = null; - private java.util.List<Long> _msgids = null; - - public State() - { - } - - public void setQueue(AMQQueue queue) - { - _queue = queue; - } - - public AMQQueue getQueue() - { - return _queue; - } - - public void setVhost(VirtualHost vhost) - { - _vhost = vhost; - } - - public VirtualHost getVhost() - { - return _vhost; - } - - public Exchange getExchange() - { - return _exchange; - } - - public void setExchange(Exchange exchange) - { - _exchange = exchange; - } - - public String toString() - { - StringBuilder status = new StringBuilder(); - - if (_vhost != null) - { - status.append(_vhost.getName()); - - if (_exchange != null) - { - status.append("["); - status.append(_exchange.getNameShortString()); - status.append("]"); - - if (_queue != null) - { - status.append("->'"); - status.append(_queue.getNameShortString()); - status.append("'"); - - if (_msgids != null) - { - status.append(printMessages()); - } - } - } - } - - return status.toString(); - } - - - public String printMessages() - { - StringBuilder sb = new StringBuilder(); - - Long previous = null; - - Long start = null; - for (Long id : _msgids) - { - if (previous != null) - { - if (id == previous + 1) - { - if (start == null) - { - start = previous; - } - } - else - { - if (start != null) - { - sb.append(","); - sb.append(start); - sb.append("-"); - sb.append(id); - start = null; - } - else - { - sb.append(","); - sb.append(previous); - } - } - } - - previous = id; - } - - if (start != null) - { - sb.append(","); - sb.append(start); - sb.append("-"); - sb.append(_msgids.get(_msgids.size() - 1)); - } - else - { - sb.append(","); - sb.append(previous); - } - - // surround list in () - sb.replace(0, 1, "("); - sb.append(")"); - return sb.toString(); - } - - public void clearAll() - { - _vhost = null; - clearExchange(); - } - - public void clearExchange() - { - _exchange = null; - clearQueue(); - } - - public void clearQueue() - { - _queue = null; - clearMessages(); - } - - public void clearMessages() - { - _msgids = null; - } - - /** - * A common location to provide parsing of the message id string - * utilised by a number of the commands. - * The String is comma separated list of ids that can be individual ids - * or a range (4-10) - * - * @param msgString string of msg ids to parse 1,2,4-10 - */ - public void setMessages(String msgString) - { - StringTokenizer tok = new StringTokenizer(msgString, ","); - - if (tok.hasMoreTokens()) - { - _msgids = new LinkedList<Long>(); - } - - while (tok.hasMoreTokens()) - { - String next = tok.nextToken(); - if (next.contains("-")) - { - Long start = Long.parseLong(next.substring(0, next.indexOf("-"))); - Long end = Long.parseLong(next.substring(next.indexOf("-") + 1)); - - if (end >= start) - { - for (long l = start; l <= end; l++) - { - _msgids.add(l); - } - } - } - else - { - _msgids.add(Long.parseLong(next)); - } - } - - } - - public void setMessages(java.util.List<Long> msgids) - { - _msgids = msgids; - } - - public java.util.List<Long> getMessages() - { - return _msgids; - } - }//Class State - -}//Class MessageStoreTool diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java deleted file mode 100644 index 5444197cb4..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java +++ /dev/null @@ -1,66 +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.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.tools.utils.Console; - -public abstract class AbstractCommand implements Command -{ - protected Console _console; - protected MessageStoreTool _tool; - - public AbstractCommand(MessageStoreTool tool) - { - _console = tool.getConsole(); - _tool = tool; - } - - public void setOutput(Console out) - { - _console = out; - } - - protected void commandError(String message, String[] args) - { - _console.print(getCommand() + " : " + message); - - if (args != null) - { - for (int i = 1; i < args.length; i++) - { - _console.print(args[i]); - } - } - _console.println(""); - _console.println(help()); - } - - - public abstract String help(); - - public abstract String usage(); - - public abstract String getCommand(); - - - public abstract void execute(String... args); -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java deleted file mode 100644 index b0006b3fe6..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java +++ /dev/null @@ -1,85 +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.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; - -public class Clear extends AbstractCommand -{ - public Clear(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Clears any selection."; - } - - public String usage() - { - return "clear [ all | virtualhost | exchange | queue | msgs ]"; - } - - public String getCommand() - { - return "clear"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - if (args.length < 1) - { - doClose("all"); - } - else - { - doClose(args[1]); - } - } - - private void doClose(String type) - { - if (type.equals("virtualhost") - || type.equals("all")) - { - _tool.getState().clearAll(); - } - - if (type.equals("exchange")) - { - _tool.getState().clearExchange(); - } - - if (type.equals("queue")) - { - _tool.getState().clearQueue(); - } - - if (type.equals("msgs")) - { - _tool.getState().clearMessages(); - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java deleted file mode 100644 index bfa775a34a..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java +++ /dev/null @@ -1,36 +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.tools.messagestore.commands; - -import org.apache.qpid.tools.utils.Console; - -public interface Command -{ - public void setOutput(Console out); - - public String help(); - - public abstract String usage(); - - String getCommand(); - - public void execute(String... args); -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java deleted file mode 100644 index 348c95572d..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java +++ /dev/null @@ -1,59 +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.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.txn.ServerTransaction; -import org.apache.qpid.server.txn.LocalTransaction; - -public class Copy extends Move -{ - public Copy(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Copy messages between queues.";/*\n" + - "The currently selected message set will be copied to the specifed queue.\n" + - "Alternatively the values can be provided on the command line."; */ - } - - public String usage() - { - return "copy to=<queue> [from=<queue>] [msgids=<msgids eg, 1,2,4-10>]"; - } - - public String getCommand() - { - return "copy"; - } - - protected void doCommand(AMQQueue fromQueue, long start, long end, AMQQueue toQueue) - { - ServerTransaction txn = new LocalTransaction(fromQueue.getVirtualHost().getTransactionLog()); - fromQueue.copyMessagesToAnotherQueue(start, end, toQueue.getNameShortString().toString(), txn); - txn.commit(); - } - -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java deleted file mode 100644 index 8bb5d02b01..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java +++ /dev/null @@ -1,305 +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.tools.messagestore.commands; - -import org.apache.commons.codec.binary.Hex; -import org.apache.qpid.server.queue.QueueEntryImpl; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.tools.utils.Console; - -import java.io.UnsupportedEncodingException; -import java.util.LinkedList; -import java.util.List; - -public class Dump extends Show -{ - private static final int LINE_SIZE = 8; - private static final String DEFAULT_ENCODING = "utf-8"; - private static final boolean SPACE_BYTES = true; - private static final String BYTE_SPACER = " "; - private static final String NON_PRINTING_ASCII_CHAR = "?"; - - protected boolean _content = true; - - public Dump(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Dump selected message content. Default: show=content"; - } - - public String usage() - { - return getCommand() + " [show=[all],[msgheaders],[_amqHeaders],[routing],[content]] [id=<msgid e.g. 1,2,4-10>]"; - } - - public String getCommand() - { - return "dump"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - - if (args.length >= 2) - { - for (String arg : args) - { - if (arg.startsWith("show=")) - { - _content = arg.contains("content") || arg.contains("all"); - } - } - - parseArgs(args); - } - - performShow(); - } - - - protected List<List> createMessageData(java.util.List<Long> msgids, List<QueueEntry> messages, boolean showHeaders, boolean showRouting, - boolean showMessageHeaders) - { - - List<List> display = new LinkedList<List>(); - - List<String> hex = new LinkedList<String>(); - List<String> ascii = new LinkedList<String>(); - display.add(hex); - display.add(ascii); - - for (QueueEntry entry : messages) - { - ServerMessage msg = entry.getMessage(); - if (!includeMsg(msg, msgids)) - { - continue; - } - - //Add divider between messages - hex.add(Console.ROW_DIVIDER); - ascii.add(Console.ROW_DIVIDER); - - // Show general message information - hex.add(Show.Columns.ID.name()); - ascii.add(msg.getMessageNumber().toString()); - - hex.add(Console.ROW_DIVIDER); - ascii.add(Console.ROW_DIVIDER); - - if (showRouting) - { - addShowInformation(hex, ascii, msg, "Routing Details", true, false, false); - } - if (showHeaders) - { - addShowInformation(hex, ascii, msg, "Headers", false, true, false); - } - if (showMessageHeaders) - { - addShowInformation(hex, ascii, msg, null, false, false, true); - } - - // Add Content Body section - hex.add("Content Body"); - ascii.add(""); - hex.add(Console.ROW_DIVIDER); - ascii.add(Console.ROW_DIVIDER); - - - final int messageSize = (int) msg.getSize(); - if (messageSize != 0) - { - hex.add("Hex"); - hex.add(Console.ROW_DIVIDER); - - - ascii.add("ASCII"); - ascii.add(Console.ROW_DIVIDER); - - java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocate(64 * 1024); - - int position = 0; - - while(position < messageSize) - { - - position += msg.getContent(buf, position); - buf.flip(); - //Duplicate so we don't destroy original data :) - java.nio.ByteBuffer hexBuffer = buf; - - java.nio.ByteBuffer charBuffer = hexBuffer.duplicate(); - - Hex hexencoder = new Hex(); - - while (hexBuffer.hasRemaining()) - { - byte[] line = new byte[LINE_SIZE]; - - int bufsize = hexBuffer.remaining(); - if (bufsize < LINE_SIZE) - { - hexBuffer.get(line, 0, bufsize); - } - else - { - bufsize = line.length; - hexBuffer.get(line); - } - - byte[] encoded = hexencoder.encode(line); - - try - { - String encStr = new String(encoded, 0, bufsize * 2, DEFAULT_ENCODING); - String hexLine = ""; - - int strLength = encStr.length(); - for (int c = 0; c < strLength; c++) - { - hexLine += encStr.charAt(c); - - if ((c & 1) == 1 && SPACE_BYTES) - { - hexLine += BYTE_SPACER; - } - } - - hex.add(hexLine); - } - catch (UnsupportedEncodingException e) - { - _console.println(e.getMessage()); - return null; - } - } - - while (charBuffer.hasRemaining()) - { - String asciiLine = ""; - - for (int pos = 0; pos < LINE_SIZE; pos++) - { - if (charBuffer.hasRemaining()) - { - byte ch = charBuffer.get(); - - if (isPrintable(ch)) - { - asciiLine += (char) ch; - } - else - { - asciiLine += NON_PRINTING_ASCII_CHAR; - } - - if (SPACE_BYTES) - { - asciiLine += BYTE_SPACER; - } - } - else - { - break; - } - } - - ascii.add(asciiLine); - } - buf.clear(); - } - } - else - { - List<String> result = new LinkedList<String>(); - - display.add(result); - result.add("No ContentBodies"); - } - } - - // if hex is empty then we have no data to display - if (hex.size() == 0) - { - return null; - } - - return display; - } - - private void addShowInformation(List<String> column1, List<String> column2, ServerMessage msg, - String title, boolean routing, boolean headers, boolean messageHeaders) - { - List<QueueEntry> single = new LinkedList<QueueEntry>(); - single.add(new QueueEntryImpl(null,msg, Long.MIN_VALUE)); - - List<List> routingData = super.createMessageData(null, single, headers, routing, messageHeaders); - - //Reformat data - if (title != null) - { - column1.add(title); - column2.add(""); - column1.add(Console.ROW_DIVIDER); - column2.add(Console.ROW_DIVIDER); - } - - // look at all columns in the routing Data - for (List item : routingData) - { - // the item should be: - // Title - // *divider - // value - // otherwise we can't reason about the correct value - if (item.size() == 3) - { - //Filter out the columns we are not interested in. - - String columnName = item.get(0).toString(); - - if (!(columnName.equals(Show.Columns.ID.name()) - || columnName.equals(Show.Columns.Size.name()))) - { - column1.add(columnName); - column2.add(item.get(2).toString()); - } - } - } - column1.add(Console.ROW_DIVIDER); - column2.add(Console.ROW_DIVIDER); - } - - private boolean isPrintable(byte c) - { - return c > 31 && c < 127; - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java deleted file mode 100644 index 0f9546541b..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java +++ /dev/null @@ -1,98 +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.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.tools.utils.Console; - -import java.util.LinkedList; -import java.util.Map; - -public class Help extends AbstractCommand -{ - public Help(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Provides detailed help on commands."; - } - - public String getCommand() - { - return "help"; - } - - public String usage() - { - return "help [<command>]"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - if (args.length > 1) - { - Command command = _tool.getCommands().get(args[1]); - if (command != null) - { - _console.println(command.help()); - _console.println("Usage:" + command.usage()); - } - else - { - commandError("Command not found: ", args); - } - } - else - { - java.util.List<java.util.List> data = new LinkedList<java.util.List>(); - - java.util.List<String> commandName = new LinkedList<String>(); - java.util.List<String> commandDescription = new LinkedList<String>(); - - data.add(commandName); - data.add(commandDescription); - - //Set up Headers - commandName.add("Command"); - commandDescription.add("Description"); - - commandName.add(Console.ROW_DIVIDER); - commandDescription.add(Console.ROW_DIVIDER); - - //Add current Commands with descriptions - Map<String, Command> commands = _tool.getCommands(); - - for (Command command : commands.values()) - { - commandName.add(command.getCommand()); - commandDescription.add(command.help()); - } - - _console.printMap("Available Commands", data); - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java deleted file mode 100644 index 3c4a0c8fac..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java +++ /dev/null @@ -1,314 +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.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.tools.messagestore.MessageStoreTool; -import org.apache.qpid.tools.utils.Console; - -import java.util.Collection; -import java.util.LinkedList; - -public class List extends AbstractCommand -{ - - public List(MessageStoreTool tool) - { - super(tool); - } - - public void setOutput(Console out) - { - _console = out; - } - - public String help() - { - return "list available items."; - } - - public String usage() - { - return "list queues [<exchange>] | exchanges | bindings [<exchange>] | all"; - } - - public String getCommand() - { - return "list"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - if (args.length > 1) - { - if ((args[1].equals("exchanges")) - || (args[1].equals("queues")) - || (args[1].equals("bindings")) - || (args[1].equals("all"))) - { - if (args.length == 2) - { - doList(args[1]); - } - else if (args.length == 3) - { - doList(args[1], args[2]); - } - } - else - { - commandError("Unknown options. ", args); - } - } - else if (args.length < 2) - { - doList("all"); - } - else - { - doList(args[1]); - } - } - - private void doList(String... listItem) - { - if (_tool.getState().getVhost() == null) - { - _console.println("No Virtualhost open. Open a Virtualhost first."); - listVirtualHosts(); - return; - } - - VirtualHost vhost = _tool.getState().getVhost(); - - java.util.List<String> data = null; - - if (listItem[0].equals("queues")) - { - if (listItem.length > 1) - { - data = listQueues(vhost, new AMQShortString(listItem[1])); - } - else - { - Exchange exchange = _tool.getState().getExchange(); - data = listQueues(vhost, exchange); - } - } - - if (listItem[0].equals("exchanges")) - { - data = listExchanges(vhost); - } - - if (listItem[0].equals("bindings")) - { - - if (listItem.length > 1) - { - data = listBindings(vhost, new AMQShortString(listItem[1])); - } - else - { - Exchange exchange = _tool.getState().getExchange(); - - data = listBindings(vhost, exchange); - } - } - - if (data != null) - { - if (data.size() == 1) - { - _console.println("No '" + listItem[0] + "' to display,"); - } - else - { - _console.displayList(true, data.toArray(new String[0])); - } - } - - - if (listItem[0].equals("all")) - { - - boolean displayed = false; - Exchange exchange = _tool.getState().getExchange(); - - //Do the display here for each one so that they are pretty printed - data = listQueues(vhost, exchange); - if (data != null) - { - displayed = true; - _console.displayList(true, data.toArray(new String[0])); - } - - if (exchange == null) - { - data = listExchanges(vhost); - if (data != null) - { - displayed = true; - _console.displayList(true, data.toArray(new String[0])); - } - } - - data = listBindings(vhost, exchange); - if (data != null) - { - displayed = true; - _console.displayList(true, data.toArray(new String[0])); - } - - if (!displayed) - { - _console.println("Nothing to list"); - } - } - } - - private void listVirtualHosts() - { - Collection<VirtualHost> vhosts = ApplicationRegistry.getInstance() - .getVirtualHostRegistry().getVirtualHosts(); - - String[] data = new String[vhosts.size() + 1]; - - data[0] = "Available VirtualHosts"; - - int index = 1; - for (VirtualHost vhost : vhosts) - { - data[index] = vhost.getName(); - index++; - } - - _console.displayList(true, data); - } - - private java.util.List<String> listBindings(VirtualHost vhost, AMQShortString exchangeName) - { - return listBindings(vhost, vhost.getExchangeRegistry().getExchange(exchangeName)); - } - - private java.util.List<String> listBindings(VirtualHost vhost, Exchange exchange) - { - Collection<AMQShortString> queues = vhost.getQueueRegistry().getQueueNames(); - - if (queues == null || queues.size() == 0) - { - return null; - } - - java.util.List<String> data = new LinkedList<String>(); - - data.add("Current Bindings"); - - for (AMQShortString queue : queues) - { - if (exchange != null) - { - if (exchange.isBound(queue)) - { - data.add(queue.toString()); - } - } - else - { - data.add(queue.toString()); - } - } - - return data; - } - - private java.util.List<String> listExchanges(VirtualHost vhost) - { - Collection<AMQShortString> queues = vhost.getExchangeRegistry().getExchangeNames(); - - if (queues == null || queues.size() == 0) - { - return null; - } - - java.util.List<String> data = new LinkedList<String>(); - - data.add("Available Exchanges"); - - for (AMQShortString queue : queues) - { - data.add(queue.toString()); - } - - return data; - } - - private java.util.List<String> listQueues(VirtualHost vhost, AMQShortString exchangeName) - { - return listQueues(vhost, vhost.getExchangeRegistry().getExchange(exchangeName)); - } - - private java.util.List<String> listQueues(VirtualHost vhost, Exchange exchange) - { - Collection<AMQQueue> queues = vhost.getQueueRegistry().getQueues(); - - if (queues == null || queues.size() == 0) - { - return null; - } - - java.util.List<String> data = new LinkedList<String>(); - - data.add("Available Queues"); - - for (AMQQueue queue : queues) - { - if (exchange != null) - { - if (exchange.isBound(queue)) - { - data.add(queue.getNameShortString().toString()); - } - } - else - { - data.add(queue.getNameShortString().toString()); - } - } - - if (exchange != null) - { - if (queues.size() == 1) - { - return null; - } - } - - return data; - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java deleted file mode 100644 index 244a311c30..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java +++ /dev/null @@ -1,94 +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.tools.messagestore.commands; - -import org.apache.qpid.configuration.Configuration; -import org.apache.qpid.tools.messagestore.MessageStoreTool; - -public class Load extends AbstractCommand -{ - public Load(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Loads specified broker configuration file."; - } - - public String usage() - { - return "load <configuration file>"; - } - - public String getCommand() - { - return "load"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - if (args.length > 2) - { - _console.print("load " + args[1] + ": additional options not understood:"); - for (int i = 2; i < args.length; i++) - { - _console.print(args[i] + " "); - } - _console.println(""); - } - else if (args.length < 2) - { - _console.println("Enter Configuration file."); - String input = _console.readln(); - if (input != null) - { - doLoad(input); - } - else - { - _console.println("Did not recognise config file."); - } - } - else - { - doLoad(args[1]); - } - } - - private void doLoad(String configfile) - { - _console.println("Loading Configuration:" + configfile); - - try - { - _tool.setConfigurationFile(configfile); - } - catch (Configuration.InitException e) - { - _console.println("Unable to open config file due to: '" + e.getMessage() + "'"); - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java deleted file mode 100644 index 615f6ec1c2..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java +++ /dev/null @@ -1,202 +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.tools.messagestore.commands; - -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.txn.ServerTransaction; -import org.apache.qpid.server.txn.LocalTransaction; -import org.apache.qpid.tools.messagestore.MessageStoreTool; - -import java.util.LinkedList; -import java.util.List; - -public class Move extends AbstractCommand -{ - - public Move(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Move messages between queues.";/*\n" + - "The currently selected message set will be moved to the specifed queue.\n" + - "Alternatively the values can be provided on the command line.";*/ - } - - public String usage() - { - return "move to=<queue> [from=<queue>] [msgids=<msgids eg, 1,2,4-10>]"; - } - - public String getCommand() - { - return "move"; - } - - public void execute(String... args) - { - AMQQueue toQueue = null; - AMQQueue fromQueue = _tool.getState().getQueue(); - java.util.List<Long> msgids = _tool.getState().getMessages(); - - if (args.length >= 2) - { - for (String arg : args) - { - if (arg.startsWith("to=")) - { - String queueName = arg.substring(arg.indexOf("=") + 1); - toQueue = _tool.getState().getVhost().getQueueRegistry().getQueue(new AMQShortString(queueName)); - } - - if (arg.startsWith("from=")) - { - String queueName = arg.substring(arg.indexOf("=") + 1); - fromQueue = _tool.getState().getVhost().getQueueRegistry().getQueue(new AMQShortString(queueName)); - } - - if (arg.startsWith("msgids=")) - { - String msgidStr = arg.substring(arg.indexOf("=") + 1); - - // Record the current message selection - java.util.List<Long> currentIDs = _tool.getState().getMessages(); - - // Use the ToolState class to perform the messasge parsing - _tool.getState().setMessages(msgidStr); - msgids = _tool.getState().getMessages(); - - // Reset the original selection of messages - _tool.getState().setMessages(currentIDs); - } - } - } - - if (!checkRequirements(fromQueue, toQueue, msgids)) - { - return; - } - - processIDs(fromQueue, toQueue, msgids); - } - - private void processIDs(AMQQueue fromQueue, AMQQueue toQueue, java.util.List<Long> msgids) - { - Long previous = null; - Long start = null; - - if (msgids == null) - { - msgids = allMessageIDs(fromQueue); - } - - if (msgids == null || msgids.size() == 0) - { - _console.println("No Messages to move."); - return; - } - - for (long id : msgids) - { - if (previous != null) - { - if (id == previous + 1) - { - if (start == null) - { - start = previous; - } - } - else - { - if (start != null) - { - //move a range of ids - doCommand(fromQueue, start, id, toQueue); - start = null; - } - else - { - //move a single id - doCommand(fromQueue, id, id, toQueue); - } - } - } - - previous = id; - } - - if (start != null) - { - //move a range of ids - doCommand(fromQueue, start, previous, toQueue); - } - } - - private List<Long> allMessageIDs(AMQQueue fromQueue) - { - List<Long> ids = new LinkedList<Long>(); - - if (fromQueue != null) - { - List<QueueEntry> messages = fromQueue.getMessagesOnTheQueue(); - if (messages != null) - { - for (QueueEntry msg : messages) - { - ids.add(msg.getMessage().getMessageNumber()); - } - } - } - - return ids; - } - - protected boolean checkRequirements(AMQQueue fromQueue, AMQQueue toQueue, List<Long> msgids) - { - if (toQueue == null) - { - _console.println("Destination queue not specifed."); - _console.println(usage()); - return false; - } - - if (fromQueue == null) - { - _console.println("Source queue not specifed."); - _console.println(usage()); - return false; - } - - return true; - } - - protected void doCommand(AMQQueue fromQueue, long start, long id, AMQQueue toQueue) - { - ServerTransaction txn = new LocalTransaction(fromQueue.getVirtualHost().getTransactionLog()); - fromQueue.moveMessagesToAnotherQueue(start, id, toQueue.getNameShortString().toString(), txn); - txn.commit(); - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java deleted file mode 100644 index 8df4afa2db..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java +++ /dev/null @@ -1,67 +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.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.server.queue.AMQQueue; - -public class Purge extends Move -{ - public Purge(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Purge messages from a queue.\n" + - "The currently selected message set will be purged from the specifed queue.\n" + - "Alternatively the values can be provided on the command line."; - } - - public String usage() - { - return "purge from=<queue> [msgids=<msgids eg, 1,2,4-10>]"; - } - - public String getCommand() - { - return "purge"; - } - - - protected boolean checkRequirements(AMQQueue fromQueue, AMQQueue toQueue, java.util.List<Long> msgids) - { - if (fromQueue == null) - { - _console.println("Source queue not specifed."); - _console.println(usage()); - return false; - } - - return true; - } - - protected void doCommand(AMQQueue fromQueue, long start, long end, AMQQueue toQueue) - { - fromQueue.removeMessagesFromQueue(start, end); - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java deleted file mode 100644 index a81bc07c38..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java +++ /dev/null @@ -1,54 +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.tools.messagestore.commands; - -import org.apache.qpid.tools.messagestore.MessageStoreTool; - -public class Quit extends AbstractCommand -{ - public Quit(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Quit the tool."; - } - - public String usage() - { - return "quit"; - } - - public String getCommand() - { - return "quit"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals("quit"); - - _tool.quit(); - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java deleted file mode 100644 index ff59568374..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java +++ /dev/null @@ -1,233 +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.tools.messagestore.commands; - -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.server.exchange.Exchange; -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; -import java.util.StringTokenizer; - -public class Select extends AbstractCommand -{ - - public Select(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Perform a selection."; - } - - public String usage() - { - return "select virtualhost <name> |exchange <name> |queue <name> | msg id=<msgids eg. 1,2,4-10>"; - } - - public String getCommand() - { - return "select"; - } - - public void execute(String... args) - { - assert args.length > 2; - assert args[0].equals("select"); - - if (args.length < 3) - { - if (args[1].equals("show")) - { - doSelect(args[1], null); - } - else - { - _console.print("select : unknown command:"); - _console.println(help()); - } - } - else - { - if (args[1].equals("virtualhost") - || args[1].equals("vhost") - || args[1].equals("exchange") - || args[1].equals("queue") - || args[1].equals("msg") - ) - { - doSelect(args[1], args[2]); - } - else - { - _console.println(help()); - } - } - } - - private void doSelect(String type, String item) - { - if (type.equals("virtualhost")) - { - - VirtualHost vhost = ApplicationRegistry.getInstance() - .getVirtualHostRegistry().getVirtualHost(item); - - if (vhost == null) - { - _console.println("Virtualhost '" + item + "' not found."); - } - else - { - _tool.getState().setVhost(vhost); - } - } - - if (type.equals("exchange")) - { - - VirtualHost vhost = _tool.getState().getVhost(); - - if (vhost == null) - { - _console.println("No Virtualhost open. Open a Virtualhost first."); - return; - } - - - Exchange exchange = vhost.getExchangeRegistry().getExchange(new AMQShortString(item)); - - if (exchange == null) - { - _console.println("Exchange '" + item + "' not found."); - } - else - { - _tool.getState().setExchange(exchange); - } - - if (_tool.getState().getQueue() != null) - { - if (!exchange.isBound(_tool.getState().getQueue())) - { - _tool.getState().setQueue(null); - } - } - } - - if (type.equals("queue")) - { - VirtualHost vhost = _tool.getState().getVhost(); - - if (vhost == null) - { - _console.println("No Virtualhost open. Open a Virtualhost first."); - return; - } - - AMQQueue queue = vhost.getQueueRegistry().getQueue(new AMQShortString(item)); - - if (queue == null) - { - _console.println("Queue '" + item + "' not found."); - } - else - { - _tool.getState().setQueue(queue); - - if (_tool.getState().getExchange() == null) - { - for (AMQShortString exchangeName : vhost.getExchangeRegistry().getExchangeNames()) - { - Exchange exchange = vhost.getExchangeRegistry().getExchange(exchangeName); - if (exchange.isBound(queue)) - { - _tool.getState().setExchange(exchange); - break; - } - } - } - - //remove the message selection - _tool.getState().setMessages((java.util.List<Long>) null); - } - } - - if (type.equals("msg")) - { - if (item.startsWith("id=")) - { - StringTokenizer tok = new StringTokenizer(item.substring(item.indexOf("=") + 1), ","); - - java.util.List<Long> msgids = null; - - if (tok.hasMoreTokens()) - { - msgids = new LinkedList<Long>(); - } - - while (tok.hasMoreTokens()) - { - String next = tok.nextToken(); - if (next.contains("-")) - { - Long start = Long.parseLong(next.substring(0, next.indexOf("-"))); - Long end = Long.parseLong(next.substring(next.indexOf("-") + 1)); - - if (end >= start) - { - for (long l = start; l <= end; l++) - { - msgids.add(l); - } - } - } - else - { - msgids.add(Long.parseLong(next)); - } - } - - _tool.getState().setMessages(msgids); - } - - } - - if (type.equals("show")) - { - _console.println(_tool.getState().toString()); - if (_tool.getState().getMessages() != null) - { - _console.print("Msgs:"); - for (Long l : _tool.getState().getMessages()) - { - _console.print(" " + l); - } - _console.println(""); - } - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java deleted file mode 100644 index 806e161bbc..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java +++ /dev/null @@ -1,516 +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.tools.messagestore.commands; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.server.message.AMQMessage; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.tools.messagestore.MessageStoreTool; -import org.apache.qpid.tools.utils.Console; - -import java.util.LinkedList; -import java.util.List; - -public class Show extends AbstractCommand -{ - protected boolean _amqHeaders = false; - protected boolean _routing = false; - protected boolean _msgHeaders = false; - - public Show(MessageStoreTool tool) - { - super(tool); - } - - public String help() - { - return "Shows the messages headers."; - } - - public String usage() - { - return getCommand() + " [show=[all],[msgheaders],[amqheaders],[routing]] [id=<msgid e.g. 1,2,4-10>]"; - } - - public String getCommand() - { - return "show"; - } - - public void execute(String... args) - { - assert args.length > 0; - assert args[0].equals(getCommand()); - - if (args.length < 2) - { - parseArgs("all"); - } - else - { - parseArgs(args); - } - - performShow(); - } - - protected void parseArgs(String... args) - { - List<Long> msgids = null; - - if (args.length >= 2) - { - for (String arg : args) - { - if (arg.startsWith("show=")) - { - _msgHeaders = arg.contains("msgheaders") || arg.contains("all"); - _amqHeaders = arg.contains("amqheaders") || arg.contains("all"); - _routing = arg.contains("routing") || arg.contains("all"); - } - - if (arg.startsWith("id=")) - { - _tool.getState().setMessages(msgids); - } - }//for args - }// if args > 2 - } - - protected void performShow() - { - if (_tool.getState().getVhost() == null) - { - _console.println("No Virtualhost selected. 'DuSelect' a Virtualhost first."); - return; - } - - AMQQueue _queue = _tool.getState().getQueue(); - - List<Long> msgids = _tool.getState().getMessages(); - - if (_queue != null) - { - List<QueueEntry> messages = _queue.getMessagesOnTheQueue(); - if (messages == null || messages.size() == 0) - { - _console.println("No messages on queue"); - return; - } - - List<List> data = createMessageData(msgids, messages, _amqHeaders, _routing, _msgHeaders); - if (data != null) - { - _console.printMap(null, data); - } - else - { - String message = "No data to display."; - if (msgids != null) - { - message += " Is message selection correct? " + _tool.getState().printMessages(); - } - _console.println(message); - } - - } - else - { - _console.println("No Queue specified to show."); - } - } - - /** - * Create the list data for display from the messages. - * - * @param msgids The list of message ids to display - * @param messages A list of messages to format and display. - * @param showHeaders should the header info be shown - * @param showRouting show the routing info be shown - * @param showMessageHeaders show the msg headers be shown - * @return the formated data lists for printing - */ - protected List<List> createMessageData(List<Long> msgids, List<QueueEntry> messages, boolean showHeaders, boolean showRouting, - boolean showMessageHeaders) - { - - // Currenly exposed message properties -// //Printing the content Body -// msg.getContentBodyIterator(); -// //Print the Headers -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getAppId(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getAppIdAsString(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getClusterId(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getContentType(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getCorrelationId(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getDeliveryMode(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getEncoding(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getExpiration(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getHeaders(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getMessageNumber(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getPriority(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getPropertyFlags(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getReplyTo(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getTimestamp(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getType(); -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getUserId(); -// -// //Print out all the property names -// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getHeaders().getPropertyNames(); -// -// msg.getMessageNumber(); -// msg.getSize(); -// msg.getArrivalTime(); - -// msg.getDeliveredSubscription(); -// msg.getDeliveredToConsumer(); -// msg.getMessageHandle(); -// msg.getMessageNumber(); -// msg.getMessagePublishInfo(); -// msg.getPublisher(); - -// msg.getStoreContext(); -// msg.isAllContentReceived(); -// msg.isPersistent(); -// msg.isRedelivered(); -// msg.isRejectedBy(); -// msg.isTaken(); - - //Header setup - - List<List> data = new LinkedList<List>(); - - List<String> id = new LinkedList<String>(); - data.add(id); - id.add(Columns.ID.name()); - id.add(Console.ROW_DIVIDER); - - List<String> exchange = new LinkedList<String>(); - List<String> routingkey = new LinkedList<String>(); - List<String> immediate = new LinkedList<String>(); - List<String> mandatory = new LinkedList<String>(); - if (showRouting) - { - data.add(exchange); - exchange.add(Columns.Exchange.name()); - exchange.add(Console.ROW_DIVIDER); - - data.add(routingkey); - routingkey.add(Columns.RoutingKey.name()); - routingkey.add(Console.ROW_DIVIDER); - - data.add(immediate); - immediate.add(Columns.isImmediate.name()); - immediate.add(Console.ROW_DIVIDER); - - data.add(mandatory); - mandatory.add(Columns.isMandatory.name()); - mandatory.add(Console.ROW_DIVIDER); - } - - List<String> size = new LinkedList<String>(); - List<String> appid = new LinkedList<String>(); - List<String> clusterid = new LinkedList<String>(); - List<String> contenttype = new LinkedList<String>(); - List<String> correlationid = new LinkedList<String>(); - List<String> deliverymode = new LinkedList<String>(); - List<String> encoding = new LinkedList<String>(); - List<String> arrival = new LinkedList<String>(); - List<String> expiration = new LinkedList<String>(); - List<String> priority = new LinkedList<String>(); - List<String> propertyflag = new LinkedList<String>(); - List<String> replyto = new LinkedList<String>(); - List<String> timestamp = new LinkedList<String>(); - List<String> type = new LinkedList<String>(); - List<String> userid = new LinkedList<String>(); - List<String> ispersitent = new LinkedList<String>(); - List<String> isredelivered = new LinkedList<String>(); - List<String> isdelivered = new LinkedList<String>(); - - data.add(size); - size.add(Columns.Size.name()); - size.add(Console.ROW_DIVIDER); - - if (showHeaders) - { - data.add(ispersitent); - ispersitent.add(Columns.isPersistent.name()); - ispersitent.add(Console.ROW_DIVIDER); - - data.add(isredelivered); - isredelivered.add(Columns.isRedelivered.name()); - isredelivered.add(Console.ROW_DIVIDER); - - data.add(isdelivered); - isdelivered.add(Columns.isDelivered.name()); - isdelivered.add(Console.ROW_DIVIDER); - - data.add(appid); - appid.add(Columns.App_ID.name()); - appid.add(Console.ROW_DIVIDER); - - data.add(clusterid); - clusterid.add(Columns.Cluster_ID.name()); - clusterid.add(Console.ROW_DIVIDER); - - data.add(contenttype); - contenttype.add(Columns.Content_Type.name()); - contenttype.add(Console.ROW_DIVIDER); - - data.add(correlationid); - correlationid.add(Columns.Correlation_ID.name()); - correlationid.add(Console.ROW_DIVIDER); - - data.add(deliverymode); - deliverymode.add(Columns.Delivery_Mode.name()); - deliverymode.add(Console.ROW_DIVIDER); - - data.add(encoding); - encoding.add(Columns.Encoding.name()); - encoding.add(Console.ROW_DIVIDER); - - data.add(arrival); - expiration.add(Columns.Arrival.name()); - expiration.add(Console.ROW_DIVIDER); - - data.add(expiration); - expiration.add(Columns.Expiration.name()); - expiration.add(Console.ROW_DIVIDER); - - data.add(priority); - priority.add(Columns.Priority.name()); - priority.add(Console.ROW_DIVIDER); - - data.add(propertyflag); - propertyflag.add(Columns.Property_Flag.name()); - propertyflag.add(Console.ROW_DIVIDER); - - data.add(replyto); - replyto.add(Columns.ReplyTo.name()); - replyto.add(Console.ROW_DIVIDER); - - data.add(timestamp); - timestamp.add(Columns.Timestamp.name()); - timestamp.add(Console.ROW_DIVIDER); - - data.add(type); - type.add(Columns.Type.name()); - type.add(Console.ROW_DIVIDER); - - data.add(userid); - userid.add(Columns.UserID.name()); - userid.add(Console.ROW_DIVIDER); - } - - List<String> msgHeaders = new LinkedList<String>(); - if (showMessageHeaders) - { - data.add(msgHeaders); - msgHeaders.add(Columns.MsgHeaders.name()); - msgHeaders.add(Console.ROW_DIVIDER); - } - - //Add create the table of data - for (QueueEntry entry : messages) - { - ServerMessage msg = entry.getMessage(); - if (!includeMsg(msg, msgids)) - { - continue; - } - - id.add(msg.getMessageNumber().toString()); - - size.add("" + msg.getSize()); - - arrival.add("" + msg.getArrivalTime()); - - ispersitent.add(msg.isPersistent() ? "true" : "false"); - - - isredelivered.add(entry.isRedelivered() ? "true" : "false"); - - isdelivered.add(entry.getDeliveredToConsumer() ? "true" : "false"); - -// msg.getMessageHandle(); - - BasicContentHeaderProperties headers = null; - - try - { - if(msg instanceof AMQMessage) - { - headers = ((BasicContentHeaderProperties) ((AMQMessage)msg).getContentHeaderBody().getProperties()); - } - } - catch (AMQException e) - { - //ignore -// commandError("Unable to read properties for message: " + e.getMessage(), null); - } - - if (headers != null) - { - String appidS = headers.getAppIdAsString(); - appid.add(appidS == null ? "null" : appidS); - - String clusterS = headers.getClusterIdAsString(); - clusterid.add(clusterS == null ? "null" : clusterS); - - String contentS = headers.getContentTypeAsString(); - contenttype.add(contentS == null ? "null" : contentS); - - String correlationS = headers.getCorrelationIdAsString(); - correlationid.add(correlationS == null ? "null" : correlationS); - - deliverymode.add("" + headers.getDeliveryMode()); - - AMQShortString encodeSS = headers.getEncoding(); - encoding.add(encodeSS == null ? "null" : encodeSS.toString()); - - expiration.add("" + headers.getExpiration()); - - FieldTable headerFT = headers.getHeaders(); - msgHeaders.add(headerFT == null ? "none" : "" + headerFT.toString()); - - priority.add("" + headers.getPriority()); - propertyflag.add("" + headers.getPropertyFlags()); - - AMQShortString replytoSS = headers.getReplyTo(); - replyto.add(replytoSS == null ? "null" : replytoSS.toString()); - - timestamp.add("" + headers.getTimestamp()); - - AMQShortString typeSS = headers.getType(); - type.add(typeSS == null ? "null" : typeSS.toString()); - - AMQShortString useridSS = headers.getUserId(); - userid.add(useridSS == null ? "null" : useridSS.toString()); - - MessagePublishInfo info = null; - try - { - if(msg instanceof AMQMessage) - { - info = ((AMQMessage)msg).getMessagePublishInfo(); - } - - } - catch (AMQException e) - { - //ignore - } - - if (info != null) - { - AMQShortString exchangeSS = info.getExchange(); - exchange.add(exchangeSS == null ? "null" : exchangeSS.toString()); - - AMQShortString routingkeySS = info.getRoutingKey(); - routingkey.add(routingkeySS == null ? "null" : routingkeySS.toString()); - - immediate.add(info.isImmediate() ? "true" : "false"); - mandatory.add(info.isMandatory() ? "true" : "false"); - } - -// msg.getPublisher(); -- only used in clustering -// msg.getStoreContext(); -// msg.isAllContentReceived(); - - }// if headers!=null - -// need to access internal map and do lookups. -// msg.isTaken(); -// msg.getDeliveredSubscription(); -// msg.isRejectedBy(); - - } - - // if id only had the header and the divider in it then we have no data to display - if (id.size() == 2) - { - return null; - } - return data; - } - - protected boolean includeMsg(ServerMessage msg, List<Long> msgids) - { - if (msgids == null) - { - return true; - } - - Long msgid = msg.getMessageNumber(); - - boolean found = false; - - if (msgids != null) - { - //check msgid is in msgids - for (Long l : msgids) - { - if (l.equals(msgid)) - { - found = true; - break; - } - } - } - return found; - } - - public enum Columns - { - ID, - Size, - Exchange, - RoutingKey, - isImmediate, - isMandatory, - isPersistent, - isRedelivered, - isDelivered, - App_ID, - Cluster_ID, - Content_Type, - Correlation_ID, - Delivery_Mode, - Encoding, - Arrival, - Expiration, - Priority, - Property_Flag, - ReplyTo, - Timestamp, - Type, - UserID, - MsgHeaders - } -} - - diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java index c27c52eb8e..bfcdbe7460 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java @@ -60,9 +60,6 @@ public class Passwd private static void output(String user, byte[] encoded) throws IOException { - -// File passwdFile = new File("qpid.passwd"); - PrintStream ps = new PrintStream(System.out); user += ":"; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java deleted file mode 100644 index 986fea32cc..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java +++ /dev/null @@ -1,51 +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.tools.utils; - -public interface CommandParser -{ - /** - * If there is more than one command received on the last parse request. - * - * Subsequent calls to parse will utilise this input rather than reading new data from the input source - * @return boolean - */ - boolean more(); - - /** - * True if the currently parsed command has been requested as a background operation - * - * @return boolean - */ - boolean isBackground(); - - /** - * Parses user commands, and groups tokens in the - * String[] format that all Java main's love. - * - * If more than one command is provided in one input line then the more() method will return true. - * A subsequent call to parse() will continue to parse that input line before reading new input. - * - * @return <code>input</code> split in args[] format; null if eof. - * @throws java.io.IOException if there is a problem reading from the input stream - */ - String[] parse() throws java.io.IOException; -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java deleted file mode 100644 index cf457d1ea5..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java +++ /dev/null @@ -1,90 +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.tools.utils; - -import java.util.List; - -public interface Console -{ - public enum CellFormat - { - CENTRED, LEFT, RIGHT - } - - public static String ROW_DIVIDER = "*divider"; - - public void print(String... message); - - public void println(String... message); - - public String readln(); - - /** - * Reads and parses the command line. - * - * - * @return The next command or null - */ - public String[] readCommand(); - - public CommandParser getCommandParser(); - - public void setCommandParser(CommandParser parser); - - /** - * - * Prints the list of String nicely. - * - * +-------------+ - * | Heading | - * +-------------+ - * | Item 1 | - * | Item 2 | - * | Item 3 | - * +-------------+ - * - * @param hasTitle should list[0] be used as a heading - * @param list The list of Strings to display - */ - public void displayList(boolean hasTitle, String... list); - - /** - * - * Prints the list of String nicely. - * - * +----------------------------+ - * | Heading | - * +----------------------------+ - * | title | title | .. - * +----------------------------+ - * | Item 2 | value 2 | .. - * +----------------------------+ (*divider) - * | Item 3 | value 2 | .. - * +----------------------------+ - * - * @param title The title to display if any - * @param entries the entries to display in a map. - */ - void printMap(String title, List<List> entries); - - - public void close(); -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java deleted file mode 100644 index 09444ccdd7..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java +++ /dev/null @@ -1,121 +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.tools.utils; - -import java.io.BufferedReader; -import java.io.IOException; -import java.util.StringTokenizer; - -public class SimpleCommandParser implements CommandParser -{ - private static final String COMMAND_SEPERATOR = ";"; - - /** Input source of commands */ - protected BufferedReader _reader; - - /** The next list of commands from the command line */ - private StringBuilder _nextCommand = null; - - public SimpleCommandParser(BufferedReader reader) - { - _reader = reader; - } - - public boolean more() - { - return _nextCommand != null; - } - - public boolean isBackground() - { - return false; - } - - public String[] parse() throws IOException - { - String[] commands = null; - - String input = null; - - if (_nextCommand == null) - { - input = _reader.readLine(); - } - else - { - input = _nextCommand.toString(); - _nextCommand = null; - } - - if (input == null) - { - return null; - } - - StringTokenizer tok = new StringTokenizer(input, " "); - - int tokenCount = tok.countTokens(); - int index = 0; - - if (tokenCount > 0) - { - commands = new String[tokenCount]; - boolean commandComplete = false; - - while (tok.hasMoreTokens()) - { - String next = tok.nextToken(); - - if (next.equals(COMMAND_SEPERATOR)) - { - commandComplete = true; - _nextCommand = new StringBuilder(); - continue; - } - - if (commandComplete) - { - _nextCommand.append(next); - _nextCommand.append(" "); - } - else - { - commands[index] = next; - index++; - } - } - - } - - //Reduce the String[] if not all the tokens were used in this command. - // i.e. there is more than one command on the line. - if (index != tokenCount) - { - String[] shortCommands = new String[index]; - System.arraycopy(commands, 0, shortCommands, 0, index); - return shortCommands; - } - else - { - return commands; - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java b/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java deleted file mode 100644 index 2791a39f92..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java +++ /dev/null @@ -1,364 +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.tools.utils; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - -public class SimpleConsole implements Console -{ - /** SLF4J Logger. */ - private static Logger _devlog = LoggerFactory.getLogger(SimpleConsole.class); - - /** Console Writer. */ - protected BufferedWriter _consoleWriter; - - /** Console Reader. */ - protected BufferedReader _consoleReader; - - /** Parser for command-line input. */ - protected CommandParser _parser; - - public SimpleConsole(BufferedWriter writer, BufferedReader reader) - { - _consoleWriter = writer; - _consoleReader = reader; - _parser = new SimpleCommandParser(_consoleReader); - } - - public void print(String... message) - { - try - { - for (String s : message) - { - _consoleWriter.write(s); - } - _consoleWriter.flush(); - } - catch (IOException e) - { - _devlog.error(e.getMessage() + ": Occurred whilst trying to write:" + Arrays.asList(message)); - } - - } - - public void println(String... message) - { - print(message); - print(System.getProperty("line.separator")); - } - - - public String readln() - { - try - { - return _consoleReader.readLine(); - } - catch (IOException e) - { - _devlog.debug("Unable to read input due to:" + e.getMessage()); - return null; - } - } - - public String[] readCommand() - { - try - { - return _parser.parse(); - } - catch (IOException e) - { - _devlog.error("Error reading command:" + e.getMessage()); - return new String[0]; - } - } - - public CommandParser getCommandParser() - { - return _parser; - } - - public void setCommandParser(CommandParser parser) - { - _parser = parser; - } - - public void displayList(boolean hasTitle, String... list) - { - java.util.List<java.util.List> data = new LinkedList<List>(); - - java.util.List<String> values = new LinkedList<String>(); - - data.add(values); - - for (String value : list) - { - values.add(value); - } - - if (hasTitle) - { - values.add(1, "*divider"); - } - - printMap(null, data); - } - - /** - * - * Prints the list of String nicely. - * - * +----------------------------+ - * | Heading | - * +----------------------------+ - * | title | title | .. - * +----------------------------+ - * | Item 2 | value 2 | .. - * | Item 3 | value 2 | .. - * +----------------------------+ - * - * @param title The title to display if any - * @param entries the entries to display in a map. - */ - public void printMap(String title, java.util.List<java.util.List> entries) - { - try - { - int columns = entries.size(); - - int[] columnWidth = new int[columns]; - - // calculate row count - int rowMax = 0; - - //the longest item - int itemMax = 0; - - for (int i = 0; i < columns; i++) - { - int columnIRowMax = entries.get(i).size(); - - if (columnIRowMax > rowMax) - { - rowMax = columnIRowMax; - } - for (Object values : entries.get(i)) - { - if (values.toString().equals(Console.ROW_DIVIDER)) - { - continue; - } - - int itemLength = values.toString().length(); - - //note for single width - if (itemLength > itemMax) - { - itemMax = itemLength; - } - - //note for mulit width - if (itemLength > columnWidth[i]) - { - columnWidth[i] = itemLength; - } - - } - } - - int tableWidth = 0; - - - for (int i = 0; i < columns; i++) - { - // plus 2 for the space padding - columnWidth[i] += 2; - } - for (int size : columnWidth) - { - tableWidth += size; - } - tableWidth += (columns - 1); - - if (title != null) - { - if (title.length() > tableWidth) - { - tableWidth = title.length(); - } - - printCellRow("+", "-", tableWidth); - - printCell(CellFormat.CENTRED, "|", tableWidth, " " + title + " ", 0); - _consoleWriter.newLine(); - - } - - //put top line | or bottom of title - printCellRow("+", "-", tableWidth); - - //print the table data - int row = 0; - - for (; row < rowMax; row++) - { - for (int i = 0; i < columns; i++) - { - java.util.List columnData = entries.get(i); - - String value; - // does this column have a value for this row - if (columnData.size() > row) - { - value = " " + columnData.get(row).toString() + " "; - } - else - { - value = " "; - } - - if (i == 0 && value.equals(" " + Console.ROW_DIVIDER + " ")) - { - printCellRow("+", "-", tableWidth); - //move on to the next row - break; - } - else - { - printCell(CellFormat.LEFT, "|", columnWidth[i], value, i); - } - - // if it is the last row then do a new line. - if (i == columns - 1) - { - _consoleWriter.newLine(); - } - } - } - - printCellRow("+", "-", tableWidth); - - } - catch (IOException e) - { - _devlog.error(e.getMessage() + ": Occured whilst trying to write."); - } - } - - public void close() - { - - try - { - _consoleReader.close(); - } - catch (IOException e) - { - _devlog.error(e.getMessage() + ": Occured whilst trying to close reader."); - } - - try - { - - _consoleWriter.close(); - } - catch (IOException e) - { - _devlog.error(e.getMessage() + ": Occured whilst trying to close writer."); - } - - } - - private void printCell(CellFormat format, String edge, int cellWidth, String cell, int column) throws IOException - { - int pad = cellWidth - cell.length(); - - if (column == 0) - { - _consoleWriter.write(edge); - } - - switch (format) - { - case CENTRED: - printPad(" ", pad / 2); - break; - case RIGHT: - printPad(" ", pad); - break; - } - - _consoleWriter.write(cell); - - - switch (format) - { - case CENTRED: - // if pad isn't even put the extra one on the right - if (pad % 2 == 0) - { - printPad(" ", pad / 2); - } - else - { - printPad(" ", (pad / 2) + 1); - } - break; - case LEFT: - printPad(" ", pad); - break; - } - - - _consoleWriter.write(edge); - - } - - private void printCellRow(String edge, String mid, int cellWidth) throws IOException - { - _consoleWriter.write(edge); - - printPad(mid, cellWidth); - - _consoleWriter.write(edge); - _consoleWriter.newLine(); - } - - private void printPad(String padChar, int count) throws IOException - { - for (int i = 0; i < count; i++) - { - _consoleWriter.write(padChar); - } - } - - -} |